@indigoai-us/hq-cli 5.101.7 → 5.102.0

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 CHANGED
@@ -2,6 +2,50 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.102.0] — 2026-08-17
6
+
7
+ ### Added
8
+
9
+ - **`hq integrations` now covers the whole lifecycle**, not just using an
10
+ already-connected app. Previously the CLI could `list` / `tools` / `call` /
11
+ `approve` / `reject`, and everything else had to happen on the console.
12
+
13
+ Find and add:
14
+ - `hq integrations catalog [query]` (alias `search`) — browse or search
15
+ connectable apps, showing each one's sign-in style and provenance.
16
+ - `hq integrations inspect <app>` — surfaces, credentials, and warnings for
17
+ an app before you commit to it.
18
+ - `hq integrations discover <docsUrl>` — find a connectable server from a
19
+ documentation page.
20
+ - `hq integrations connect <app>` (alias `add`) — connect by domain, catalog
21
+ entry (`--entry-id`), docs page (`--docs-url`), or a raw endpoint
22
+ (`--mcp-url`). Auth mode is detected rather than declared: no-auth installs
23
+ directly, API-key apps read the key from `--token-stdin` / a hidden prompt
24
+ / `--token`, and OAuth apps run a browser sign-in. If hq-pro answers
25
+ `INTEGRATION_FACTORY_OAUTH_REQUIRED` to a direct install, the browser flow
26
+ starts automatically.
27
+ - `hq integrations reconnect [app]` — re-authenticate an app whose stored
28
+ credentials stopped working.
29
+
30
+ Govern and remove:
31
+ - `hq integrations show [app]` — one connection in full.
32
+ - `hq integrations policy [app] [--set auto-allow|confirm|deny]`.
33
+ - `hq integrations grants|grant|ungrant` — per-tool approval exceptions.
34
+ - `hq integrations access|share|unshare` — who in the company may use an app.
35
+ - `hq integrations audit` / `pending` — recent activity and queued approvals.
36
+ - `hq integrations disconnect [app]` (alias `remove`) — deletes stored
37
+ credentials; confirms first, and refuses non-interactively without `--yes`.
38
+
39
+ OAuth sign-in uses an RFC 8252 §7.3 loopback listener (`127.0.0.1`, ephemeral
40
+ port, pinned callback path), so the code never leaves the machine and the
41
+ PKCE verifier stays server-side in hq-pro. Against a backend that does not
42
+ admit a loopback callback, connect degrades to printing the sign-in URL for
43
+ the console to finish, and says so rather than reporting a false success.
44
+
45
+ Requires hq-pro with `INTEGRATION_FACTORY_OAUTH_LOOPBACK_ENABLED` for the
46
+ fully CLI-native OAuth path; every other verb works against any current
47
+ deployment.
48
+
5
49
  ## [5.101.7] — 2026-08-17
6
50
 
7
51
  ### Changed
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Typed client for hq-pro's integration-factory routes.
3
+ *
4
+ * One function per endpoint, each a thin `vaultApiFetch` + `raiseForResponse`
5
+ * pair. The wire shapes below MIRROR hq-pro (`src/vault-service/handlers/
6
+ * integrations-admin.ts`) rather than importing from it — hq-cli takes no
7
+ * cross-repo source dependency — so every field a newer backend might not emit
8
+ * is optional and read defensively.
9
+ */
10
+ import { type GranteeType, type Permission, type WriteAllowlistGrant, type WritePolicy } from "./integrations-core.js";
11
+ export interface CatalogEntry {
12
+ name: string;
13
+ domain: string;
14
+ description?: string;
15
+ /** True when the app is one-click connectable (has a remote MCP surface). */
16
+ mcpReady: boolean;
17
+ scope?: "company" | "global";
18
+ /**
19
+ * Provenance: `integrations.sh` is the curated third-party feed,
20
+ * `hq-recommended` is first-party, `hq-discovered` is a Community definition
21
+ * learned from a successful connect elsewhere.
22
+ */
23
+ source?: "hq-discovered" | "integrations.sh" | "hq-recommended";
24
+ authClass?: "none" | "key" | "oauth";
25
+ /** Opaque server-owned id; prefer it over echoing connection details back. */
26
+ entryId?: string;
27
+ }
28
+ export declare function listCatalog(token: string, companyUid: string, opts?: {
29
+ limit?: number;
30
+ query?: string;
31
+ }): Promise<CatalogEntry[]>;
32
+ export interface BlueprintSurface {
33
+ kind: string;
34
+ slug: string;
35
+ name: string;
36
+ url?: string;
37
+ docs?: string;
38
+ authStatus: "required" | "optional" | "none" | "unknown";
39
+ credentialIds: string[];
40
+ readiness: {
41
+ strategy: string;
42
+ score: number;
43
+ reason: string;
44
+ };
45
+ }
46
+ export interface Blueprint {
47
+ provider: string;
48
+ displayName: string;
49
+ domain: string;
50
+ summary?: string;
51
+ description?: string;
52
+ credentials: Array<{
53
+ id: string;
54
+ type: string;
55
+ label: string;
56
+ generateUrl?: string;
57
+ }>;
58
+ surfaces: BlueprintSurface[];
59
+ recommendedSurface?: BlueprintSurface;
60
+ warnings: Array<{
61
+ code: string;
62
+ message: string;
63
+ source?: string;
64
+ }>;
65
+ }
66
+ export declare function pullBlueprint(token: string, companyUid: string, input: {
67
+ domain?: string;
68
+ query?: string;
69
+ catalogEntryId?: string;
70
+ }): Promise<Blueprint>;
71
+ export interface DocsDiscovery {
72
+ docsUrl: string;
73
+ displayName: string;
74
+ provider: string;
75
+ mcpUrl: string;
76
+ authMode: "none" | "bearer" | "oauth";
77
+ transport: string;
78
+ verification: "verified" | "pending-auth";
79
+ confidence: "high" | "medium";
80
+ evidenceUrls: string[];
81
+ }
82
+ export interface DiscoverDocsResult {
83
+ /** Absent when hq-pro verified no MCP surface on the page. */
84
+ discovery?: DocsDiscovery;
85
+ /**
86
+ * Caller- and company-bound continuation token. Passing this to connect is
87
+ * what lets hq-pro trust the endpoint WITHOUT re-reading a URL the terminal
88
+ * echoed back at it. Short-lived (~15 min).
89
+ */
90
+ discoveryReceiptId?: string;
91
+ sourceTruncated?: boolean;
92
+ }
93
+ export declare function discoverDocs(token: string, companyUid: string, docsUrl: string): Promise<DiscoverDocsResult>;
94
+ export interface InstallResult {
95
+ connection: {
96
+ id: string;
97
+ provider: string;
98
+ status: string;
99
+ scopes?: string[];
100
+ };
101
+ installation: {
102
+ id: string;
103
+ displayName: string;
104
+ domain: string;
105
+ status: "installed" | "needs_credentials";
106
+ surface?: {
107
+ kind?: string;
108
+ url?: string;
109
+ authStatus?: string;
110
+ };
111
+ };
112
+ mcp?: {
113
+ tools?: Array<{
114
+ name: string;
115
+ description?: string;
116
+ mode?: string;
117
+ }>;
118
+ };
119
+ credential?: {
120
+ required: boolean;
121
+ configured: boolean;
122
+ authStatus?: string;
123
+ };
124
+ writeAllowlist?: WriteAllowlistGrant[];
125
+ }
126
+ export interface InstallInput {
127
+ domain?: string;
128
+ query?: string;
129
+ catalogEntryId?: string;
130
+ discoveryReceiptId?: string;
131
+ mcpUrl?: string;
132
+ provider?: string;
133
+ displayName?: string;
134
+ docsUrl?: string;
135
+ authMode?: "none" | "bearer";
136
+ bearerToken?: string;
137
+ }
138
+ export declare function installIntegration(token: string, companyUid: string, input: InstallInput): Promise<InstallResult>;
139
+ export declare function uninstallIntegration(token: string, companyUid: string, installationId: string): Promise<{
140
+ installationId: string;
141
+ connectionId: string;
142
+ }>;
143
+ export interface OAuthStartResult {
144
+ provider: string;
145
+ displayName: string;
146
+ /** The remote auth server's authorize URL — open this in a browser. */
147
+ authorizationUrl: string;
148
+ state: string;
149
+ expiresAt: string;
150
+ }
151
+ export interface OAuthStartInput {
152
+ mcpUrl?: string;
153
+ catalogEntryId?: string;
154
+ discoveryReceiptId?: string;
155
+ provider?: string;
156
+ displayName?: string;
157
+ domain?: string;
158
+ docsUrl?: string;
159
+ /**
160
+ * The native client's loopback callback. hq-pro accepts it only when its
161
+ * loopback flag is on and the URI matches the pinned RFC 8252 §7.3 shape;
162
+ * omitting it pins the console callback instead.
163
+ */
164
+ redirectUri?: string;
165
+ }
166
+ export declare function startOAuth(token: string, companyUid: string, input: OAuthStartInput): Promise<OAuthStartResult>;
167
+ export declare function completeOAuth(token: string, companyUid: string, input: {
168
+ state: string;
169
+ code: string;
170
+ }): Promise<InstallResult>;
171
+ export declare function updateGovernance(token: string, companyUid: string, input: {
172
+ connectionId: string;
173
+ writePolicy?: WritePolicy;
174
+ /**
175
+ * REPLACES the connection's whole per-tool allowlist — hq-pro has no
176
+ * add/remove verb. Callers must read the current list, merge, and send the
177
+ * full result, never a delta.
178
+ */
179
+ writeAllowlist?: WriteAllowlistGrant[];
180
+ }): Promise<{
181
+ writePolicy?: WritePolicy;
182
+ writeAllowlist: WriteAllowlistGrant[];
183
+ }>;
184
+ export interface ConnectionAccess {
185
+ connectionId: string;
186
+ provider: string;
187
+ creator: {
188
+ uid: string;
189
+ name?: string | null;
190
+ };
191
+ /** True when the connection predates access control and has no ACL row. */
192
+ grandfathered: boolean;
193
+ /** Whether THIS caller may grant/revoke. */
194
+ canManage: boolean;
195
+ access: {
196
+ mode: string;
197
+ grantCount: number;
198
+ };
199
+ entries: Array<{
200
+ granteeType: GranteeType;
201
+ granteeId: string;
202
+ granteeName?: string | null;
203
+ permission: Permission;
204
+ grantedBy: string;
205
+ grantedByName?: string | null;
206
+ grantedAt: string;
207
+ }>;
208
+ }
209
+ export declare function getConnectionAccess(token: string, companyUid: string, connectionId: string): Promise<ConnectionAccess>;
210
+ export declare function mutateConnectionAccess(token: string, companyUid: string, action: "grant" | "revoke", input: {
211
+ connectionId: string;
212
+ granteeType: GranteeType;
213
+ granteeId?: string;
214
+ permission?: Permission;
215
+ }): Promise<ConnectionAccess>;
216
+ //# sourceMappingURL=integrations-api.d.ts.map
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Typed client for hq-pro's integration-factory routes.
3
+ *
4
+ * One function per endpoint, each a thin `vaultApiFetch` + `raiseForResponse`
5
+ * pair. The wire shapes below MIRROR hq-pro (`src/vault-service/handlers/
6
+ * integrations-admin.ts`) rather than importing from it — hq-cli takes no
7
+ * cross-repo source dependency — so every field a newer backend might not emit
8
+ * is optional and read defensively.
9
+ */
10
+ import { vaultApiFetch } from "../utils/vault-api.js";
11
+ import { raiseForResponse, } from "./integrations-core.js";
12
+ export async function listCatalog(token, companyUid, opts = {}) {
13
+ const query = {
14
+ companyUid,
15
+ limit: String(opts.limit ?? 60),
16
+ };
17
+ // hq-pro bounds the query itself; normalize whitespace here so an
18
+ // accidentally padded shell argument doesn't read as a different search.
19
+ const q = (opts.query ?? "").replace(/\s+/g, " ").trim();
20
+ if (q)
21
+ query.query = q;
22
+ const res = await vaultApiFetch({
23
+ token,
24
+ path: "/v1/integrations/factory/catalog",
25
+ query,
26
+ });
27
+ if (!res.ok)
28
+ await raiseForResponse(res, "Failed to browse the app catalog");
29
+ const body = (await res.json());
30
+ return body.entries ?? [];
31
+ }
32
+ export async function pullBlueprint(token, companyUid, input) {
33
+ const res = await vaultApiFetch({
34
+ token,
35
+ path: "/v1/integrations/factory/blueprint",
36
+ method: "POST",
37
+ body: { companyUid, ...input },
38
+ });
39
+ if (!res.ok)
40
+ await raiseForResponse(res, "Failed to inspect the app");
41
+ const body = (await res.json());
42
+ return body.blueprint;
43
+ }
44
+ export async function discoverDocs(token, companyUid, docsUrl) {
45
+ const res = await vaultApiFetch({
46
+ token,
47
+ path: "/v1/integrations/factory/discover-docs",
48
+ method: "POST",
49
+ body: { companyUid, docsUrl },
50
+ });
51
+ if (!res.ok)
52
+ await raiseForResponse(res, "Failed to read that documentation page");
53
+ return (await res.json());
54
+ }
55
+ export async function installIntegration(token, companyUid, input) {
56
+ const res = await vaultApiFetch({
57
+ token,
58
+ path: "/v1/integrations/factory/install",
59
+ method: "POST",
60
+ body: { companyUid, ...input },
61
+ });
62
+ if (!res.ok)
63
+ await raiseForResponse(res, "Failed to connect the app");
64
+ return (await res.json());
65
+ }
66
+ export async function uninstallIntegration(token, companyUid, installationId) {
67
+ const res = await vaultApiFetch({
68
+ token,
69
+ path: "/v1/integrations/factory/uninstall",
70
+ method: "POST",
71
+ body: { companyUid, installationId },
72
+ });
73
+ if (!res.ok)
74
+ await raiseForResponse(res, "Failed to disconnect the app");
75
+ return (await res.json());
76
+ }
77
+ export async function startOAuth(token, companyUid, input) {
78
+ const res = await vaultApiFetch({
79
+ token,
80
+ path: "/v1/integrations/factory/oauth/start",
81
+ method: "POST",
82
+ body: { companyUid, ...input },
83
+ });
84
+ if (!res.ok)
85
+ await raiseForResponse(res, "Failed to start sign-in");
86
+ return (await res.json());
87
+ }
88
+ export async function completeOAuth(token, companyUid, input) {
89
+ const res = await vaultApiFetch({
90
+ token,
91
+ path: "/v1/integrations/factory/oauth/complete",
92
+ method: "POST",
93
+ body: { companyUid, ...input },
94
+ });
95
+ if (!res.ok)
96
+ await raiseForResponse(res, "Failed to finish sign-in");
97
+ return (await res.json());
98
+ }
99
+ /* ------------------------------------------------------------------ */
100
+ /* Governance: write policy + per-tool grants */
101
+ /* ------------------------------------------------------------------ */
102
+ export async function updateGovernance(token, companyUid, input) {
103
+ const res = await vaultApiFetch({
104
+ token,
105
+ path: "/v1/integrations/admin",
106
+ method: "PATCH",
107
+ body: { companyUid, ...input },
108
+ });
109
+ if (!res.ok)
110
+ await raiseForResponse(res, "Failed to update the app's settings");
111
+ return (await res.json());
112
+ }
113
+ export async function getConnectionAccess(token, companyUid, connectionId) {
114
+ const res = await vaultApiFetch({
115
+ token,
116
+ path: "/v1/integrations/factory/access",
117
+ query: { companyUid, connectionId },
118
+ });
119
+ if (!res.ok)
120
+ await raiseForResponse(res, "Failed to read who can use this app");
121
+ return (await res.json());
122
+ }
123
+ export async function mutateConnectionAccess(token, companyUid, action, input) {
124
+ const res = await vaultApiFetch({
125
+ token,
126
+ path: `/v1/integrations/factory/access/${action}`,
127
+ method: "POST",
128
+ body: { companyUid, ...input },
129
+ });
130
+ if (!res.ok) {
131
+ await raiseForResponse(res, action === "grant" ? "Failed to share the app" : "Failed to remove access");
132
+ }
133
+ return (await res.json());
134
+ }
135
+ //# sourceMappingURL=integrations-api.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * `hq integrations catalog | inspect | discover | connect | reconnect`.
3
+ *
4
+ * The add half of the lifecycle: find an app, look at what it exposes, and
5
+ * connect it. Connecting is the interesting one — an app authenticates in one
6
+ * of three ways and hq-cli should not make the caller work out which:
7
+ *
8
+ * none the MCP endpoint is anonymously callable → install directly
9
+ * key the endpoint wants a bearer token → take it from --token /
10
+ * --token-stdin / a hidden prompt, never from argv history
11
+ * oauth the endpoint speaks OAuth 2.1 → run the RFC 8252 loopback flow
12
+ *
13
+ * Detection is server-authoritative and lazy. Rather than guessing from
14
+ * catalog metadata (which can be stale), the command attempts the direct
15
+ * install and treats hq-pro's `INTEGRATION_FACTORY_OAUTH_REQUIRED` as the
16
+ * signal to switch into the browser flow. That way a catalog row that says
17
+ * "key" but is really OAuth-protected still connects on the first try.
18
+ */
19
+ import { Command } from "commander";
20
+ /**
21
+ * Validate `--auth` against the supported enum, rejecting an unknown value
22
+ * before anything is installed. A finite provider/runtime option must never be
23
+ * narrowed by a truthy cast — an unrecognised mode has to fail loudly rather
24
+ * than fall through to a default.
25
+ */
26
+ export declare function assertAuthMode(value: string | undefined): void;
27
+ /** Single-quote a value for a copy-pasteable shell command. */
28
+ export declare function shellQuote(value: string): string;
29
+ export declare function registerConnectCommands(integrations: Command): void;
30
+ //# sourceMappingURL=integrations-connect.d.ts.map