@indigoai-us/hq-cli 5.94.1 → 5.94.2
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/CHANGELOG.md +23 -0
- package/dist/commands/company-transfer.d.ts +185 -0
- package/dist/commands/company-transfer.js +664 -0
- package/dist/commands/company.js +3 -0
- package/dist/commands/core-checkpoint.js +157 -36
- package/dist/commands/search.js +23 -11
- package/dist/lib/search-index/index.d.ts +1 -0
- package/dist/lib/search-index/index.js +41 -5
- package/dist/main.js +8 -4
- package/dist/utils/unexpected-cli-error.d.ts +16 -0
- package/dist/utils/unexpected-cli-error.js +26 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.94.2]
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `hq search <query>` works. It has been broken since 5.86.0: the query was
|
|
10
|
+
registered as a subcommand literally named `<query>`, so every search
|
|
11
|
+
returned `error: unknown command '<the query>'` and only `hq search get`
|
|
12
|
+
functioned. Queries are now an argument on `search` itself, so
|
|
13
|
+
`hq search release gate` needs no quoting. (#337)
|
|
14
|
+
- Commands no longer fail silently. The top-level handler printed a message
|
|
15
|
+
only for one error class, so a crashing qmd made `hq index status` exit 1
|
|
16
|
+
with zero bytes on stdout and stderr — indistinguishable from success. Every
|
|
17
|
+
failure now emits a diagnostic, routed through the existing redaction chain
|
|
18
|
+
so visibility widens without disclosure widening. (#337)
|
|
19
|
+
- The CLI no longer prefers a bundled qmd it cannot run. 5.94.1 made
|
|
20
|
+
package-local resolution work and thereby started preferring a global
|
|
21
|
+
install's qmd whose native better-sqlite3 bindings were never compiled,
|
|
22
|
+
breaking search on hosts that had previously fallen through to a working qmd
|
|
23
|
+
on `PATH`. Resolution now verifies the candidate actually runs. The probe
|
|
24
|
+
costs a ~0.3s spawn, so it runs only when a fallback exists — with nothing
|
|
25
|
+
better available the answer is the bundled binary either way, and a failure
|
|
26
|
+
there is now loud rather than silent. (#337)
|
|
27
|
+
|
|
5
28
|
## [5.94.1]
|
|
6
29
|
|
|
7
30
|
### Fixed
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type ActiveMember } from "./members.js";
|
|
3
|
+
/**
|
|
4
|
+
* `hq company transfer` — company ownership handover (client-service-pack
|
|
5
|
+
* US-010). The server adds ZERO new API routes (vault-api-hq-prod sits at the
|
|
6
|
+
* 600-route quota), so the CLI half multiplexes onto two EXISTING routes:
|
|
7
|
+
*
|
|
8
|
+
* - WRITES ride `POST /membership/role` with a body discriminator
|
|
9
|
+
* `action: "transfer-initiate" | "transfer-accept" | "transfer-decline" |
|
|
10
|
+
* "transfer-cancel"`. The ordinary role payload has never carried an
|
|
11
|
+
* `action` field, so the shapes cannot collide.
|
|
12
|
+
* - The READ rides `GET /membership/company/{companyUid}?view=transfers`;
|
|
13
|
+
* without that exact `view` value the route returns the member roster as
|
|
14
|
+
* before.
|
|
15
|
+
*
|
|
16
|
+
* TWO-PARTY BY CONSTRUCTION. `initiate` NOMINATES; it does not transfer
|
|
17
|
+
* anything. Ownership moves only when the nominee runs `accept`. The four write
|
|
18
|
+
* transitions (initiate / accept / decline / cancel) all multiplex through one
|
|
19
|
+
* POST with an explicit `action`, matching the server's route shape.
|
|
20
|
+
*
|
|
21
|
+
* ABSENT-FIELD DISCIPLINE (policy `hq-absent-field-never-means-constraining-value`).
|
|
22
|
+
* This flow spans two repos that deploy in EITHER order, so absence is load
|
|
23
|
+
* bearing in both directions and is handled in both directions here:
|
|
24
|
+
*
|
|
25
|
+
* - REQUESTS: a flag the operator did not pass is OMITTED from the body — it
|
|
26
|
+
* is never defaulted to a value on the wire. `--initiator-role` unset sends
|
|
27
|
+
* no `initiatorRole` at all, so the server applies its own reversible
|
|
28
|
+
* "keep them as admin" default; the CLI can therefore never turn silence
|
|
29
|
+
* into `remove`. Same for `--transfer-id` (absent = "the pending one").
|
|
30
|
+
* - RESPONSES: fields are read with presence and value SEPARATELY
|
|
31
|
+
* ({@link readWireField}), because an OLDER server omits fields a newer CLI
|
|
32
|
+
* knows about. An absent `effects.initiator` renders as "not reported",
|
|
33
|
+
* never as "removed" — the CLI must not narrate a destructive outcome it
|
|
34
|
+
* was never actually told about.
|
|
35
|
+
*
|
|
36
|
+
* Conventions mirror `company.ts` / `files.ts`: ensureCognitoToken() →
|
|
37
|
+
* getEntityUid() → vaultApiFetch() → error-check → chalk output, with the
|
|
38
|
+
* confirmation prompt behind an injectable seam so tests can drive it.
|
|
39
|
+
*/
|
|
40
|
+
/** The four write transitions the server accepts. */
|
|
41
|
+
export declare const TRANSFER_ACTIONS: readonly ["initiate", "accept", "decline", "cancel"];
|
|
42
|
+
export type TransferAction = (typeof TRANSFER_ACTIONS)[number];
|
|
43
|
+
/**
|
|
44
|
+
* Legal `--initiator-role` values. `owner` is deliberately absent: a handover
|
|
45
|
+
* where the outgoing owner keeps `owner` is not a handover. `remove` is the
|
|
46
|
+
* ONLY value that takes them off the company, and it can only ever arrive from
|
|
47
|
+
* an explicit flag.
|
|
48
|
+
*/
|
|
49
|
+
export declare const INITIATOR_ROLE_VALUES: readonly ["admin", "member", "guest", "remove"];
|
|
50
|
+
export type InitiatorRoleFlag = (typeof INITIATOR_ROLE_VALUES)[number];
|
|
51
|
+
/**
|
|
52
|
+
* What the SERVER falls back to when `initiatorRole` is absent. Mirrored here
|
|
53
|
+
* only so the confirmation preview can tell the operator what will happen; the
|
|
54
|
+
* CLI never puts this value on the wire, because sending it would defeat the
|
|
55
|
+
* point of leaving the field absent.
|
|
56
|
+
*/
|
|
57
|
+
export declare const DEFAULT_INITIATOR_ROLE = "admin";
|
|
58
|
+
/** A response field's presence, kept separate from its value. */
|
|
59
|
+
export interface WireRead<T = unknown> {
|
|
60
|
+
present: boolean;
|
|
61
|
+
value: T | undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Read a field off a server response reporting presence and value SEPARATELY.
|
|
65
|
+
*
|
|
66
|
+
* The CLI-side twin of hq-pro's `readField`. `body.x ?? fallback` is wrong here
|
|
67
|
+
* because it makes an OLDER server's omitted field indistinguishable from a
|
|
68
|
+
* newer server explicitly sending `null`/`false`/`""` — and every caller below
|
|
69
|
+
* needs to say "this server didn't tell me" rather than invent an answer.
|
|
70
|
+
*/
|
|
71
|
+
export declare function readWireField<T = unknown>(body: Record<string, unknown> | null | undefined, key: string): WireRead<T>;
|
|
72
|
+
/**
|
|
73
|
+
* Parse `--initiator-role`.
|
|
74
|
+
*
|
|
75
|
+
* Returns `undefined` when the flag was NOT passed, and that `undefined` means
|
|
76
|
+
* "omit the field entirely" — never "remove", and never a substituted default.
|
|
77
|
+
* A present-but-invalid value throws instead of falling back, so a typo can
|
|
78
|
+
* never be silently reinterpreted as a different disposition.
|
|
79
|
+
*/
|
|
80
|
+
export declare function parseInitiatorRole(raw: string | undefined): InitiatorRoleFlag | undefined;
|
|
81
|
+
export interface TransferBodyParams {
|
|
82
|
+
companyUid: string;
|
|
83
|
+
action: TransferAction;
|
|
84
|
+
targetPersonUid?: string;
|
|
85
|
+
initiatorRole?: InitiatorRoleFlag;
|
|
86
|
+
transferId?: string;
|
|
87
|
+
reason?: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The wire prefix that turns `POST /membership/role` into a transfer request.
|
|
91
|
+
* Mirrors the server's `TRANSFER_ACTION_PREFIX` in `_ownership-transfer.ts`:
|
|
92
|
+
* a body whose `action` starts with `transfer-` is a transfer; anything else
|
|
93
|
+
* (including no `action` at all) is an ordinary role change.
|
|
94
|
+
*/
|
|
95
|
+
export declare const TRANSFER_ACTION_PREFIX = "transfer-";
|
|
96
|
+
/**
|
|
97
|
+
* The query discriminator on `GET /membership/company/{companyUid}` that
|
|
98
|
+
* selects the transfer surface instead of the member roster. Mirrors the
|
|
99
|
+
* server's `TRANSFER_HISTORY_VIEW` (exact match on the server side).
|
|
100
|
+
*/
|
|
101
|
+
export declare const TRANSFER_HISTORY_VIEW = "transfers";
|
|
102
|
+
/**
|
|
103
|
+
* Build the `POST /membership/role` transfer body. The CLI-side `action`
|
|
104
|
+
* ("initiate" | …) is prefixed to the wire discriminator ("transfer-initiate"
|
|
105
|
+
* | …) HERE, so no call site can accidentally post an unprefixed action that
|
|
106
|
+
* the server would route to the ordinary role-change handler.
|
|
107
|
+
*
|
|
108
|
+
* Every optional field is included ONLY when the caller actually supplied it.
|
|
109
|
+
* This is the single choke point for the request half of the absent-field rule:
|
|
110
|
+
* if a value is not here, the server sees no key at all and applies its own
|
|
111
|
+
* safe default, which keeps the CLI correct against a server that predates any
|
|
112
|
+
* of these fields.
|
|
113
|
+
*/
|
|
114
|
+
export declare function buildTransferBody(params: TransferBodyParams): Record<string, unknown>;
|
|
115
|
+
/**
|
|
116
|
+
* Describe what the confirmation prompt should say happens to the OUTGOING
|
|
117
|
+
* owner. Absence resolves to the reversible, non-destructive sentence.
|
|
118
|
+
*/
|
|
119
|
+
export declare function describeInitiatorOutcome(role: InitiatorRoleFlag | undefined): string;
|
|
120
|
+
/**
|
|
121
|
+
* Render the `effects` block of an accepted transfer.
|
|
122
|
+
*
|
|
123
|
+
* Reads every field for PRESENCE first. An older server that does not send
|
|
124
|
+
* `effects`, or omits a field inside it, gets an honest "not reported" line —
|
|
125
|
+
* inventing "removed" or "unchanged" here would be the CLI asserting a
|
|
126
|
+
* destructive outcome it has no evidence for.
|
|
127
|
+
*/
|
|
128
|
+
export declare function describeEffects(rawEffects: unknown): string[];
|
|
129
|
+
/** Injectable yes/no confirmation seam (stubbed in tests). */
|
|
130
|
+
export type ConfirmFn = (message: string) => Promise<boolean>;
|
|
131
|
+
/**
|
|
132
|
+
* The real prompt. Refuses (rather than assuming yes) when stdin is not a TTY,
|
|
133
|
+
* so a transfer can never be initiated or accepted by a pipeline that simply
|
|
134
|
+
* had nobody to ask. `--yes` is the only non-interactive path, and it is an
|
|
135
|
+
* explicit operator act.
|
|
136
|
+
*
|
|
137
|
+
* Exported ONLY so the non-TTY refusal — the one confirmation path the
|
|
138
|
+
* injectable seam hides from the command tests — can be covered directly.
|
|
139
|
+
*/
|
|
140
|
+
export declare function realConfirm(message: string): Promise<boolean>;
|
|
141
|
+
/**
|
|
142
|
+
* Resolve `--to` to a `personUid`. A uid is used as-is; anything else is
|
|
143
|
+
* matched (case-insensitively) against the company's ACTIVE roster by email,
|
|
144
|
+
* slug, or name, because the server requires a uid and only accepts a target
|
|
145
|
+
* who is already an active member.
|
|
146
|
+
*/
|
|
147
|
+
export declare function matchMemberTarget(members: ActiveMember[], target: string): ActiveMember | undefined;
|
|
148
|
+
export declare function resolveTargetPersonUid(token: string, companyUid: string, target: string): Promise<{
|
|
149
|
+
personUid: string;
|
|
150
|
+
label: string;
|
|
151
|
+
}>;
|
|
152
|
+
export interface TransferDeps {
|
|
153
|
+
confirm?: ConfirmFn;
|
|
154
|
+
}
|
|
155
|
+
export interface RunTransferParams {
|
|
156
|
+
companySlug: string;
|
|
157
|
+
action: TransferAction;
|
|
158
|
+
/** `--to` (initiate only): email, slug, name, or prs_… uid. */
|
|
159
|
+
to?: string;
|
|
160
|
+
/** Raw `--initiator-role`; `undefined` means the flag was NOT passed. */
|
|
161
|
+
initiatorRoleRaw?: string;
|
|
162
|
+
/** Raw `--transfer-id`; `undefined` means "the pending one". */
|
|
163
|
+
transferId?: string;
|
|
164
|
+
reason?: string;
|
|
165
|
+
yes?: boolean;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* `hq company transfer initiate|accept|decline|cancel`.
|
|
169
|
+
*
|
|
170
|
+
* `initiate` and `accept` are the two transitions that move (or commit to
|
|
171
|
+
* moving) ownership, so both stop for an explicit confirmation that spells out
|
|
172
|
+
* who becomes owner and what happens to the outgoing owner. `decline` and
|
|
173
|
+
* `cancel` only ever ABORT a nomination, so they do not prompt.
|
|
174
|
+
*/
|
|
175
|
+
export declare function runCompanyTransfer(params: RunTransferParams, deps?: TransferDeps): Promise<void>;
|
|
176
|
+
export interface RunTransferStatusParams {
|
|
177
|
+
companySlug: string;
|
|
178
|
+
limit?: string;
|
|
179
|
+
cursor?: string;
|
|
180
|
+
json?: boolean;
|
|
181
|
+
}
|
|
182
|
+
/** `hq company transfer status` — the pending nomination plus the audit trail. */
|
|
183
|
+
export declare function runCompanyTransferStatus(params: RunTransferStatusParams): Promise<void>;
|
|
184
|
+
export declare function registerCompanyTransferCommand(company: Command, deps?: TransferDeps): void;
|
|
185
|
+
//# sourceMappingURL=company-transfer.d.ts.map
|