@indigoai-us/hq-cli 5.77.14 → 5.78.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 +23 -0
- package/assets/scaffold/core/scripts/archive-old-threads.sh +86 -0
- package/assets/scaffold/core/scripts/backfill-company-skill-mirrors.sh +73 -0
- package/assets/scaffold/core/scripts/backfill-workspace-mirror.sh +51 -0
- package/assets/scaffold/core/scripts/codex-skill-bridge.sh +550 -0
- package/assets/scaffold/core/scripts/detect-stale-core-policy-mirror.sh +185 -0
- package/assets/scaffold/core/scripts/hq-status-summary.sh +187 -0
- package/assets/scaffold/core/scripts/ontology-readme-drift.sh +264 -0
- package/assets/scaffold/core/scripts/qmd-reindex-after-sync.sh +93 -0
- package/assets/scaffold/core/scripts/rebuild-all-indexes.sh +69 -0
- package/assets/scaffold/core/scripts/rebuild-companies-index.sh +206 -0
- package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +127 -0
- package/assets/scaffold/core/scripts/rebuild-orchestrator-index.sh +86 -0
- package/assets/scaffold/core/scripts/rebuild-projects-index.sh +105 -0
- package/assets/scaffold/core/scripts/rebuild-public-knowledge-index.sh +83 -0
- package/assets/scaffold/core/scripts/rebuild-reports-index.sh +114 -0
- package/assets/scaffold/core/scripts/rebuild-social-drafts-index.sh +135 -0
- package/assets/scaffold/core/scripts/rebuild-threads-index.sh +82 -0
- package/assets/scaffold/core/scripts/rebuild-workers-index.sh +97 -0
- package/assets/scaffold/core/scripts/resize-screenshot.sh +47 -0
- package/assets/scaffold/core/scripts/token-usage-report.sh +254 -0
- package/assets/scaffold/core/scripts/worktree.sh +136 -0
- package/dist/commands/agents.js +28 -12
- package/dist/commands/api-keys.js +100 -20
- package/dist/commands/core.d.ts +90 -0
- package/dist/commands/core.js +279 -0
- package/dist/commands/files-browse.d.ts +21 -0
- package/dist/commands/files-browse.js +145 -24
- package/dist/commands/files.js +33 -7
- package/dist/commands/members.js +16 -5
- package/dist/commands/pack-install.d.ts +14 -0
- package/dist/commands/pack-install.js +42 -2
- package/dist/commands/pkg-install.js +39 -2
- package/dist/commands/run.js +6 -2
- package/dist/commands/secrets.js +120 -65
- package/dist/main.js +6 -0
- package/dist/utils/api-key-command-gate.d.ts +45 -0
- package/dist/utils/api-key-command-gate.js +51 -0
- package/dist/utils/hq-roots.d.ts +91 -0
- package/dist/utils/hq-roots.js +212 -0
- package/dist/utils/resolve-vault-credential.d.ts +63 -3
- package/dist/utils/resolve-vault-credential.js +152 -6
- package/dist/utils/run-bundled-script.d.ts +66 -0
- package/dist/utils/run-bundled-script.js +80 -0
- package/dist/utils/vault-api.d.ts +21 -0
- package/dist/utils/vault-api.js +74 -3
- package/package.json +7 -1
- package/.claude/policies/hq-cli-interactive-reads-use-sts-vend.md +0 -49
- package/.github/workflows/ci.yml +0 -46
- package/.github/workflows/publish.yml +0 -36
- package/.github/workflows/scripts/smoke-test-pkg.sh +0 -97
- package/e2e/cli.test.ts +0 -128
- package/eslint.config.js +0 -37
- package/pnpm-workspace.yaml +0 -4
- package/scripts/chmod-bins.mjs +0 -49
- package/scripts/generate-dsn.mjs +0 -26
- package/scripts/generate-scan-packages-table.mjs +0 -113
- package/scripts/smoke-sources-signals.sh +0 -103
- package/src/bin/hq-auth-refresh.ts +0 -43
- package/src/cli-version.ts +0 -13
- package/src/commands/__fixtures__/make-tar.ts +0 -126
- package/src/commands/_patterns.ts +0 -10
- package/src/commands/add.ts +0 -75
- package/src/commands/agents.test.ts +0 -527
- package/src/commands/agents.ts +0 -923
- package/src/commands/api-keys.test.ts +0 -291
- package/src/commands/api-keys.ts +0 -382
- package/src/commands/artifact-verify.test.ts +0 -177
- package/src/commands/auth.test.ts +0 -82
- package/src/commands/auth.ts +0 -213
- package/src/commands/billing.test.ts +0 -158
- package/src/commands/billing.ts +0 -146
- package/src/commands/channels.test.ts +0 -74
- package/src/commands/channels.ts +0 -91
- package/src/commands/cloud-demote.test.ts +0 -401
- package/src/commands/cloud-demote.ts +0 -278
- package/src/commands/cloud-provision.test.ts +0 -1117
- package/src/commands/cloud-provision.ts +0 -942
- package/src/commands/cloud.pull-all.test.ts +0 -711
- package/src/commands/cloud.pull-per-company.test.ts +0 -188
- package/src/commands/cloud.push-all.test.ts +0 -485
- package/src/commands/cloud.scope-excluded-warning.test.ts +0 -22
- package/src/commands/cloud.selectors.test.ts +0 -208
- package/src/commands/cloud.status.test.ts +0 -160
- package/src/commands/cloud.test.ts +0 -41
- package/src/commands/cloud.ts +0 -2130
- package/src/commands/company.test.ts +0 -202
- package/src/commands/company.ts +0 -148
- package/src/commands/creators.test.ts +0 -60
- package/src/commands/creators.ts +0 -117
- package/src/commands/crm.test.ts +0 -179
- package/src/commands/crm.ts +0 -236
- package/src/commands/db-migrate.ts +0 -55
- package/src/commands/db-provision.ts +0 -114
- package/src/commands/db-sql.ts +0 -124
- package/src/commands/db-status.ts +0 -108
- package/src/commands/db.ts +0 -26
- package/src/commands/dm.test.ts +0 -748
- package/src/commands/dm.ts +0 -1226
- package/src/commands/feedback.test.ts +0 -413
- package/src/commands/feedback.ts +0 -169
- package/src/commands/files-browse.test.ts +0 -1249
- package/src/commands/files-browse.ts +0 -1506
- package/src/commands/files-delete.test.ts +0 -484
- package/src/commands/files-recovery.test.ts +0 -370
- package/src/commands/files.test.ts +0 -722
- package/src/commands/files.ts +0 -1446
- package/src/commands/group-grants.test.ts +0 -330
- package/src/commands/group-grants.ts +0 -455
- package/src/commands/groups.test.ts +0 -44
- package/src/commands/groups.ts +0 -368
- package/src/commands/integrations.test.ts +0 -633
- package/src/commands/integrations.ts +0 -507
- package/src/commands/list.ts +0 -85
- package/src/commands/login.ts +0 -52
- package/src/commands/logout.ts +0 -30
- package/src/commands/marketplace-install.test.ts +0 -543
- package/src/commands/marketplace-security.test.ts +0 -646
- package/src/commands/mcp-registration.test.ts +0 -2787
- package/src/commands/mcp-registration.ts +0 -2612
- package/src/commands/mcp-status.test.ts +0 -483
- package/src/commands/mcp-status.ts +0 -575
- package/src/commands/mcp-status.us011.test.ts +0 -243
- package/src/commands/meetings.test.ts +0 -522
- package/src/commands/meetings.ts +0 -789
- package/src/commands/members.test.ts +0 -1474
- package/src/commands/members.ts +0 -1181
- package/src/commands/onboard-identity-guard.test.ts +0 -99
- package/src/commands/onboard-identity-guard.ts +0 -103
- package/src/commands/onboard-join.test.ts +0 -96
- package/src/commands/onboard-join.ts +0 -101
- package/src/commands/onboard-warning.test.ts +0 -26
- package/src/commands/onboard-warning.ts +0 -12
- package/src/commands/onboard.ts +0 -267
- package/src/commands/outposts-heartbeat.test.ts +0 -299
- package/src/commands/outposts-heartbeat.ts +0 -310
- package/src/commands/outposts-replica-sync.test.ts +0 -272
- package/src/commands/outposts-self-deploy.test.ts +0 -243
- package/src/commands/outposts.test.ts +0 -1039
- package/src/commands/outposts.ts +0 -1935
- package/src/commands/pack-install-secret-authorization.test.ts +0 -115
- package/src/commands/pack-install.test.ts +0 -1469
- package/src/commands/pack-install.ts +0 -2256
- package/src/commands/pack-update-cache.test.ts +0 -149
- package/src/commands/packs-update-api-key.test.ts +0 -105
- package/src/commands/packs.test.ts +0 -88
- package/src/commands/packs.ts +0 -622
- package/src/commands/people.test.ts +0 -807
- package/src/commands/people.ts +0 -422
- package/src/commands/pkg-install.dispatch.test.ts +0 -103
- package/src/commands/pkg-install.ts +0 -221
- package/src/commands/pkg-list.test.ts +0 -79
- package/src/commands/pkg-list.ts +0 -13
- package/src/commands/pkg-remove.ts +0 -71
- package/src/commands/pkg-update.ts +0 -189
- package/src/commands/publish.test.ts +0 -650
- package/src/commands/publish.ts +0 -580
- package/src/commands/reindex.test.ts +0 -259
- package/src/commands/reindex.ts +0 -274
- package/src/commands/rescue.test.ts +0 -39
- package/src/commands/rescue.ts +0 -210
- package/src/commands/run.env-local.test.ts +0 -88
- package/src/commands/run.test.ts +0 -45
- package/src/commands/run.ts +0 -190
- package/src/commands/safe-extract.test.ts +0 -459
- package/src/commands/safe-extract.ts +0 -444
- package/src/commands/secrets-scope.test.ts +0 -56
- package/src/commands/secrets-scope.ts +0 -32
- package/src/commands/secrets.parse-destination.test.ts +0 -38
- package/src/commands/secrets.test.ts +0 -2050
- package/src/commands/secrets.ts +0 -2249
- package/src/commands/signals.ts +0 -351
- package/src/commands/skill.test.ts +0 -770
- package/src/commands/skill.ts +0 -796
- package/src/commands/sources.ts +0 -362
- package/src/commands/sync-mode.test.ts +0 -366
- package/src/commands/sync-mode.ts +0 -387
- package/src/commands/sync-narrow.test.ts +0 -633
- package/src/commands/sync-narrow.ts +0 -555
- package/src/commands/sync.ts +0 -165
- package/src/commands/team-sync.ts +0 -628
- package/src/commands/update.ts +0 -71
- package/src/commands/whoami.ts +0 -83
- package/src/commands/workers.test.ts +0 -158
- package/src/commands/workers.ts +0 -301
- package/src/index.test.ts +0 -32
- package/src/index.ts +0 -20
- package/src/lib/db/control-plane.test.ts +0 -59
- package/src/lib/db/control-plane.ts +0 -113
- package/src/lib/db/local.test.ts +0 -81
- package/src/lib/db/local.ts +0 -148
- package/src/lib/db/migrate.test.ts +0 -133
- package/src/lib/db/migrate.ts +0 -137
- package/src/lib/db/paths.test.ts +0 -112
- package/src/lib/db/paths.ts +0 -128
- package/src/lib/db/remote-engine.test.ts +0 -44
- package/src/lib/db/remote-engine.ts +0 -148
- package/src/lib/db/remote-sql.test.ts +0 -32
- package/src/lib/db/remote-sql.ts +0 -62
- package/src/lib/db/sql.test.ts +0 -106
- package/src/lib/db/sql.ts +0 -192
- package/src/lib/hq-cloud-dep.smoke.test.ts +0 -75
- package/src/lib/local-tree-diff.test.ts +0 -301
- package/src/lib/local-tree-diff.ts +0 -362
- package/src/lib/narrow-hint-banner.test.ts +0 -235
- package/src/lib/narrow-hint-banner.ts +0 -212
- package/src/lib/plan-limit-nag.test.ts +0 -317
- package/src/lib/plan-limit-nag.ts +0 -264
- package/src/main.ts +0 -371
- package/src/node-preflight.test.ts +0 -60
- package/src/node-preflight.ts +0 -67
- package/src/outpost/session-heartbeat-bounds.test.ts +0 -195
- package/src/outpost/session-heartbeat-guard.test.ts +0 -105
- package/src/outpost/session-heartbeat-publisher.test.ts +0 -178
- package/src/outpost/session-heartbeat-publisher.ts +0 -186
- package/src/outpost/session-heartbeat-retain-guard.test.ts +0 -126
- package/src/outpost/session-heartbeat.test.ts +0 -459
- package/src/outpost/session-heartbeat.ts +0 -877
- package/src/packaging.test.ts +0 -109
- package/src/run/__fixtures__/discover-schemas/example.env.schema +0 -4
- package/src/run/discover-schemas.test.ts +0 -153
- package/src/run/discover-schemas.ts +0 -79
- package/src/run/hq-plugin.test.ts +0 -371
- package/src/run/hq-plugin.ts +0 -277
- package/src/run/varlock-shape.test.ts +0 -57
- package/src/schemas/hq-package.schema.json +0 -155
- package/src/sentry-before-send.test.ts +0 -359
- package/src/sentry-before-send.ts +0 -221
- package/src/sentry-epipe.test.ts +0 -37
- package/src/sentry-release.test.ts +0 -54
- package/src/sentry.test.ts +0 -24
- package/src/sentry.ts +0 -54
- package/src/strategies/link.ts +0 -62
- package/src/strategies/merge.ts +0 -142
- package/src/types.ts +0 -146
- package/src/utils/__fixtures__/scan-packages.generated-block.sh +0 -23
- package/src/utils/auth-error.test.ts +0 -40
- package/src/utils/auth-error.ts +0 -42
- package/src/utils/billing-gate.test.ts +0 -95
- package/src/utils/billing-gate.ts +0 -228
- package/src/utils/breadcrumb-buffer.ts +0 -18
- package/src/utils/cli-telemetry.test.ts +0 -153
- package/src/utils/cli-telemetry.ts +0 -61
- package/src/utils/cognito-session.machine.test.ts +0 -109
- package/src/utils/cognito-session.refresh.test.ts +0 -84
- package/src/utils/cognito-session.test.ts +0 -328
- package/src/utils/cognito-session.ts +0 -448
- package/src/utils/company-selection-error.test.ts +0 -42
- package/src/utils/company-selection-error.ts +0 -45
- package/src/utils/contribution-table.ts +0 -83
- package/src/utils/environmental-error.test.ts +0 -45
- package/src/utils/environmental-error.ts +0 -39
- package/src/utils/epipe.test.ts +0 -28
- package/src/utils/epipe.ts +0 -29
- package/src/utils/expected-cli-error.test.ts +0 -28
- package/src/utils/expected-cli-error.ts +0 -39
- package/src/utils/feedback-diagnostics.test.ts +0 -183
- package/src/utils/feedback-diagnostics.ts +0 -123
- package/src/utils/feedback-screenshots.test.ts +0 -134
- package/src/utils/feedback-screenshots.ts +0 -124
- package/src/utils/feedback-versions.test.ts +0 -98
- package/src/utils/feedback-versions.ts +0 -68
- package/src/utils/git.ts +0 -74
- package/src/utils/integrity.ts +0 -59
- package/src/utils/intercepted-process-exit.test.ts +0 -37
- package/src/utils/intercepted-process-exit.ts +0 -36
- package/src/utils/login-provider.test.ts +0 -42
- package/src/utils/login-provider.ts +0 -30
- package/src/utils/manifest.test.ts +0 -167
- package/src/utils/manifest.ts +0 -131
- package/src/utils/pack-contributions.test.ts +0 -603
- package/src/utils/pack-contributions.ts +0 -450
- package/src/utils/people.ts +0 -215
- package/src/utils/registry-client.ts +0 -204
- package/src/utils/registry.ts +0 -90
- package/src/utils/resolve-vault-credential.test.ts +0 -69
- package/src/utils/resolve-vault-credential.ts +0 -60
- package/src/utils/sandbox-runner-client.test.ts +0 -279
- package/src/utils/sandbox-runner-client.ts +0 -260
- package/src/utils/secrets-cache.ts +0 -183
- package/src/utils/sentry-identity.ts +0 -45
- package/src/utils/settle-with-timeout.test.ts +0 -21
- package/src/utils/settle-with-timeout.ts +0 -22
- package/src/utils/user-yaml-error.test.ts +0 -24
- package/src/utils/user-yaml-error.ts +0 -30
- package/src/utils/vault-api.test.ts +0 -536
- package/src/utils/vault-api.ts +0 -382
- package/src/utils/version-check.test.ts +0 -176
- package/src/utils/version-check.ts +0 -155
- package/src/utils/version-gate.test.ts +0 -1024
- package/src/utils/version-gate.ts +0 -702
- package/test/commands/db-tenant-isolation.test.ts +0 -94
- package/test/commands/db.test.ts +0 -85
- package/test/commands/signals.test.ts +0 -207
- package/test/commands/sources.test.ts +0 -231
- package/test/e2e/smoke-install-mcp.sh +0 -113
- package/test/fixtures/hq-pack-smoke-mcp/mcp/smoke-http.json +0 -1
- package/test/fixtures/hq-pack-smoke-mcp/package.yaml +0 -11
- package/test/fixtures/signals/action_item/sample.md +0 -16
- package/test/fixtures/signals/summary/sample.md +0 -12
- package/test/fixtures/sources/meetings/sample.md +0 -25
- package/test/helpers/cli-runner.ts +0 -150
- package/test/helpers/s3-list-mock.ts +0 -79
- package/test/helpers/vault-service-mock.ts +0 -295
- package/test/sources-signals/smoke.test.ts +0 -228
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -11
- package/vitest.e2e.config.ts +0 -7
package/src/commands/groups.ts
DELETED
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
4
|
-
import { vaultApiFetch, getCompanyUid } from "./secrets.js";
|
|
5
|
-
import { GROUP_ID_PATTERN } from "./_patterns.js";
|
|
6
|
-
|
|
7
|
-
const EMAIL_PATTERN = /^[^\s]+@[^\s]+$/;
|
|
8
|
-
const PERSON_UID_PATTERN = /^prs_[A-Za-z0-9_-]+$/;
|
|
9
|
-
const AGENT_UID_PATTERN = /^agt_[A-Za-z0-9_-]+$/;
|
|
10
|
-
|
|
11
|
-
const INVALID_PRINCIPAL_HINT =
|
|
12
|
-
"must be an email address, a personUid (prs_…), or an agentUid (agt_…)";
|
|
13
|
-
|
|
14
|
-
export function detectPrincipalType(
|
|
15
|
-
principal: string,
|
|
16
|
-
): { granteeType: "email" | "person"; granteeId: string } | null {
|
|
17
|
-
if (EMAIL_PATTERN.test(principal)) {
|
|
18
|
-
// Server normalizes email again; we normalize here so local validation /
|
|
19
|
-
// cache keys agree with the server-side canonicalization.
|
|
20
|
-
return { granteeType: "email", granteeId: principal.trim().toLowerCase() };
|
|
21
|
-
}
|
|
22
|
-
// Agent uids ride the same personUid wire slot as people — that is the
|
|
23
|
-
// server contract (group members, DM recipients, and memberships all carry
|
|
24
|
-
// agt_* in the personUid field; see hq-pro handlers).
|
|
25
|
-
if (PERSON_UID_PATTERN.test(principal) || AGENT_UID_PATTERN.test(principal)) {
|
|
26
|
-
return { granteeType: "person", granteeId: principal };
|
|
27
|
-
}
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function shortDate(iso: string): string {
|
|
32
|
-
return iso.slice(0, 10);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function registerGroupsCommand(program: Command): void {
|
|
36
|
-
const groups = program
|
|
37
|
-
.command("groups")
|
|
38
|
-
.description("Manage groups in HQ vault")
|
|
39
|
-
.option("--company <slug>", "Company slug (resolves to companyUid)");
|
|
40
|
-
|
|
41
|
-
groups
|
|
42
|
-
.command("create <groupId>")
|
|
43
|
-
.description("Create a new group")
|
|
44
|
-
.requiredOption("--name <name>", "Human-readable group name")
|
|
45
|
-
.option("--description <desc>", "Optional description")
|
|
46
|
-
.action(async (rawGroupId: string, opts: { name: string; description?: string }) => {
|
|
47
|
-
try {
|
|
48
|
-
// Auto-prepend `grp_` when the caller passes a bare id (`core` →
|
|
49
|
-
// `grp_core`); the server does the same as a defense-in-depth
|
|
50
|
-
// backstop. The post-prepend form must still match GROUP_ID_PATTERN.
|
|
51
|
-
const groupId = rawGroupId.startsWith("grp_") ? rawGroupId : `grp_${rawGroupId}`;
|
|
52
|
-
if (!GROUP_ID_PATTERN.test(groupId)) {
|
|
53
|
-
console.error(chalk.red(`Invalid group id '${rawGroupId}': resulting id '${groupId}' must match grp_<alphanumeric, underscore, hyphen>`));
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const token = await ensureCognitoToken();
|
|
58
|
-
const companySlug = groups.opts().company as string | undefined;
|
|
59
|
-
const companyUid = await getCompanyUid(token, companySlug);
|
|
60
|
-
|
|
61
|
-
const body: Record<string, unknown> = { groupId, name: opts.name };
|
|
62
|
-
if (opts.description) body.description = opts.description;
|
|
63
|
-
|
|
64
|
-
const res = await vaultApiFetch({
|
|
65
|
-
token,
|
|
66
|
-
path: `/secrets/${encodeURIComponent(companyUid)}/groups`,
|
|
67
|
-
method: "POST",
|
|
68
|
-
body,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
if (!res.ok) {
|
|
72
|
-
const err = await res.json().catch(() => ({})) as Record<string, string>;
|
|
73
|
-
if (res.status === 401) {
|
|
74
|
-
console.error(chalk.red("Not authenticated — please run `hq login`"));
|
|
75
|
-
} else if (res.status === 403) {
|
|
76
|
-
console.error(chalk.red("Not authorized — owner role required"));
|
|
77
|
-
} else if (res.status === 409) {
|
|
78
|
-
console.error(chalk.red(`Group already exists: ${groupId}`));
|
|
79
|
-
} else if (res.status >= 500) {
|
|
80
|
-
console.error(chalk.red(`Server error: ${err.error ?? res.statusText}`));
|
|
81
|
-
} else {
|
|
82
|
-
console.error(chalk.red(err.message ?? err.error ?? "Invalid request"));
|
|
83
|
-
}
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
console.log(chalk.green(`Group '${groupId}' (${opts.name}) created`));
|
|
88
|
-
} catch (err) {
|
|
89
|
-
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
groups
|
|
95
|
-
.command("delete <groupId>")
|
|
96
|
-
.description("Delete a group")
|
|
97
|
-
.action(async (groupId: string) => {
|
|
98
|
-
try {
|
|
99
|
-
if (!GROUP_ID_PATTERN.test(groupId)) {
|
|
100
|
-
console.error(chalk.red(`Invalid group id '${groupId}': must match grp_<alphanumeric>`));
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const token = await ensureCognitoToken();
|
|
105
|
-
const companySlug = groups.opts().company as string | undefined;
|
|
106
|
-
const companyUid = await getCompanyUid(token, companySlug);
|
|
107
|
-
|
|
108
|
-
const res = await vaultApiFetch({
|
|
109
|
-
token,
|
|
110
|
-
path: `/secrets/${encodeURIComponent(companyUid)}/groups`,
|
|
111
|
-
method: "DELETE",
|
|
112
|
-
query: { groupId },
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
if (!res.ok) {
|
|
116
|
-
const err = await res.json().catch(() => ({})) as Record<string, string>;
|
|
117
|
-
if (res.status === 401) {
|
|
118
|
-
console.error(chalk.red("Not authenticated — please run `hq login`"));
|
|
119
|
-
} else if (res.status === 403) {
|
|
120
|
-
console.error(chalk.red("Not authorized — owner role required"));
|
|
121
|
-
} else if (res.status === 404) {
|
|
122
|
-
console.error(chalk.red(`Group not found: ${groupId}`));
|
|
123
|
-
} else if (res.status >= 500) {
|
|
124
|
-
console.error(chalk.red(`Server error: ${err.error ?? res.statusText}`));
|
|
125
|
-
} else {
|
|
126
|
-
console.error(chalk.red(err.message ?? err.error ?? "Invalid request"));
|
|
127
|
-
}
|
|
128
|
-
process.exit(1);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
console.log(chalk.green(`Group '${groupId}' deleted`));
|
|
132
|
-
} catch (err) {
|
|
133
|
-
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
groups
|
|
139
|
-
.command("add <groupId> <principal>")
|
|
140
|
-
.description("Add a person or agent to a group (principal: email, personUid, or agentUid)")
|
|
141
|
-
.action(async (groupId: string, principal: string) => {
|
|
142
|
-
try {
|
|
143
|
-
if (!GROUP_ID_PATTERN.test(groupId)) {
|
|
144
|
-
console.error(chalk.red(`Invalid group id '${groupId}': must match grp_<alphanumeric>`));
|
|
145
|
-
process.exit(1);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const detected = detectPrincipalType(principal);
|
|
149
|
-
if (!detected) {
|
|
150
|
-
console.error(chalk.red(`Invalid principal '${principal}': ${INVALID_PRINCIPAL_HINT}`));
|
|
151
|
-
process.exit(1);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const token = await ensureCognitoToken();
|
|
155
|
-
const companySlug = groups.opts().company as string | undefined;
|
|
156
|
-
const companyUid = await getCompanyUid(token, companySlug);
|
|
157
|
-
|
|
158
|
-
const res = await vaultApiFetch({
|
|
159
|
-
token,
|
|
160
|
-
path: `/secrets/${encodeURIComponent(companyUid)}/groups/members`,
|
|
161
|
-
method: "POST",
|
|
162
|
-
body: { groupId, granteeType: detected.granteeType, granteeId: detected.granteeId },
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
if (!res.ok) {
|
|
166
|
-
const err = await res.json().catch(() => ({})) as Record<string, string>;
|
|
167
|
-
if (res.status === 401) {
|
|
168
|
-
console.error(chalk.red("Not authenticated — please run `hq login`"));
|
|
169
|
-
} else if (res.status === 403) {
|
|
170
|
-
console.error(chalk.red("Not authorized — owner role or group creator required"));
|
|
171
|
-
} else if (res.status === 404) {
|
|
172
|
-
// Server provides a helpful message (email not found vs group not found)
|
|
173
|
-
console.error(chalk.red(err.error ?? `Not found`));
|
|
174
|
-
} else if (res.status >= 500) {
|
|
175
|
-
console.error(chalk.red(`Server error: ${err.error ?? res.statusText}`));
|
|
176
|
-
} else {
|
|
177
|
-
console.error(chalk.red(err.message ?? err.error ?? "Invalid request"));
|
|
178
|
-
}
|
|
179
|
-
process.exit(1);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
console.log(chalk.green(`Added ${principal} to group '${groupId}'`));
|
|
183
|
-
} catch (err) {
|
|
184
|
-
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
185
|
-
process.exit(1);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
groups
|
|
190
|
-
.command("remove <groupId> <principal>")
|
|
191
|
-
.description("Remove a person or agent from a group (principal: email, personUid, or agentUid)")
|
|
192
|
-
.action(async (groupId: string, principal: string) => {
|
|
193
|
-
try {
|
|
194
|
-
if (!GROUP_ID_PATTERN.test(groupId)) {
|
|
195
|
-
console.error(chalk.red(`Invalid group id '${groupId}': must match grp_<alphanumeric>`));
|
|
196
|
-
process.exit(1);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const detected = detectPrincipalType(principal);
|
|
200
|
-
if (!detected) {
|
|
201
|
-
console.error(chalk.red(`Invalid principal '${principal}': ${INVALID_PRINCIPAL_HINT}`));
|
|
202
|
-
process.exit(1);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const token = await ensureCognitoToken();
|
|
206
|
-
const companySlug = groups.opts().company as string | undefined;
|
|
207
|
-
const companyUid = await getCompanyUid(token, companySlug);
|
|
208
|
-
|
|
209
|
-
const res = await vaultApiFetch({
|
|
210
|
-
token,
|
|
211
|
-
path: `/secrets/${encodeURIComponent(companyUid)}/groups/members`,
|
|
212
|
-
method: "DELETE",
|
|
213
|
-
query: {
|
|
214
|
-
groupId,
|
|
215
|
-
granteeType: detected.granteeType,
|
|
216
|
-
granteeId: detected.granteeId,
|
|
217
|
-
},
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
if (!res.ok) {
|
|
221
|
-
const err = await res.json().catch(() => ({})) as Record<string, string>;
|
|
222
|
-
if (res.status === 401) {
|
|
223
|
-
console.error(chalk.red("Not authenticated — please run `hq login`"));
|
|
224
|
-
} else if (res.status === 403) {
|
|
225
|
-
console.error(chalk.red("Not authorized — owner role or group creator required"));
|
|
226
|
-
} else if (res.status === 404) {
|
|
227
|
-
console.error(chalk.red(err.error ?? `Not found`));
|
|
228
|
-
} else if (res.status >= 500) {
|
|
229
|
-
console.error(chalk.red(`Server error: ${err.error ?? res.statusText}`));
|
|
230
|
-
} else {
|
|
231
|
-
console.error(chalk.red(err.message ?? err.error ?? "Invalid request"));
|
|
232
|
-
}
|
|
233
|
-
process.exit(1);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
console.log(chalk.green(`Removed ${principal} from group '${groupId}'`));
|
|
237
|
-
} catch (err) {
|
|
238
|
-
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
239
|
-
process.exit(1);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
groups
|
|
244
|
-
.command("list")
|
|
245
|
-
.description("List all groups in the company")
|
|
246
|
-
.action(async () => {
|
|
247
|
-
try {
|
|
248
|
-
const token = await ensureCognitoToken();
|
|
249
|
-
const companySlug = groups.opts().company as string | undefined;
|
|
250
|
-
const companyUid = await getCompanyUid(token, companySlug);
|
|
251
|
-
|
|
252
|
-
const res = await vaultApiFetch({
|
|
253
|
-
token,
|
|
254
|
-
path: `/secrets/${encodeURIComponent(companyUid)}/groups`,
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
if (!res.ok) {
|
|
258
|
-
const err = await res.json().catch(() => ({})) as Record<string, string>;
|
|
259
|
-
if (res.status === 401) {
|
|
260
|
-
console.error(chalk.red("Not authenticated — please run `hq login`"));
|
|
261
|
-
} else if (res.status === 403) {
|
|
262
|
-
console.error(chalk.red("Not authorized to list groups"));
|
|
263
|
-
} else if (res.status >= 500) {
|
|
264
|
-
console.error(chalk.red(`Server error: ${err.error ?? res.statusText}`));
|
|
265
|
-
} else {
|
|
266
|
-
console.error(chalk.red(err.message ?? err.error ?? "Invalid request"));
|
|
267
|
-
}
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const data = await res.json() as {
|
|
272
|
-
groups: Array<{ groupId: string; name: string; description?: string; createdAt: string }>;
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
if (data.groups.length === 0) {
|
|
276
|
-
console.log(chalk.gray("No groups in this company yet."));
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const idW = Math.max(8, ...data.groups.map((g) => g.groupId.length));
|
|
281
|
-
const nameW = Math.max(4, ...data.groups.map((g) => g.name.length));
|
|
282
|
-
const descW = Math.max(11, ...data.groups.map((g) => (g.description ?? "").length));
|
|
283
|
-
console.log(chalk.bold([
|
|
284
|
-
"GROUP_ID".padEnd(idW),
|
|
285
|
-
"NAME".padEnd(nameW),
|
|
286
|
-
"DESCRIPTION".padEnd(descW),
|
|
287
|
-
"CREATED_AT",
|
|
288
|
-
].join(" ")));
|
|
289
|
-
for (const g of data.groups) {
|
|
290
|
-
console.log([
|
|
291
|
-
g.groupId.padEnd(idW),
|
|
292
|
-
g.name.padEnd(nameW),
|
|
293
|
-
(g.description ?? "").padEnd(descW),
|
|
294
|
-
shortDate(g.createdAt),
|
|
295
|
-
].join(" "));
|
|
296
|
-
}
|
|
297
|
-
} catch (err) {
|
|
298
|
-
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
299
|
-
process.exit(1);
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
groups
|
|
304
|
-
.command("members <groupId>")
|
|
305
|
-
.description("List members of a group")
|
|
306
|
-
.action(async (groupId: string) => {
|
|
307
|
-
try {
|
|
308
|
-
if (!GROUP_ID_PATTERN.test(groupId)) {
|
|
309
|
-
console.error(chalk.red(`Invalid group id '${groupId}': must match grp_<alphanumeric>`));
|
|
310
|
-
process.exit(1);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const token = await ensureCognitoToken();
|
|
314
|
-
const companySlug = groups.opts().company as string | undefined;
|
|
315
|
-
const companyUid = await getCompanyUid(token, companySlug);
|
|
316
|
-
|
|
317
|
-
const res = await vaultApiFetch({
|
|
318
|
-
token,
|
|
319
|
-
path: `/secrets/${encodeURIComponent(companyUid)}/groups/members`,
|
|
320
|
-
query: { groupId },
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
if (!res.ok) {
|
|
324
|
-
const err = await res.json().catch(() => ({})) as Record<string, string>;
|
|
325
|
-
if (res.status === 401) {
|
|
326
|
-
console.error(chalk.red("Not authenticated — please run `hq login`"));
|
|
327
|
-
} else if (res.status === 403) {
|
|
328
|
-
console.error(chalk.red("Not authorized to view group members"));
|
|
329
|
-
} else if (res.status === 404) {
|
|
330
|
-
console.error(chalk.red(`Group not found: ${groupId}`));
|
|
331
|
-
} else if (res.status >= 500) {
|
|
332
|
-
console.error(chalk.red(`Server error: ${err.error ?? res.statusText}`));
|
|
333
|
-
} else {
|
|
334
|
-
console.error(chalk.red(err.message ?? err.error ?? "Invalid request"));
|
|
335
|
-
}
|
|
336
|
-
process.exit(1);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const data = await res.json() as {
|
|
340
|
-
members: Array<{ personUid: string; addedBy: string; addedAt: string }>;
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
console.log(`Members of '${groupId}':`);
|
|
344
|
-
if (data.members.length === 0) {
|
|
345
|
-
console.log(chalk.gray("No members yet — use 'hq groups add' to add."));
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const uidW = Math.max(10, ...data.members.map((m) => m.personUid.length));
|
|
350
|
-
const byW = Math.max(8, ...data.members.map((m) => m.addedBy.length));
|
|
351
|
-
console.log(chalk.bold([
|
|
352
|
-
"PERSON_UID".padEnd(uidW),
|
|
353
|
-
"ADDED_BY".padEnd(byW),
|
|
354
|
-
"ADDED_AT",
|
|
355
|
-
].join(" ")));
|
|
356
|
-
for (const m of data.members) {
|
|
357
|
-
console.log([
|
|
358
|
-
m.personUid.padEnd(uidW),
|
|
359
|
-
m.addedBy.padEnd(byW),
|
|
360
|
-
shortDate(m.addedAt),
|
|
361
|
-
].join(" "));
|
|
362
|
-
}
|
|
363
|
-
} catch (err) {
|
|
364
|
-
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
365
|
-
process.exit(1);
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
}
|