@indigoai-us/hq-cli 5.136.0 → 5.138.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 +12 -0
- package/dist/command-catalog.generated.d.ts +19 -3
- package/dist/command-catalog.generated.js +24 -3
- package/dist/commands/channels.d.ts +9 -1
- package/dist/commands/channels.js +56 -1
- package/dist/commands/dm.d.ts +11 -10
- package/dist/commands/dm.js +11 -14
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.138.0] — 2026-09-23
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `hq dm accept`, `decline`, and `block` now resolve pending requests to the
|
|
10
|
+
server's `personUid` field instead of sending unsupported action body fields.
|
|
11
|
+
|
|
12
|
+
## [5.137.0] — 2026-09-23
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Added `hq channels members <channel>` to print channel members for human and machine-principal sessions, with a `--json` format for scripts.
|
|
16
|
+
|
|
5
17
|
## [5.136.0] — 2026-09-23
|
|
6
18
|
|
|
7
19
|
### Added
|
|
@@ -2333,7 +2333,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
2333
2333
|
readonly subcommands: readonly [];
|
|
2334
2334
|
}, {
|
|
2335
2335
|
readonly name: "accept";
|
|
2336
|
-
readonly description: "Accept a pending connection request (by email, personUid, or pairKey). Their held message is then delivered.";
|
|
2336
|
+
readonly description: "Accept a pending connection request (by requester email, personUid, or pairKey from `hq dm requests`). Their held message is then delivered.";
|
|
2337
2337
|
readonly aliases: readonly [];
|
|
2338
2338
|
readonly hidden: false;
|
|
2339
2339
|
readonly usage: "[options] <person>";
|
|
@@ -2346,7 +2346,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
2346
2346
|
readonly subcommands: readonly [];
|
|
2347
2347
|
}, {
|
|
2348
2348
|
readonly name: "decline";
|
|
2349
|
-
readonly description: "Decline a pending connection request (by email, personUid, or pairKey).";
|
|
2349
|
+
readonly description: "Decline a pending connection request (by requester email, personUid, or pairKey from `hq dm requests`).";
|
|
2350
2350
|
readonly aliases: readonly [];
|
|
2351
2351
|
readonly hidden: false;
|
|
2352
2352
|
readonly usage: "[options] <person>";
|
|
@@ -2359,7 +2359,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
2359
2359
|
readonly subcommands: readonly [];
|
|
2360
2360
|
}, {
|
|
2361
2361
|
readonly name: "block";
|
|
2362
|
-
readonly description: "Block a person so they can't send you further requests (by email, personUid, or pairKey).";
|
|
2362
|
+
readonly description: "Block a person so they can't send you further requests (by requester email, personUid, or pairKey from `hq dm requests`).";
|
|
2363
2363
|
readonly aliases: readonly [];
|
|
2364
2364
|
readonly hidden: false;
|
|
2365
2365
|
readonly usage: "[options] <person>";
|
|
@@ -2388,6 +2388,22 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
2388
2388
|
readonly arguments: readonly [];
|
|
2389
2389
|
readonly options: readonly [];
|
|
2390
2390
|
readonly subcommands: readonly [];
|
|
2391
|
+
}, {
|
|
2392
|
+
readonly name: "members";
|
|
2393
|
+
readonly description: "Print the members of one of your DM channels.";
|
|
2394
|
+
readonly aliases: readonly [];
|
|
2395
|
+
readonly hidden: false;
|
|
2396
|
+
readonly usage: "[options] <channel>";
|
|
2397
|
+
readonly arguments: readonly [{
|
|
2398
|
+
readonly name: "channel";
|
|
2399
|
+
readonly required: true;
|
|
2400
|
+
readonly variadic: false;
|
|
2401
|
+
}];
|
|
2402
|
+
readonly options: readonly [{
|
|
2403
|
+
readonly flags: "--json";
|
|
2404
|
+
readonly description: "Output the member array as JSON.";
|
|
2405
|
+
}];
|
|
2406
|
+
readonly subcommands: readonly [];
|
|
2391
2407
|
}];
|
|
2392
2408
|
}, {
|
|
2393
2409
|
readonly name: "onboard";
|
|
@@ -3001,7 +3001,7 @@ export const COMMAND_CATALOG = [
|
|
|
3001
3001
|
},
|
|
3002
3002
|
{
|
|
3003
3003
|
"name": "accept",
|
|
3004
|
-
"description": "Accept a pending connection request (by email, personUid, or pairKey). Their held message is then delivered.",
|
|
3004
|
+
"description": "Accept a pending connection request (by requester email, personUid, or pairKey from `hq dm requests`). Their held message is then delivered.",
|
|
3005
3005
|
"aliases": [],
|
|
3006
3006
|
"hidden": false,
|
|
3007
3007
|
"usage": "[options] <person>",
|
|
@@ -3017,7 +3017,7 @@ export const COMMAND_CATALOG = [
|
|
|
3017
3017
|
},
|
|
3018
3018
|
{
|
|
3019
3019
|
"name": "decline",
|
|
3020
|
-
"description": "Decline a pending connection request (by email, personUid, or pairKey).",
|
|
3020
|
+
"description": "Decline a pending connection request (by requester email, personUid, or pairKey from `hq dm requests`).",
|
|
3021
3021
|
"aliases": [],
|
|
3022
3022
|
"hidden": false,
|
|
3023
3023
|
"usage": "[options] <person>",
|
|
@@ -3033,7 +3033,7 @@ export const COMMAND_CATALOG = [
|
|
|
3033
3033
|
},
|
|
3034
3034
|
{
|
|
3035
3035
|
"name": "block",
|
|
3036
|
-
"description": "Block a person so they can't send you further requests (by email, personUid, or pairKey).",
|
|
3036
|
+
"description": "Block a person so they can't send you further requests (by requester email, personUid, or pairKey from `hq dm requests`).",
|
|
3037
3037
|
"aliases": [],
|
|
3038
3038
|
"hidden": false,
|
|
3039
3039
|
"usage": "[options] <person>",
|
|
@@ -3067,6 +3067,27 @@ export const COMMAND_CATALOG = [
|
|
|
3067
3067
|
"arguments": [],
|
|
3068
3068
|
"options": [],
|
|
3069
3069
|
"subcommands": []
|
|
3070
|
+
},
|
|
3071
|
+
{
|
|
3072
|
+
"name": "members",
|
|
3073
|
+
"description": "Print the members of one of your DM channels.",
|
|
3074
|
+
"aliases": [],
|
|
3075
|
+
"hidden": false,
|
|
3076
|
+
"usage": "[options] <channel>",
|
|
3077
|
+
"arguments": [
|
|
3078
|
+
{
|
|
3079
|
+
"name": "channel",
|
|
3080
|
+
"required": true,
|
|
3081
|
+
"variadic": false
|
|
3082
|
+
}
|
|
3083
|
+
],
|
|
3084
|
+
"options": [
|
|
3085
|
+
{
|
|
3086
|
+
"flags": "--json",
|
|
3087
|
+
"description": "Output the member array as JSON."
|
|
3088
|
+
}
|
|
3089
|
+
],
|
|
3090
|
+
"subcommands": []
|
|
3070
3091
|
}
|
|
3071
3092
|
]
|
|
3072
3093
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { type ChannelSummary } from "./dm.js";
|
|
2
|
+
import { type CallerIdentity, type ChannelMember, type ChannelSummary } from "./dm.js";
|
|
3
3
|
/**
|
|
4
4
|
* Human label for a channel row. Named channels (personal/company) show their
|
|
5
5
|
* name + a `hq dm <slug>` hint; unnamed group DMs are identified by their
|
|
@@ -12,5 +12,13 @@ export declare function describeChannel(c: ChannelSummary): string;
|
|
|
12
12
|
* fetched list) so the formatting is unit-testable without network/auth.
|
|
13
13
|
*/
|
|
14
14
|
export declare function formatChannelsList(channels: ChannelSummary[]): string;
|
|
15
|
+
/**
|
|
16
|
+
* Remove the caller from a channel roster before it is used for mentions.
|
|
17
|
+
* Machine principals identify themselves with the JWT uid; human sessions can
|
|
18
|
+
* also match the roster's email when the uid claim is absent.
|
|
19
|
+
*/
|
|
20
|
+
export declare function excludeCallerFromChannelMembers(members: ChannelMember[], caller: CallerIdentity): ChannelMember[];
|
|
21
|
+
/** Render the roster as either dm-bind's tab-separated format or JSON. */
|
|
22
|
+
export declare function formatChannelMembers(members: ChannelMember[], json: boolean): string;
|
|
15
23
|
export declare function registerChannelsCommand(program: Command): void;
|
|
16
24
|
//# sourceMappingURL=channels.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
3
3
|
import { vaultApiFetch } from "../utils/vault-api.js";
|
|
4
|
-
import { channelSlug } from "./dm.js";
|
|
4
|
+
import { callerFromToken, channelSlug, fetchChannelMembers, fetchChannels, matchChannelsByName, } from "./dm.js";
|
|
5
5
|
/**
|
|
6
6
|
* Human label for a channel row. Named channels (personal/company) show their
|
|
7
7
|
* name + a `hq dm <slug>` hint; unnamed group DMs are identified by their
|
|
@@ -42,6 +42,31 @@ export function formatChannelsList(channels) {
|
|
|
42
42
|
}
|
|
43
43
|
return lines.join("\n");
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Remove the caller from a channel roster before it is used for mentions.
|
|
47
|
+
* Machine principals identify themselves with the JWT uid; human sessions can
|
|
48
|
+
* also match the roster's email when the uid claim is absent.
|
|
49
|
+
*/
|
|
50
|
+
export function excludeCallerFromChannelMembers(members, caller) {
|
|
51
|
+
return members.filter((member) => {
|
|
52
|
+
if (caller.personUid && member.personUid === caller.personUid)
|
|
53
|
+
return false;
|
|
54
|
+
if (caller.email &&
|
|
55
|
+
member.email &&
|
|
56
|
+
member.email.trim().toLowerCase() === caller.email) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Render the roster as either dm-bind's tab-separated format or JSON. */
|
|
63
|
+
export function formatChannelMembers(members, json) {
|
|
64
|
+
if (json)
|
|
65
|
+
return JSON.stringify(members);
|
|
66
|
+
return members
|
|
67
|
+
.map((member) => `${member.displayName ?? ""}\t${member.personUid}`)
|
|
68
|
+
.join("\n");
|
|
69
|
+
}
|
|
45
70
|
async function runChannelsList() {
|
|
46
71
|
try {
|
|
47
72
|
const token = await ensureCognitoToken();
|
|
@@ -64,6 +89,29 @@ async function runChannelsList() {
|
|
|
64
89
|
process.exit(1);
|
|
65
90
|
}
|
|
66
91
|
}
|
|
92
|
+
async function runChannelsMembers(channelName, opts) {
|
|
93
|
+
try {
|
|
94
|
+
const token = await ensureCognitoToken();
|
|
95
|
+
const channels = await fetchChannels(token);
|
|
96
|
+
const matches = matchChannelsByName(channels, channelName);
|
|
97
|
+
if (matches.length === 0) {
|
|
98
|
+
console.error(chalk.red(`No channel named '${channelName}' — run \`hq channels\` to see your channels.`));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
if (matches.length > 1) {
|
|
102
|
+
const scopes = matches.map((match) => match.scope ?? "?").join(", ");
|
|
103
|
+
console.error(chalk.red(`'${channelName}' matches ${matches.length} channels (${scopes}) — this is ambiguous.`));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
const members = await fetchChannelMembers(token, matches[0].channelId);
|
|
107
|
+
const visibleMembers = excludeCallerFromChannelMembers(members, callerFromToken(token));
|
|
108
|
+
console.log(formatChannelMembers(visibleMembers, opts.json === true));
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
67
115
|
export function registerChannelsCommand(program) {
|
|
68
116
|
const channels = program
|
|
69
117
|
.command("channels")
|
|
@@ -74,5 +122,12 @@ export function registerChannelsCommand(program) {
|
|
|
74
122
|
.action(async () => {
|
|
75
123
|
await runChannelsList();
|
|
76
124
|
});
|
|
125
|
+
channels
|
|
126
|
+
.command("members <channel>")
|
|
127
|
+
.description("Print the members of one of your DM channels.")
|
|
128
|
+
.option("--json", "Output the member array as JSON.")
|
|
129
|
+
.action(async (channelName, opts) => {
|
|
130
|
+
await runChannelsMembers(channelName, opts);
|
|
131
|
+
});
|
|
77
132
|
}
|
|
78
133
|
//# sourceMappingURL=channels.js.map
|
package/dist/commands/dm.d.ts
CHANGED
|
@@ -175,23 +175,24 @@ export interface ConnectionRequest {
|
|
|
175
175
|
*/
|
|
176
176
|
export declare function matchRequest(requests: ConnectionRequest[], identifier: string): ConnectionRequest | null;
|
|
177
177
|
/**
|
|
178
|
-
* Build the POST body for an accept/decline/block action.
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
* `withEmail`). Pure → unit-testable.
|
|
178
|
+
* Build the POST body for an accept/decline/block action. The connections
|
|
179
|
+
* endpoints accept the requester's `personUid`, so resolve friendly email or
|
|
180
|
+
* pairKey input through the pending request before building the body. Pure →
|
|
181
|
+
* unit-testable.
|
|
183
182
|
*
|
|
184
183
|
* Throws Error with a user-facing message when the identifier can't be resolved.
|
|
185
184
|
*/
|
|
186
185
|
export declare function buildConnectionActionBody(identifier: string, matched: ConnectionRequest | null): {
|
|
187
|
-
|
|
188
|
-
} | {
|
|
189
|
-
withPersonUid: string;
|
|
190
|
-
} | {
|
|
191
|
-
withEmail: string;
|
|
186
|
+
personUid: string;
|
|
192
187
|
};
|
|
193
188
|
/** Fetch the caller's channels (GET /v1/notify/channels). */
|
|
194
189
|
export declare function fetchChannels(token: string): Promise<ChannelSummary[]>;
|
|
190
|
+
/**
|
|
191
|
+
* Fetch a channel's roster (GET /v1/notify/channels/{id}/members). Any member
|
|
192
|
+
* who can read the channel may read its members, so this is the same gate the
|
|
193
|
+
* post itself passes. Used to resolve `@name` mentions into participant uids.
|
|
194
|
+
*/
|
|
195
|
+
export declare function fetchChannelMembers(token: string, channelId: string): Promise<ChannelMember[]>;
|
|
195
196
|
/** One incoming DM as returned by GET /v1/notify/inbox. */
|
|
196
197
|
export interface DmInboxEvent {
|
|
197
198
|
eventId: string;
|
package/dist/commands/dm.js
CHANGED
|
@@ -355,25 +355,22 @@ export function matchRequest(requests, identifier) {
|
|
|
355
355
|
return null;
|
|
356
356
|
}
|
|
357
357
|
/**
|
|
358
|
-
* Build the POST body for an accept/decline/block action.
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
* `withEmail`). Pure → unit-testable.
|
|
358
|
+
* Build the POST body for an accept/decline/block action. The connections
|
|
359
|
+
* endpoints accept the requester's `personUid`, so resolve friendly email or
|
|
360
|
+
* pairKey input through the pending request before building the body. Pure →
|
|
361
|
+
* unit-testable.
|
|
363
362
|
*
|
|
364
363
|
* Throws Error with a user-facing message when the identifier can't be resolved.
|
|
365
364
|
*/
|
|
366
365
|
export function buildConnectionActionBody(identifier, matched) {
|
|
367
366
|
if (matched) {
|
|
368
|
-
return {
|
|
367
|
+
return { personUid: matched.fromPersonUid };
|
|
369
368
|
}
|
|
370
369
|
const r = identifier.trim();
|
|
371
370
|
const rcpt = detectRecipient(r);
|
|
372
371
|
if (rcpt?.toPersonUid)
|
|
373
|
-
return {
|
|
374
|
-
|
|
375
|
-
return { withEmail: rcpt.toEmail };
|
|
376
|
-
throw new Error(`Could not resolve '${identifier}' to a pending request — pass the requester's email, personUid, or the pairKey from \`hq dm requests\`.`);
|
|
372
|
+
return { personUid: rcpt.toPersonUid };
|
|
373
|
+
throw new Error(`Could not resolve '${identifier}' to a pending request — pass the requester's email or personUid, or a pairKey from \`hq dm requests\`.`);
|
|
377
374
|
}
|
|
378
375
|
const CONNECTION_ACTIONS = {
|
|
379
376
|
accept: { path: "accept", done: "Connection accepted" },
|
|
@@ -503,7 +500,7 @@ export async function fetchChannels(token) {
|
|
|
503
500
|
* who can read the channel may read its members, so this is the same gate the
|
|
504
501
|
* post itself passes. Used to resolve `@name` mentions into participant uids.
|
|
505
502
|
*/
|
|
506
|
-
async function fetchChannelMembers(token, channelId) {
|
|
503
|
+
export async function fetchChannelMembers(token, channelId) {
|
|
507
504
|
const res = await vaultApiFetch({
|
|
508
505
|
token,
|
|
509
506
|
path: `/v1/notify/channels/${encodeURIComponent(channelId)}/members`,
|
|
@@ -1328,19 +1325,19 @@ export function registerDmCommand(program) {
|
|
|
1328
1325
|
});
|
|
1329
1326
|
dm
|
|
1330
1327
|
.command("accept <person>")
|
|
1331
|
-
.description("Accept a pending connection request (by email, personUid, or pairKey). Their held message is then delivered.")
|
|
1328
|
+
.description("Accept a pending connection request (by requester email, personUid, or pairKey from `hq dm requests`). Their held message is then delivered.")
|
|
1332
1329
|
.action(async (person) => {
|
|
1333
1330
|
await runConnectionAction("accept", person);
|
|
1334
1331
|
});
|
|
1335
1332
|
dm
|
|
1336
1333
|
.command("decline <person>")
|
|
1337
|
-
.description("Decline a pending connection request (by email, personUid, or pairKey).")
|
|
1334
|
+
.description("Decline a pending connection request (by requester email, personUid, or pairKey from `hq dm requests`).")
|
|
1338
1335
|
.action(async (person) => {
|
|
1339
1336
|
await runConnectionAction("decline", person);
|
|
1340
1337
|
});
|
|
1341
1338
|
dm
|
|
1342
1339
|
.command("block <person>")
|
|
1343
|
-
.description("Block a person so they can't send you further requests (by email, personUid, or pairKey).")
|
|
1340
|
+
.description("Block a person so they can't send you further requests (by requester email, personUid, or pairKey from `hq dm requests`).")
|
|
1344
1341
|
.action(async (person) => {
|
|
1345
1342
|
await runConnectionAction("block", person);
|
|
1346
1343
|
});
|