@allocator-one/harness-cli 1.15.0 → 1.15.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 CHANGED
@@ -63,7 +63,10 @@ Only the presentation depends on the name you typed:
63
63
  `harness mcp` under `harness`. The tools are the same.
64
64
 
65
65
  Messages, exit codes and `--json` output are the same under both names, so
66
- a script written for `harness` works with `operator` unchanged. The CLI
66
+ a script written for `harness` works with `operator` unchanged. The
67
+ exceptions are the version label and the hints that tell you what to
68
+ type, such as `operator login`: they name the command you typed, because
69
+ someone who installed only `@allocator-one/operator` has no `harness`. The CLI
67
70
  tells the names apart by the command path it was started with. Launchers
68
71
  that start it by its file path instead, such as npm's Windows shims, pnpm,
69
72
  Volta and Yarn PnP, hide that path. In that case the CLI goes by the
@@ -84,8 +87,29 @@ worktree, and agent slot on the machine.
84
87
  - `harness logout`: revokes the stored login on the server and deletes it
85
88
  locally.
86
89
  - `harness whoami`: shows the resolved host and where the credential came
87
- from. With an outside expert's `pxc_…` credential it also names the
88
- expert's role and plan (see [Outside experts](#outside-experts)).
90
+ from. For a login it also shows the kind of credential, the user, the
91
+ organization and when it expires, as `GET /api/v1/session` describes it.
92
+ On a server without that route, or with an `aone_…` API token, those
93
+ fields are `null` under `--json`. With an outside expert's `pxc_…`
94
+ credential it names the expert's role and plan instead (see
95
+ [Outside experts](#outside-experts)).
96
+
97
+ A login gets one of two kinds of credential. The server picks the kind from
98
+ your standing in the organization you choose:
99
+
100
+ - `harness-cli` is the full credential that Harness staff get.
101
+ - `operator-cli` is the Operator-only credential that any other admin or
102
+ member Operator serves gets. It reaches the Operator commands, `whoami`
103
+ and `logout`, and the server refuses everything else (issues, plans,
104
+ gates) with 403.
105
+
106
+ `login` says after signing in which kind you got (`kind` under `--json`).
107
+ If the new credential fails verification, nothing is stored. A credential
108
+ the browser login just minted is then revoked, in one best-effort attempt,
109
+ so a 30-day credential isn't left behind. A token you pasted is never
110
+ revoked, whatever the failure, since you may be using it elsewhere.
111
+ Holders keep their kind even after gaining staff standing: they log in again
112
+ to get the full credential.
89
113
 
90
114
  `operator login`, `operator logout` and `operator whoami` are the same
91
115
  commands and use the same stored login.
@@ -160,7 +184,12 @@ design, acceptance criteria, notes, and comments (up to 20, cached locally)
160
184
  and prints their paths. Screenshots are often the reproduction, so read
161
185
  them. `--no-images` skips the downloads. If a download fails, a stderr
162
186
  notice says so and the command still succeeds. `harness comments <id>`
163
- prints the full, untruncated comment thread with comment ids. The
187
+ prints the full, untruncated comment thread with comment ids, oldest first.
188
+ It requests up to 200 comments per page and follows the oldest comment id
189
+ until the server reports `has_more=false`. `--json` and the MCP
190
+ `comment_list` tool return the same complete array of full comment objects.
191
+ Missing pagination metadata, repeated comment ids, or an older-page failure
192
+ produce an error without presenting a partial thread as complete. The
164
193
  `Blocked:` line names a cancelled or archived blocker as such
165
194
  (`yes, by CLI-3 (cancelled), CLI-4 (archived), CLI-5`). When the issue has
166
195
  wait conditions, `Waiting:` says how many still hold it, and a
@@ -457,8 +486,11 @@ gate.
457
486
  you: it freezes the next revision and lets ready steps run.
458
487
  `--collaborator <id-or-email>` (repeatable) adds people to the plan room
459
488
  where its Operator steps run, and `--retry-budget` and
460
- `--spend-budget-cents` set the budgets. `harness plan hold <plan-id>`
461
- pauses every step until the plan is released again.
489
+ `--spend-budget-cents` set the budgets. Each Operator step runs as a job
490
+ (`harness jobs` names its plan step), and the changes a job proposes need a
491
+ person's approval in the app, at the job's approval link. The CLI never
492
+ approves. `harness plan hold <plan-id>` pauses every step until the plan is
493
+ released again.
462
494
 
463
495
  **Gates.** `harness gate satisfy <issue> <gate>` ticks a gate and
464
496
  `harness gate unsatisfy <issue> <gate>` unticks it. `<gate>` is the gate's
@@ -469,10 +501,12 @@ refuses and lists each one's id, key, and label instead of choosing. Pass
469
501
  the condition id of the one you mean. Only when nothing matches exactly
470
502
  does a label or key match ignoring case, and that too is refused when more
471
503
  than one gate matches. Ticking a gate requires staff standing in the
472
- organization or a place in its plan's room. Only the owner can tick an
473
- owned gate, and only its owner or Harness staff can untick it. Anyone else
474
- gets an error that names the gate's owner as of the refusal, which the
475
- server sends with it, and exits `1`:
504
+ organization or a place in its plan's room. The server refuses an
505
+ Operator-only login (`operator-cli`) here, even for a person in the plan's
506
+ room: it reaches only the Operator commands, `whoami` and `logout`. Only
507
+ the owner can tick an owned gate, and only its owner or Harness staff can
508
+ untick it. Anyone else gets an error that names the gate's owner as of
509
+ the refusal, which the server sends with it, and exits `1`:
476
510
 
477
511
  ```text
478
512
  harness: Cannot satisfy gate "Compliance sign-off" on IO-121: only its owner, Compliance officer (role:compliance_officer), can tick it (HTTP 403)
@@ -979,7 +1013,7 @@ The server exposes 72 tools:
979
1013
  | People | `member_list`, `member_add`, `member_set_role`, `user_list` |
980
1014
  | Plans and gates | `plan_create`, `plan_show`, `plan_edit`, `plan_release`, `plan_hold`, `gate_set`, `condition_list` |
981
1015
  | Outside experts | `assignment_list` |
982
- | Operator jobs | `jobs_list`, `job_show`, `job_changes`, `job_steer`, `job_cancel`, `job_pause` |
1016
+ | Operator jobs | `jobs_list`, `job_show`, `job_changes`, `job_start`, `job_steer`, `job_cancel`, `job_pause` |
983
1017
  | Continue and procedures | `job_continue`, `procedure_save`, `procedure_show`, `procedure_run` |
984
1018
  | Operator's catalog | `tools_search` (without `query` it browses the index; `domain`, `cursor`, `all`), `skills_list` |
985
1019
  | Other | `stats`, `upload` |
@@ -1021,6 +1055,14 @@ Some tools behave differently from their commands:
1021
1055
  `cancelled`, or `waiting_on` is set. A person has to act exactly when
1022
1056
  `waiting_on` is set; a job paused without it resumes by itself. Neither
1023
1057
  tool approves anything; give a person the `approval_url`.
1058
+ - `job_start` is the CLI's `run`: `outcome`, and optionally `budget` (US
1059
+ dollars; only the organization's admins) and `skill`; there is no `team`.
1060
+ It returns `job_id`, `state`, `workbench_url` and, when a change set
1061
+ waits, `approval_url`, and never approves: give a person the link. Each
1062
+ call starts another job, so it is never retried; after an "Outcome
1063
+ unknown" error look with `jobs_list` (`mine`) before calling again. It
1064
+ carries `idempotentHint: false` and `destructiveHint: false`. The endpoint
1065
+ needs a server that has the Operator start-job API.
1024
1066
  - `job_steer`, `job_cancel` and `job_pause` return the job as `job_show`
1025
1067
  does. None is idempotent, so none is retried: after an error that leaves
1026
1068
  the outcome unknown, read the job with `job_show` before calling one
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/version.ts
4
+ import { readFileSync } from "fs";
5
+ var pkg = JSON.parse(
6
+ readFileSync(new URL("../package.json", import.meta.url), "utf8")
7
+ );
8
+ var CLI_VERSION = pkg.version;
9
+ var CLI_PACKAGE = pkg.name;
10
+
11
+ // src/invocation.ts
12
+ import { basename } from "path";
13
+ var OPERATOR_PACKAGE = "@allocator-one/operator";
14
+ function invokedName(argv1 = process.argv[1], packageName = CLI_PACKAGE) {
15
+ const typed = basename(argv1 ?? "").replace(/\.[cm]?js$/i, "").toLowerCase();
16
+ if (typed === "operator" || typed === "harness") return typed;
17
+ return packageName === OPERATOR_PACKAGE ? "operator" : "harness";
18
+ }
19
+ function named(text, name = invokedName()) {
20
+ if (name === "harness") return text;
21
+ return text.replace(/^harness: /gm, `${name}: `).replace(/(?<![\w@/.-])harness (?=[a-z])(?!disagree)/g, `${name} `).replaceAll("@allocator-one/harness-cli", OPERATOR_PACKAGE);
22
+ }
23
+ var OPERATOR_COMMANDS = [
24
+ "run",
25
+ "jobs",
26
+ "job",
27
+ "watch",
28
+ "steer",
29
+ "cancel",
30
+ "pause",
31
+ "continue",
32
+ "procedure",
33
+ "plan",
34
+ "gate",
35
+ "conditions",
36
+ "assignments",
37
+ "tools",
38
+ "tool",
39
+ "skills",
40
+ "mcp"
41
+ ];
42
+ var GENERAL_COMMANDS = [
43
+ "login",
44
+ "logout",
45
+ "whoami",
46
+ "version",
47
+ "keys"
48
+ ];
49
+ var OPERATOR_HEADING = "Operator:";
50
+ var ISSUES_HEADING = "Issues:";
51
+ var GENERAL_HEADING = "General:";
52
+ function layoutOperatorHelp(program) {
53
+ const rank = (name) => {
54
+ const operator = OPERATOR_COMMANDS.indexOf(name);
55
+ if (operator !== -1) return operator;
56
+ return GENERAL_COMMANDS.includes(name) ? 2 * OPERATOR_COMMANDS.length : OPERATOR_COMMANDS.length;
57
+ };
58
+ const heading = (name) => OPERATOR_COMMANDS.includes(name) ? OPERATOR_HEADING : GENERAL_COMMANDS.includes(name) ? GENERAL_HEADING : ISSUES_HEADING;
59
+ for (const command of program.commands) command.helpGroup(heading(command.name()));
60
+ program.commands.sort(
61
+ (left, right) => rank(left.name()) - rank(right.name())
62
+ );
63
+ program.commandsGroup(GENERAL_HEADING).helpCommand(true);
64
+ }
65
+
66
+ export {
67
+ CLI_VERSION,
68
+ CLI_PACKAGE,
69
+ OPERATOR_PACKAGE,
70
+ invokedName,
71
+ named,
72
+ layoutOperatorHelp
73
+ };