@indigoai-us/hq-cli 5.77.13 → 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 +34 -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 +51 -2
- package/dist/commands/pkg-install.js +43 -0
- package/dist/commands/run.js +8 -0
- package/dist/commands/secrets.js +125 -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 -2247
- package/src/commands/pack-update-cache.test.ts +0 -149
- 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 -71
- package/src/commands/pkg-install.ts +0 -215
- 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 -184
- 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 -2037
- package/src/commands/secrets.ts +0 -2240
- 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
|
@@ -1,455 +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 "../utils/vault-api.js";
|
|
5
|
-
import { GROUP_ID_PATTERN } from "./_patterns.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* `hq group-grants` — manage cross-company group access grants.
|
|
9
|
-
*
|
|
10
|
-
* A *group grant* lets a group defined in a SOURCE company be granted a role
|
|
11
|
-
* on a TARGET company. The hq-pro API authorizes the operation against the
|
|
12
|
-
* TARGET company (the caller must be able to invite into it). The CLI is a
|
|
13
|
-
* thin client — it passes the right params and surfaces the server's 403 as an
|
|
14
|
-
* actionable cross-tenant permission error. NO authorization is reimplemented
|
|
15
|
-
* here.
|
|
16
|
-
*
|
|
17
|
-
* Backend contract (hq-pro feature/agency-cross-company-group-grants):
|
|
18
|
-
* - POST /group-grants { groupId, sourceCompanyUid, targetCompanyUid, role }
|
|
19
|
-
* - POST /group-grants/revoke { groupId, sourceCompanyUid, targetCompanyUid }
|
|
20
|
-
* - GET /group-grants/outbound?sourceCompanyUid&groupId
|
|
21
|
-
* - GET /group-grants/inbound?companyUid
|
|
22
|
-
*
|
|
23
|
-
* All require a Cognito idToken bearer; 403 (code FORBIDDEN) on authz failure.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
export const VALID_GRANT_ROLES = new Set(["owner", "admin", "member", "guest"]);
|
|
27
|
-
|
|
28
|
-
export type GrantRole = "owner" | "admin" | "member" | "guest";
|
|
29
|
-
|
|
30
|
-
export interface GroupGrant {
|
|
31
|
-
groupId: string;
|
|
32
|
-
sourceCompanyUid: string;
|
|
33
|
-
targetCompanyUid: string;
|
|
34
|
-
role: string;
|
|
35
|
-
grantedBy?: string;
|
|
36
|
-
grantedAt?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface GrantGroupOptions {
|
|
40
|
-
groupId: string;
|
|
41
|
-
sourceCompanyUid: string;
|
|
42
|
-
targetCompanyUid: string;
|
|
43
|
-
role: string;
|
|
44
|
-
token: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface RevokeGroupGrantOptions {
|
|
48
|
-
groupId: string;
|
|
49
|
-
sourceCompanyUid: string;
|
|
50
|
-
targetCompanyUid: string;
|
|
51
|
-
token: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Typed HTTP error mirroring members.ts's `InviteHttpError`. Carries the
|
|
56
|
-
* server status + optional error `code` (e.g. `FORBIDDEN`) so the command
|
|
57
|
-
* layer can render an actionable message and exit non-zero.
|
|
58
|
-
*/
|
|
59
|
-
export class GrantHttpError extends Error {
|
|
60
|
-
constructor(
|
|
61
|
-
public status: number,
|
|
62
|
-
message: string,
|
|
63
|
-
public code?: string,
|
|
64
|
-
) {
|
|
65
|
-
super(message);
|
|
66
|
-
this.name = "GrantHttpError";
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Map a `GrantHttpError` onto an actionable, human-readable message. The 403
|
|
72
|
-
* branch is the story's key cross-tenant case: authorization is enforced
|
|
73
|
-
* against the TARGET company, so the message names the target the caller
|
|
74
|
-
* lacks rights on.
|
|
75
|
-
*/
|
|
76
|
-
export function formatGrantHttpError(
|
|
77
|
-
status: number,
|
|
78
|
-
fallback: string,
|
|
79
|
-
ctx: { targetCompany?: string; code?: string } = {},
|
|
80
|
-
): string {
|
|
81
|
-
if (status === 401) return "Not authenticated — please run `hq login`";
|
|
82
|
-
if (status === 403) {
|
|
83
|
-
const target = ctx.targetCompany ? ` '${ctx.targetCompany}'` : "";
|
|
84
|
-
return (
|
|
85
|
-
`Permission denied: you must be an owner or admin of the target company${target} to grant a group into it. ` +
|
|
86
|
-
"The grant is authorized against the target company — ask an owner/admin there, or have them enable adminCanInvite."
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
if (status === 404) {
|
|
90
|
-
return `Not found: ${fallback}. Check the group id and that both companies exist.`;
|
|
91
|
-
}
|
|
92
|
-
if (status === 409) {
|
|
93
|
-
return `That grant already exists: ${fallback}`;
|
|
94
|
-
}
|
|
95
|
-
if (status >= 500) return `Server error: ${fallback}`;
|
|
96
|
-
return fallback;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function shortDate(iso: string): string {
|
|
100
|
-
return iso.slice(0, 10);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* POST /group-grants — grant a group's access to a target company at a role.
|
|
105
|
-
* Pure-ish: validates inputs, then calls the API client. Throws
|
|
106
|
-
* `GrantHttpError` on non-2xx so the command layer surfaces it.
|
|
107
|
-
*/
|
|
108
|
-
export async function grantGroup(options: GrantGroupOptions): Promise<GroupGrant> {
|
|
109
|
-
if (!GROUP_ID_PATTERN.test(options.groupId)) {
|
|
110
|
-
throw new Error(
|
|
111
|
-
`Invalid group id '${options.groupId}': must match grp_<alphanumeric, underscore, hyphen>`,
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
if (!VALID_GRANT_ROLES.has(options.role)) {
|
|
115
|
-
throw new Error(
|
|
116
|
-
`Invalid role '${options.role}': must be one of owner, admin, member, guest`,
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
if (options.sourceCompanyUid === options.targetCompanyUid) {
|
|
120
|
-
throw new Error(
|
|
121
|
-
"Source and target companies are the same — a group grant must cross company boundaries.",
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const res = await vaultApiFetch({
|
|
126
|
-
token: options.token,
|
|
127
|
-
path: "/group-grants",
|
|
128
|
-
method: "POST",
|
|
129
|
-
body: {
|
|
130
|
-
groupId: options.groupId,
|
|
131
|
-
sourceCompanyUid: options.sourceCompanyUid,
|
|
132
|
-
targetCompanyUid: options.targetCompanyUid,
|
|
133
|
-
role: options.role,
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
if (!res.ok) {
|
|
138
|
-
const err = (await res.json().catch(() => ({}))) as Record<string, string>;
|
|
139
|
-
throw new GrantHttpError(
|
|
140
|
-
res.status,
|
|
141
|
-
err.message ?? err.error ?? res.statusText,
|
|
142
|
-
err.code,
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const data = (await res.json()) as { grant?: GroupGrant };
|
|
147
|
-
return (
|
|
148
|
-
data.grant ?? {
|
|
149
|
-
groupId: options.groupId,
|
|
150
|
-
sourceCompanyUid: options.sourceCompanyUid,
|
|
151
|
-
targetCompanyUid: options.targetCompanyUid,
|
|
152
|
-
role: options.role,
|
|
153
|
-
}
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** POST /group-grants/revoke — remove a group's grant on a target company. */
|
|
158
|
-
export async function revokeGroupGrant(
|
|
159
|
-
options: RevokeGroupGrantOptions,
|
|
160
|
-
): Promise<void> {
|
|
161
|
-
if (!GROUP_ID_PATTERN.test(options.groupId)) {
|
|
162
|
-
throw new Error(
|
|
163
|
-
`Invalid group id '${options.groupId}': must match grp_<alphanumeric, underscore, hyphen>`,
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const res = await vaultApiFetch({
|
|
168
|
-
token: options.token,
|
|
169
|
-
path: "/group-grants/revoke",
|
|
170
|
-
method: "POST",
|
|
171
|
-
body: {
|
|
172
|
-
groupId: options.groupId,
|
|
173
|
-
sourceCompanyUid: options.sourceCompanyUid,
|
|
174
|
-
targetCompanyUid: options.targetCompanyUid,
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
if (!res.ok) {
|
|
179
|
-
const err = (await res.json().catch(() => ({}))) as Record<string, string>;
|
|
180
|
-
throw new GrantHttpError(
|
|
181
|
-
res.status,
|
|
182
|
-
err.message ?? err.error ?? res.statusText,
|
|
183
|
-
err.code,
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/** GET /group-grants/outbound — grants a source company's group(s) hold. */
|
|
189
|
-
export async function listOutboundGrants(
|
|
190
|
-
token: string,
|
|
191
|
-
sourceCompanyUid: string,
|
|
192
|
-
groupId: string,
|
|
193
|
-
): Promise<GroupGrant[]> {
|
|
194
|
-
if (!GROUP_ID_PATTERN.test(groupId)) {
|
|
195
|
-
throw new Error(
|
|
196
|
-
`Invalid group id '${groupId}': must match grp_<alphanumeric, underscore, hyphen>`,
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const res = await vaultApiFetch({
|
|
201
|
-
token,
|
|
202
|
-
path: "/group-grants/outbound",
|
|
203
|
-
query: { sourceCompanyUid, groupId },
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
if (!res.ok) {
|
|
207
|
-
const err = (await res.json().catch(() => ({}))) as Record<string, string>;
|
|
208
|
-
throw new GrantHttpError(
|
|
209
|
-
res.status,
|
|
210
|
-
err.message ?? err.error ?? res.statusText,
|
|
211
|
-
err.code,
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const data = (await res.json()) as { grants?: GroupGrant[] | null };
|
|
216
|
-
return data.grants ?? [];
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/** GET /group-grants/inbound — grants other companies' groups hold on us. */
|
|
220
|
-
export async function listInboundGrants(
|
|
221
|
-
token: string,
|
|
222
|
-
companyUid: string,
|
|
223
|
-
): Promise<GroupGrant[]> {
|
|
224
|
-
const res = await vaultApiFetch({
|
|
225
|
-
token,
|
|
226
|
-
path: "/group-grants/inbound",
|
|
227
|
-
query: { companyUid },
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
if (!res.ok) {
|
|
231
|
-
const err = (await res.json().catch(() => ({}))) as Record<string, string>;
|
|
232
|
-
throw new GrantHttpError(
|
|
233
|
-
res.status,
|
|
234
|
-
err.message ?? err.error ?? res.statusText,
|
|
235
|
-
err.code,
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const data = (await res.json()) as { grants?: GroupGrant[] | null };
|
|
240
|
-
return data.grants ?? [];
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function printGrantsTable(grants: GroupGrant[]): void {
|
|
244
|
-
const idW = Math.max(8, ...grants.map((g) => g.groupId.length));
|
|
245
|
-
const srcW = Math.max(6, ...grants.map((g) => g.sourceCompanyUid.length));
|
|
246
|
-
const tgtW = Math.max(6, ...grants.map((g) => g.targetCompanyUid.length));
|
|
247
|
-
const roleW = Math.max(4, ...grants.map((g) => g.role.length));
|
|
248
|
-
console.log(
|
|
249
|
-
chalk.bold(
|
|
250
|
-
[
|
|
251
|
-
"GROUP_ID".padEnd(idW),
|
|
252
|
-
"SOURCE".padEnd(srcW),
|
|
253
|
-
"TARGET".padEnd(tgtW),
|
|
254
|
-
"ROLE".padEnd(roleW),
|
|
255
|
-
"GRANTED_AT",
|
|
256
|
-
].join(" "),
|
|
257
|
-
),
|
|
258
|
-
);
|
|
259
|
-
for (const g of grants) {
|
|
260
|
-
console.log(
|
|
261
|
-
[
|
|
262
|
-
g.groupId.padEnd(idW),
|
|
263
|
-
g.sourceCompanyUid.padEnd(srcW),
|
|
264
|
-
g.targetCompanyUid.padEnd(tgtW),
|
|
265
|
-
g.role.padEnd(roleW),
|
|
266
|
-
g.grantedAt ? shortDate(g.grantedAt) : "",
|
|
267
|
-
].join(" "),
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
export function registerGroupGrantsCommand(program: Command): void {
|
|
273
|
-
const grants = program
|
|
274
|
-
.command("group-grants")
|
|
275
|
-
.description(
|
|
276
|
-
"Grant a group's access to another company (cross-company), and revoke or inspect those grants",
|
|
277
|
-
)
|
|
278
|
-
.option(
|
|
279
|
-
"--company <slug>",
|
|
280
|
-
"Source company slug (the company that owns the group; resolves to sourceCompanyUid)",
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
grants
|
|
284
|
-
.command("grant <groupId> <targetCompany>")
|
|
285
|
-
.description(
|
|
286
|
-
"Grant a group (from the source company) access to <targetCompany> at a role",
|
|
287
|
-
)
|
|
288
|
-
.option(
|
|
289
|
-
"--role <role>",
|
|
290
|
-
"Role the group receives on the target company: owner, admin, member, or guest",
|
|
291
|
-
"member",
|
|
292
|
-
)
|
|
293
|
-
.action(
|
|
294
|
-
async (
|
|
295
|
-
groupId: string,
|
|
296
|
-
targetCompany: string,
|
|
297
|
-
opts: { role: string },
|
|
298
|
-
) => {
|
|
299
|
-
try {
|
|
300
|
-
const token = await ensureCognitoToken();
|
|
301
|
-
const sourceSlug = grants.opts().company as string | undefined;
|
|
302
|
-
const sourceCompanyUid = await getCompanyUid(token, sourceSlug);
|
|
303
|
-
const targetCompanyUid = await getCompanyUid(token, targetCompany);
|
|
304
|
-
|
|
305
|
-
const grant = await grantGroup({
|
|
306
|
-
groupId,
|
|
307
|
-
sourceCompanyUid,
|
|
308
|
-
targetCompanyUid,
|
|
309
|
-
role: opts.role,
|
|
310
|
-
token,
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
console.log(
|
|
314
|
-
chalk.green(
|
|
315
|
-
`Granted group '${grant.groupId}' access to '${targetCompany}' as ${grant.role}`,
|
|
316
|
-
),
|
|
317
|
-
);
|
|
318
|
-
console.log(
|
|
319
|
-
chalk.dim(
|
|
320
|
-
` source: ${grant.sourceCompanyUid} → target: ${grant.targetCompanyUid}`,
|
|
321
|
-
),
|
|
322
|
-
);
|
|
323
|
-
} catch (err) {
|
|
324
|
-
if (err instanceof GrantHttpError) {
|
|
325
|
-
console.error(
|
|
326
|
-
chalk.red(
|
|
327
|
-
formatGrantHttpError(err.status, err.message, {
|
|
328
|
-
targetCompany,
|
|
329
|
-
code: err.code,
|
|
330
|
-
}),
|
|
331
|
-
),
|
|
332
|
-
);
|
|
333
|
-
process.exit(1);
|
|
334
|
-
}
|
|
335
|
-
console.error(
|
|
336
|
-
chalk.red("Error:"),
|
|
337
|
-
err instanceof Error ? err.message : String(err),
|
|
338
|
-
);
|
|
339
|
-
process.exit(1);
|
|
340
|
-
}
|
|
341
|
-
},
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
grants
|
|
345
|
-
.command("revoke <groupId> <targetCompany>")
|
|
346
|
-
.description(
|
|
347
|
-
"Revoke a group's grant on <targetCompany>",
|
|
348
|
-
)
|
|
349
|
-
.action(async (groupId: string, targetCompany: string) => {
|
|
350
|
-
try {
|
|
351
|
-
const token = await ensureCognitoToken();
|
|
352
|
-
const sourceSlug = grants.opts().company as string | undefined;
|
|
353
|
-
const sourceCompanyUid = await getCompanyUid(token, sourceSlug);
|
|
354
|
-
const targetCompanyUid = await getCompanyUid(token, targetCompany);
|
|
355
|
-
|
|
356
|
-
await revokeGroupGrant({
|
|
357
|
-
groupId,
|
|
358
|
-
sourceCompanyUid,
|
|
359
|
-
targetCompanyUid,
|
|
360
|
-
token,
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
console.log(
|
|
364
|
-
chalk.green(
|
|
365
|
-
`Revoked group '${groupId}' grant on '${targetCompany}'`,
|
|
366
|
-
),
|
|
367
|
-
);
|
|
368
|
-
} catch (err) {
|
|
369
|
-
if (err instanceof GrantHttpError) {
|
|
370
|
-
console.error(
|
|
371
|
-
chalk.red(
|
|
372
|
-
formatGrantHttpError(err.status, err.message, {
|
|
373
|
-
targetCompany,
|
|
374
|
-
code: err.code,
|
|
375
|
-
}),
|
|
376
|
-
),
|
|
377
|
-
);
|
|
378
|
-
process.exit(1);
|
|
379
|
-
}
|
|
380
|
-
console.error(
|
|
381
|
-
chalk.red("Error:"),
|
|
382
|
-
err instanceof Error ? err.message : String(err),
|
|
383
|
-
);
|
|
384
|
-
process.exit(1);
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
grants
|
|
389
|
-
.command("outbound")
|
|
390
|
-
.description(
|
|
391
|
-
"List grants a source-company group holds on other companies",
|
|
392
|
-
)
|
|
393
|
-
.requiredOption(
|
|
394
|
-
"--group <groupId>",
|
|
395
|
-
"Group id to inspect",
|
|
396
|
-
)
|
|
397
|
-
.action(async (opts: { group: string }) => {
|
|
398
|
-
try {
|
|
399
|
-
const token = await ensureCognitoToken();
|
|
400
|
-
const sourceSlug = grants.opts().company as string | undefined;
|
|
401
|
-
const sourceCompanyUid = await getCompanyUid(token, sourceSlug);
|
|
402
|
-
|
|
403
|
-
const list = await listOutboundGrants(token, sourceCompanyUid, opts.group);
|
|
404
|
-
if (list.length === 0) {
|
|
405
|
-
console.log(chalk.gray("No outbound group grants."));
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
printGrantsTable(list);
|
|
409
|
-
} catch (err) {
|
|
410
|
-
if (err instanceof GrantHttpError) {
|
|
411
|
-
console.error(
|
|
412
|
-
chalk.red(formatGrantHttpError(err.status, err.message)),
|
|
413
|
-
);
|
|
414
|
-
process.exit(1);
|
|
415
|
-
}
|
|
416
|
-
console.error(
|
|
417
|
-
chalk.red("Error:"),
|
|
418
|
-
err instanceof Error ? err.message : String(err),
|
|
419
|
-
);
|
|
420
|
-
process.exit(1);
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
grants
|
|
425
|
-
.command("inbound")
|
|
426
|
-
.description(
|
|
427
|
-
"List grants other companies' groups hold on this company",
|
|
428
|
-
)
|
|
429
|
-
.action(async () => {
|
|
430
|
-
try {
|
|
431
|
-
const token = await ensureCognitoToken();
|
|
432
|
-
const companySlug = grants.opts().company as string | undefined;
|
|
433
|
-
const companyUid = await getCompanyUid(token, companySlug);
|
|
434
|
-
|
|
435
|
-
const list = await listInboundGrants(token, companyUid);
|
|
436
|
-
if (list.length === 0) {
|
|
437
|
-
console.log(chalk.gray("No inbound group grants."));
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
printGrantsTable(list);
|
|
441
|
-
} catch (err) {
|
|
442
|
-
if (err instanceof GrantHttpError) {
|
|
443
|
-
console.error(
|
|
444
|
-
chalk.red(formatGrantHttpError(err.status, err.message)),
|
|
445
|
-
);
|
|
446
|
-
process.exit(1);
|
|
447
|
-
}
|
|
448
|
-
console.error(
|
|
449
|
-
chalk.red("Error:"),
|
|
450
|
-
err instanceof Error ? err.message : String(err),
|
|
451
|
-
);
|
|
452
|
-
process.exit(1);
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for `hq groups` principal detection (groups.ts).
|
|
3
|
-
*
|
|
4
|
-
* Regression coverage for feedback_edb1796a-24be-4b9f-bc60-a06f39d1c9f2:
|
|
5
|
-
* `hq groups add` rejected agent identities (agt_<ULID>), forcing per-agent
|
|
6
|
-
* secret shares instead of group-carried ACLs. Agent uids ride the same
|
|
7
|
-
* personUid wire slot as people (server contract — the group-members handler
|
|
8
|
-
* accepts granteeType "person" with an agt_* granteeId, and every ACL
|
|
9
|
-
* evaluation keys off caller.personUid, which for agent JWTs IS the agt_ uid).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { describe, expect, it } from "vitest";
|
|
13
|
-
|
|
14
|
-
import { detectPrincipalType } from "./groups.js";
|
|
15
|
-
|
|
16
|
-
describe("detectPrincipalType", () => {
|
|
17
|
-
it("detects an email principal and normalizes it", () => {
|
|
18
|
-
expect(detectPrincipalType("Jane@Example.COM")).toEqual({
|
|
19
|
-
granteeType: "email",
|
|
20
|
-
granteeId: "jane@example.com",
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("detects a personUid principal", () => {
|
|
25
|
-
expect(detectPrincipalType("prs_01ABCDEF")).toEqual({
|
|
26
|
-
granteeType: "person",
|
|
27
|
-
granteeId: "prs_01ABCDEF",
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("detects an agentUid principal and sends it in the person slot (regression)", () => {
|
|
32
|
-
expect(detectPrincipalType("agt_01KWFVWZYX8H1DN57ZPNZNKM6C")).toEqual({
|
|
33
|
-
granteeType: "person",
|
|
34
|
-
granteeId: "agt_01KWFVWZYX8H1DN57ZPNZNKM6C",
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("rejects malformed principals", () => {
|
|
39
|
-
expect(detectPrincipalType("agt_")).toBeNull();
|
|
40
|
-
expect(detectPrincipalType("prs_")).toBeNull();
|
|
41
|
-
expect(detectPrincipalType("grp_finance")).toBeNull();
|
|
42
|
-
expect(detectPrincipalType("not a principal")).toBeNull();
|
|
43
|
-
});
|
|
44
|
-
});
|