@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
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { vaultApiFetch } from "./vault-api.js";
|
|
4
|
-
|
|
5
|
-
export const MAX_SCREENSHOTS = 5;
|
|
6
|
-
// Per-image ceiling. Screenshots are PNG/JPEG captures; 10 MB is generous.
|
|
7
|
-
export const MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024;
|
|
8
|
-
|
|
9
|
-
// Extension → content type. Must stay in sync with the server's allowed set
|
|
10
|
-
// (hq-pro feedback-screenshots.ts ALLOWED_CONTENT_TYPES).
|
|
11
|
-
const EXT_CONTENT_TYPE: Record<string, string> = {
|
|
12
|
-
".png": "image/png",
|
|
13
|
-
".jpg": "image/jpeg",
|
|
14
|
-
".jpeg": "image/jpeg",
|
|
15
|
-
".webp": "image/webp",
|
|
16
|
-
".gif": "image/gif",
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export function contentTypeForPath(filePath: string): string {
|
|
20
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
21
|
-
const contentType = EXT_CONTENT_TYPE[ext];
|
|
22
|
-
if (!contentType) {
|
|
23
|
-
throw new Error(
|
|
24
|
-
`unsupported screenshot type: ${filePath} (allowed: ${Object.keys(EXT_CONTENT_TYPE).join(", ")})`,
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
return contentType;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ScreenshotInput {
|
|
31
|
-
path: string;
|
|
32
|
-
contentType: string;
|
|
33
|
-
bytes: Buffer;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Validate + read the given screenshot paths (count, type, existence, size). */
|
|
37
|
-
export function loadScreenshots(paths: string[]): ScreenshotInput[] {
|
|
38
|
-
if (paths.length > MAX_SCREENSHOTS) {
|
|
39
|
-
throw new Error(
|
|
40
|
-
`at most ${MAX_SCREENSHOTS} screenshots are allowed (got ${paths.length})`,
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
return paths.map((filePath) => {
|
|
44
|
-
const contentType = contentTypeForPath(filePath);
|
|
45
|
-
let bytes: Buffer;
|
|
46
|
-
try {
|
|
47
|
-
bytes = fs.readFileSync(filePath);
|
|
48
|
-
} catch {
|
|
49
|
-
throw new Error(`cannot read screenshot: ${filePath}`);
|
|
50
|
-
}
|
|
51
|
-
if (bytes.byteLength === 0) {
|
|
52
|
-
throw new Error(`screenshot is empty: ${filePath}`);
|
|
53
|
-
}
|
|
54
|
-
if (bytes.byteLength > MAX_SCREENSHOT_BYTES) {
|
|
55
|
-
throw new Error(
|
|
56
|
-
`screenshot too large: ${filePath} (${bytes.byteLength} bytes, max ${MAX_SCREENSHOT_BYTES})`,
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
return { path: filePath, contentType, bytes };
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface PresignResponse {
|
|
64
|
-
screenshots: Array<{ key: string; url: string; contentType: string }>;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Validate the screenshot paths, request presigned PUT URLs from the feedback
|
|
69
|
-
* endpoint, upload each image direct to S3, and return the object keys to
|
|
70
|
-
* attach to the feedback submission. Returns [] for no screenshots.
|
|
71
|
-
*
|
|
72
|
-
* `fetchImpl` is injectable for tests; defaults to the global fetch.
|
|
73
|
-
*/
|
|
74
|
-
export async function uploadScreenshots(opts: {
|
|
75
|
-
paths: string[];
|
|
76
|
-
token: string;
|
|
77
|
-
fetchImpl?: typeof fetch;
|
|
78
|
-
}): Promise<string[]> {
|
|
79
|
-
if (opts.paths.length === 0) return [];
|
|
80
|
-
const inputs = loadScreenshots(opts.paths);
|
|
81
|
-
|
|
82
|
-
const res = await vaultApiFetch({
|
|
83
|
-
token: opts.token,
|
|
84
|
-
path: "/v1/feedback/screenshots/presign",
|
|
85
|
-
method: "POST",
|
|
86
|
-
body: { contentTypes: inputs.map((i) => i.contentType) },
|
|
87
|
-
});
|
|
88
|
-
if (!res.ok) {
|
|
89
|
-
const data = await res.json().catch(() => ({}));
|
|
90
|
-
const msg =
|
|
91
|
-
data &&
|
|
92
|
-
typeof data === "object" &&
|
|
93
|
-
typeof (data as { error?: unknown }).error === "string"
|
|
94
|
-
? (data as { error: string }).error
|
|
95
|
-
: res.statusText;
|
|
96
|
-
throw new Error(`Failed to presign screenshots: ${msg}`);
|
|
97
|
-
}
|
|
98
|
-
const parsed = (await res.json()) as PresignResponse;
|
|
99
|
-
if (
|
|
100
|
-
!parsed ||
|
|
101
|
-
!Array.isArray(parsed.screenshots) ||
|
|
102
|
-
parsed.screenshots.length !== inputs.length
|
|
103
|
-
) {
|
|
104
|
-
throw new Error("Presign response did not match the requested screenshots");
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const doFetch = opts.fetchImpl ?? fetch;
|
|
108
|
-
const keys: string[] = [];
|
|
109
|
-
for (let i = 0; i < inputs.length; i++) {
|
|
110
|
-
const slot = parsed.screenshots[i]!;
|
|
111
|
-
const put = await doFetch(slot.url, {
|
|
112
|
-
method: "PUT",
|
|
113
|
-
headers: { "Content-Type": slot.contentType },
|
|
114
|
-
body: inputs[i]!.bytes,
|
|
115
|
-
});
|
|
116
|
-
if (!put.ok) {
|
|
117
|
-
throw new Error(
|
|
118
|
-
`Failed to upload screenshot ${inputs[i]!.path}: HTTP ${put.status}`,
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
keys.push(slot.key);
|
|
122
|
-
}
|
|
123
|
-
return keys;
|
|
124
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
|
-
import * as os from "node:os";
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
|
|
6
|
-
vi.mock("./manifest.js", () => ({
|
|
7
|
-
findHqRoot: vi.fn(() => "/fake/hq"),
|
|
8
|
-
}));
|
|
9
|
-
vi.mock("./pack-contributions.js", () => ({
|
|
10
|
-
readHqVersion: vi.fn(() => "15.0.20"),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
import { findHqRoot } from "./manifest.js";
|
|
14
|
-
import { readHqVersion } from "./pack-contributions.js";
|
|
15
|
-
import { CLI_VERSION } from "../cli-version.js";
|
|
16
|
-
import {
|
|
17
|
-
collectVersions,
|
|
18
|
-
readCoreVersion,
|
|
19
|
-
readSyncVersion,
|
|
20
|
-
} from "./feedback-versions.js";
|
|
21
|
-
|
|
22
|
-
describe("readSyncVersion", () => {
|
|
23
|
-
let tmpHome: string;
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "hqsync-"));
|
|
27
|
-
fs.mkdirSync(path.join(tmpHome, ".hq"), { recursive: true });
|
|
28
|
-
});
|
|
29
|
-
afterEach(() => {
|
|
30
|
-
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
function writeMarker(contents: string): void {
|
|
34
|
-
fs.writeFileSync(path.join(tmpHome, ".hq", "sync-version.json"), contents);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
it("reads the version the hq-sync app records", () => {
|
|
38
|
-
writeMarker(JSON.stringify({ version: "0.8.22-beta.1", updatedAt: "x" }));
|
|
39
|
-
expect(readSyncVersion(tmpHome)).toBe("0.8.22-beta.1");
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("returns null when the marker file is absent (hq-sync not installed)", () => {
|
|
43
|
-
expect(readSyncVersion(tmpHome)).toBeNull();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("returns null on malformed JSON rather than throwing", () => {
|
|
47
|
-
writeMarker("{ not json");
|
|
48
|
-
expect(readSyncVersion(tmpHome)).toBeNull();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("returns null when version is missing or not a non-empty string", () => {
|
|
52
|
-
writeMarker(JSON.stringify({ updatedAt: "x" }));
|
|
53
|
-
expect(readSyncVersion(tmpHome)).toBeNull();
|
|
54
|
-
writeMarker(JSON.stringify({ version: "" }));
|
|
55
|
-
expect(readSyncVersion(tmpHome)).toBeNull();
|
|
56
|
-
writeMarker(JSON.stringify({ version: 123 }));
|
|
57
|
-
expect(readSyncVersion(tmpHome)).toBeNull();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe("readCoreVersion", () => {
|
|
62
|
-
beforeEach(() => {
|
|
63
|
-
vi.mocked(findHqRoot).mockReset().mockReturnValue("/fake/hq");
|
|
64
|
-
vi.mocked(readHqVersion).mockReset().mockReturnValue("15.0.20");
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("reads hqVersion from the resolved HQ root", () => {
|
|
68
|
-
expect(readCoreVersion()).toBe("15.0.20");
|
|
69
|
-
expect(readHqVersion).toHaveBeenCalledWith("/fake/hq");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("returns null (never throws) when resolution fails", () => {
|
|
73
|
-
vi.mocked(findHqRoot).mockImplementation(() => {
|
|
74
|
-
throw new Error("no hq root");
|
|
75
|
-
});
|
|
76
|
-
expect(readCoreVersion()).toBeNull();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("returns null when core.yaml has no hqVersion", () => {
|
|
80
|
-
vi.mocked(readHqVersion).mockReturnValue(null);
|
|
81
|
-
expect(readCoreVersion()).toBeNull();
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe("collectVersions", () => {
|
|
86
|
-
beforeEach(() => {
|
|
87
|
-
vi.mocked(findHqRoot).mockReset().mockReturnValue("/fake/hq");
|
|
88
|
-
vi.mocked(readHqVersion).mockReset().mockReturnValue("15.0.20");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("always reports the cli version and best-effort core/sync", () => {
|
|
92
|
-
const versions = collectVersions();
|
|
93
|
-
expect(versions.cli).toBe(CLI_VERSION);
|
|
94
|
-
expect(versions.core).toBe("15.0.20");
|
|
95
|
-
// sync reads the real homedir marker, which won't exist in CI → null.
|
|
96
|
-
expect(["string", "object"]).toContain(typeof versions.sync); // string | null
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as os from "node:os";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { CLI_VERSION } from "../cli-version.js";
|
|
5
|
-
import { findHqRoot } from "./manifest.js";
|
|
6
|
-
import { readHqVersion } from "./pack-contributions.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* The three HQ component versions captured from the submitter's environment
|
|
10
|
-
* and attached to a feedback submission so triage can see exactly which
|
|
11
|
-
* versions a report came from.
|
|
12
|
-
*
|
|
13
|
-
* - `cli` — this hq-cli build (always known).
|
|
14
|
-
* - `core` — the HQ scaffold version from `core/core.yaml` (`hqVersion`);
|
|
15
|
-
* null when the command runs outside an HQ tree.
|
|
16
|
-
* - `sync` — the installed hq-sync menubar app version, which the app
|
|
17
|
-
* records at `~/.hq/sync-version.json` on startup; null when
|
|
18
|
-
* hq-sync is not installed (e.g. CLI-only / CI environments).
|
|
19
|
-
*/
|
|
20
|
-
export interface VersionInfo {
|
|
21
|
-
cli: string;
|
|
22
|
-
core: string | null;
|
|
23
|
-
sync: string | null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Best-effort read of the hq-core scaffold version (`core/core.yaml`
|
|
28
|
-
* `hqVersion`). Resolves the HQ root from the working directory; returns
|
|
29
|
-
* null when no HQ root / core.yaml is found rather than throwing — version
|
|
30
|
-
* capture must never break a feedback submission.
|
|
31
|
-
*/
|
|
32
|
-
export function readCoreVersion(): string | null {
|
|
33
|
-
try {
|
|
34
|
-
return readHqVersion(findHqRoot());
|
|
35
|
-
} catch {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Best-effort read of the hq-sync menubar app version. The app writes
|
|
42
|
-
* `{ version, updatedAt }` to `~/.hq/sync-version.json` on startup; the CLI
|
|
43
|
-
* reads it here. Returns null when the file is absent or malformed (hq-sync
|
|
44
|
-
* not installed, or an older build that predates the marker).
|
|
45
|
-
*/
|
|
46
|
-
export function readSyncVersion(homeDir: string = os.homedir()): string | null {
|
|
47
|
-
try {
|
|
48
|
-
const raw = fs.readFileSync(
|
|
49
|
-
path.join(homeDir, ".hq", "sync-version.json"),
|
|
50
|
-
"utf-8",
|
|
51
|
-
);
|
|
52
|
-
const parsed = JSON.parse(raw) as { version?: unknown };
|
|
53
|
-
return typeof parsed.version === "string" && parsed.version.length > 0
|
|
54
|
-
? parsed.version
|
|
55
|
-
: null;
|
|
56
|
-
} catch {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Collect all three component versions, each independently best-effort. */
|
|
62
|
-
export function collectVersions(): VersionInfo {
|
|
63
|
-
return {
|
|
64
|
-
cli: CLI_VERSION,
|
|
65
|
-
core: readCoreVersion(),
|
|
66
|
-
sync: readSyncVersion(),
|
|
67
|
-
};
|
|
68
|
-
}
|
package/src/utils/git.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import simpleGit, { SimpleGit } from 'simple-git';
|
|
4
|
-
|
|
5
|
-
export async function cloneRepo(repoUrl: string, targetDir: string, branch?: string): Promise<void> {
|
|
6
|
-
const git = simpleGit();
|
|
7
|
-
const options = branch ? ['--branch', branch] : [];
|
|
8
|
-
await git.clone(repoUrl, targetDir, options);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function fetchRepo(repoDir: string): Promise<void> {
|
|
12
|
-
const git = simpleGit(repoDir);
|
|
13
|
-
await git.fetch(['--all']);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function pullRepo(repoDir: string): Promise<void> {
|
|
17
|
-
const git = simpleGit(repoDir);
|
|
18
|
-
await git.pull();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function getCurrentCommit(repoDir: string): Promise<string> {
|
|
22
|
-
const git = simpleGit(repoDir);
|
|
23
|
-
const log = await git.log({ maxCount: 1 });
|
|
24
|
-
return log.latest?.hash ?? '';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function checkoutCommit(repoDir: string, commitSha: string): Promise<void> {
|
|
28
|
-
const git = simpleGit(repoDir);
|
|
29
|
-
await git.checkout(commitSha);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function isRepo(dir: string): Promise<boolean> {
|
|
33
|
-
if (!fs.existsSync(dir)) return false;
|
|
34
|
-
try {
|
|
35
|
-
const git = simpleGit(dir);
|
|
36
|
-
return await git.checkIsRepo();
|
|
37
|
-
} catch {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function getRemoteUrl(repoDir: string): Promise<string | null> {
|
|
43
|
-
try {
|
|
44
|
-
const git = simpleGit(repoDir);
|
|
45
|
-
const remotes = await git.getRemotes(true);
|
|
46
|
-
const origin = remotes.find(r => r.name === 'origin');
|
|
47
|
-
return origin?.refs?.fetch ?? null;
|
|
48
|
-
} catch {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export async function isBehindRemote(repoDir: string): Promise<{ behind: boolean; commits: number }> {
|
|
54
|
-
try {
|
|
55
|
-
const git = simpleGit(repoDir);
|
|
56
|
-
await git.fetch();
|
|
57
|
-
const status = await git.status();
|
|
58
|
-
return { behind: status.behind > 0, commits: status.behind };
|
|
59
|
-
} catch {
|
|
60
|
-
return { behind: false, commits: 0 };
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function ensureGitignore(hqRoot: string, entry: string): void {
|
|
65
|
-
const gitignorePath = path.join(hqRoot, '.gitignore');
|
|
66
|
-
let content = '';
|
|
67
|
-
if (fs.existsSync(gitignorePath)) {
|
|
68
|
-
content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
69
|
-
}
|
|
70
|
-
if (!content.includes(entry)) {
|
|
71
|
-
content = content.trimEnd() + '\n' + entry + '\n';
|
|
72
|
-
fs.writeFileSync(gitignorePath, content);
|
|
73
|
-
}
|
|
74
|
-
}
|
package/src/utils/integrity.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integrity verification — SHA256 hash and RSA signature checks (US-005)
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import * as crypto from 'crypto';
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import { resolveDefaultHqRoot } from './cognito-session.js';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Verify a file's SHA256 hash matches the expected value.
|
|
12
|
-
*/
|
|
13
|
-
export async function computeSha256(filePath: string): Promise<string> {
|
|
14
|
-
return new Promise((resolve, reject) => {
|
|
15
|
-
const hash = crypto.createHash('sha256');
|
|
16
|
-
const stream = fs.createReadStream(filePath);
|
|
17
|
-
|
|
18
|
-
stream.on('data', (chunk) => hash.update(chunk));
|
|
19
|
-
stream.on('end', () => resolve(hash.digest('hex')));
|
|
20
|
-
stream.on('error', reject);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Verify a file's SHA256 hash matches the expected value.
|
|
26
|
-
*/
|
|
27
|
-
export async function verifySha256(
|
|
28
|
-
filePath: string,
|
|
29
|
-
expectedHash: string
|
|
30
|
-
): Promise<boolean> {
|
|
31
|
-
const computed = await computeSha256(filePath);
|
|
32
|
-
return computed === expectedHash.toLowerCase();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Verify an RSA signature of a SHA256 hash using the registry public key.
|
|
37
|
-
* The public key is expected at packages/.keys/registry-public.pem.
|
|
38
|
-
* Returns false (does not throw) if the key file is missing.
|
|
39
|
-
*/
|
|
40
|
-
export function verifyRsaSignature(
|
|
41
|
-
sha256Hash: string,
|
|
42
|
-
signature: string,
|
|
43
|
-
publicKeyPath?: string
|
|
44
|
-
): boolean {
|
|
45
|
-
const keyPath =
|
|
46
|
-
publicKeyPath ??
|
|
47
|
-
path.resolve(resolveDefaultHqRoot({ onMissing: 'throw' }), 'packages', '.keys', 'registry-public.pem');
|
|
48
|
-
|
|
49
|
-
if (!fs.existsSync(keyPath)) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const publicKey = fs.readFileSync(keyPath, 'utf-8');
|
|
54
|
-
const verifier = crypto.createVerify('SHA256');
|
|
55
|
-
verifier.update(sha256Hash);
|
|
56
|
-
verifier.end();
|
|
57
|
-
|
|
58
|
-
return verifier.verify(publicKey, Buffer.from(signature, 'base64'));
|
|
59
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { isInterceptedProcessExit } from "./intercepted-process-exit.js";
|
|
3
|
-
|
|
4
|
-
describe("isInterceptedProcessExit", () => {
|
|
5
|
-
it("matches the fuzz harness's intercepted-process.exit marker error", () => {
|
|
6
|
-
expect(isInterceptedProcessExit(new Error("fuzz-intercepted-process-exit"))).toBe(true);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it("matches when the marker is wrapped in a longer message", () => {
|
|
10
|
-
expect(
|
|
11
|
-
isInterceptedProcessExit(new Error("audit: fuzz-intercepted-process-exit code=1")),
|
|
12
|
-
).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("matches a plain object carrying the marker message", () => {
|
|
16
|
-
expect(isInterceptedProcessExit({ message: "fuzz-intercepted-process-exit" })).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("matches a bare string carrying the marker", () => {
|
|
20
|
-
expect(isInterceptedProcessExit("fuzz-intercepted-process-exit")).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("does NOT match genuine errors (so real faults still report)", () => {
|
|
24
|
-
expect(isInterceptedProcessExit(new Error("boom"))).toBe(false);
|
|
25
|
-
expect(isInterceptedProcessExit(new Error("ENOENT: no such file or directory"))).toBe(false);
|
|
26
|
-
expect(isInterceptedProcessExit(Object.assign(new Error("nope"), { code: "EACCES" }))).toBe(
|
|
27
|
-
false,
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("does NOT match non-error values", () => {
|
|
32
|
-
expect(isInterceptedProcessExit(null)).toBe(false);
|
|
33
|
-
expect(isInterceptedProcessExit(undefined)).toBe(false);
|
|
34
|
-
expect(isInterceptedProcessExit(42)).toBe(false);
|
|
35
|
-
expect(isInterceptedProcessExit({})).toBe(false);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// src/utils/intercepted-process-exit.ts
|
|
2
|
-
//
|
|
3
|
-
// Classify the "intercepted process.exit" synthetic error (HQ-CLI-3).
|
|
4
|
-
//
|
|
5
|
-
// Security/audit FUZZ harnesses (e.g. `/opt/audit/fuzz-exports.js`) monkey-patch
|
|
6
|
-
// `process.exit` to THROW a marker instead of actually terminating, so the
|
|
7
|
-
// fuzzer can keep exercising a binary's entrypoints. When such a harness drives
|
|
8
|
-
// `hq` (e.g. `hq <unknown-command>`), commander legitimately calls
|
|
9
|
-
// `process.exit(1)` — and the patched exit turns that normal CLI control-flow
|
|
10
|
-
// into a thrown `Error: fuzz-intercepted-process-exit` that propagates out of
|
|
11
|
-
// `program.parseAsync()` into the CLI's top-level catch, which then shipped it to
|
|
12
|
-
// Sentry as a fatal.
|
|
13
|
-
//
|
|
14
|
-
// This is a test-harness artifact, NOT an hq-cli defect: a real user's
|
|
15
|
-
// `process.exit` exits the process and nothing is ever thrown or captured.
|
|
16
|
-
// There is no user-facing degradation to fix, so the top-level catch skips
|
|
17
|
-
// Sentry capture for it — the genuine "expected, non-actionable" carve-out,
|
|
18
|
-
// mirroring the EPIPE (HQ-6B) and environmental-FS (HQ-CLI-2) carve-outs.
|
|
19
|
-
|
|
20
|
-
/** The marker a fuzz/audit harness throws in place of a real `process.exit`. */
|
|
21
|
-
const INTERCEPTED_PROCESS_EXIT_MARKER = "fuzz-intercepted-process-exit";
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* True when `err` is a fuzz/audit harness's intercepted-`process.exit` marker
|
|
25
|
-
* rather than a genuine fault. Callers should SKIP Sentry capture (no defect, no
|
|
26
|
-
* user-facing degradation) while preserving the intended exit code.
|
|
27
|
-
*/
|
|
28
|
-
export function isInterceptedProcessExit(err: unknown): boolean {
|
|
29
|
-
if (typeof err === "object" && err !== null) {
|
|
30
|
-
const message = (err as { message?: unknown }).message;
|
|
31
|
-
if (typeof message === "string" && message.includes(INTERCEPTED_PROCESS_EXIT_MARKER)) {
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return typeof err === "string" && err.includes(INTERCEPTED_PROCESS_EXIT_MARKER);
|
|
36
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
vi.mock("./cognito-session.js", () => ({
|
|
4
|
-
DEFAULT_COGNITO: {
|
|
5
|
-
region: "us-east-1",
|
|
6
|
-
userPoolDomain: "vault-indigo-hq-prod",
|
|
7
|
-
clientId: "client-123",
|
|
8
|
-
port: 8765,
|
|
9
|
-
identityProvider: "Google",
|
|
10
|
-
},
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
cognitoConfigForLoginProvider,
|
|
15
|
-
parseLoginProvider,
|
|
16
|
-
} from "./login-provider.js";
|
|
17
|
-
|
|
18
|
-
describe("login provider helpers", () => {
|
|
19
|
-
it("preserves the default Cognito config when no provider is supplied", () => {
|
|
20
|
-
expect(cognitoConfigForLoginProvider(undefined).identityProvider).toBe("Google");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("maps Microsoft to the Cognito MicrosoftPersonal IdP", () => {
|
|
24
|
-
expect(cognitoConfigForLoginProvider("microsoft").identityProvider).toBe(
|
|
25
|
-
"MicrosoftPersonal",
|
|
26
|
-
);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("maps picker to no forced IdP", () => {
|
|
30
|
-
expect(cognitoConfigForLoginProvider("picker").identityProvider).toBeUndefined();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("accepts provider values case-insensitively", () => {
|
|
34
|
-
expect(parseLoginProvider("Microsoft")).toBe("microsoft");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("rejects unknown providers", () => {
|
|
38
|
-
expect(() => parseLoginProvider("github")).toThrow(
|
|
39
|
-
"Provider must be one of: google, microsoft, picker",
|
|
40
|
-
);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { CognitoAuthConfig } from "@indigoai-us/hq-cloud";
|
|
2
|
-
import { DEFAULT_COGNITO } from "./cognito-session.js";
|
|
3
|
-
|
|
4
|
-
const PROVIDER_TO_COGNITO_IDP = {
|
|
5
|
-
google: "Google",
|
|
6
|
-
microsoft: "MicrosoftPersonal",
|
|
7
|
-
picker: undefined,
|
|
8
|
-
} as const;
|
|
9
|
-
|
|
10
|
-
export type LoginProvider = keyof typeof PROVIDER_TO_COGNITO_IDP;
|
|
11
|
-
|
|
12
|
-
export function parseLoginProvider(value: string | undefined): LoginProvider | undefined {
|
|
13
|
-
if (value === undefined) return undefined;
|
|
14
|
-
const normalized = value.trim().toLowerCase();
|
|
15
|
-
if (normalized in PROVIDER_TO_COGNITO_IDP) {
|
|
16
|
-
return normalized as LoginProvider;
|
|
17
|
-
}
|
|
18
|
-
throw new Error("Provider must be one of: google, microsoft, picker");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function cognitoConfigForLoginProvider(
|
|
22
|
-
value: string | undefined,
|
|
23
|
-
): CognitoAuthConfig {
|
|
24
|
-
const provider = parseLoginProvider(value);
|
|
25
|
-
if (!provider) return DEFAULT_COGNITO;
|
|
26
|
-
return {
|
|
27
|
-
...DEFAULT_COGNITO,
|
|
28
|
-
identityProvider: PROVIDER_TO_COGNITO_IDP[provider],
|
|
29
|
-
};
|
|
30
|
-
}
|