@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
package/src/types.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HQ Module Manifest Types (US-001)
|
|
3
|
-
*
|
|
4
|
-
* `modules.yaml` hosts two kinds of entries, discriminated by `strategy`:
|
|
5
|
-
* - link | merge | copy → git-repo-based module sync (existing flow)
|
|
6
|
-
* - package → hq-pack content pack installed via `hq install`
|
|
7
|
-
* (new in hq-core v12.0.0; see
|
|
8
|
-
* knowledge/public/hq-core/package-yaml-spec.md)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { ContributionKey } from './utils/contribution-table.js';
|
|
12
|
-
|
|
13
|
-
export type LegacyStrategy = 'link' | 'merge' | 'copy';
|
|
14
|
-
export type SyncStrategy = LegacyStrategy | 'package';
|
|
15
|
-
export type AccessLevel = 'public' | 'team' | `role:${string}`;
|
|
16
|
-
|
|
17
|
-
export interface PathMapping {
|
|
18
|
-
src: string; // Path within module repo
|
|
19
|
-
dest: string; // Path in HQ tree (relative to HQ root)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface LegacyModuleDefinition {
|
|
23
|
-
name: string;
|
|
24
|
-
repo: string; // Git URL (https or git@)
|
|
25
|
-
branch?: string; // Default: main
|
|
26
|
-
strategy: LegacyStrategy;
|
|
27
|
-
paths: PathMapping[]; // What to sync and where
|
|
28
|
-
access?: AccessLevel;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface PackModuleDefinition {
|
|
32
|
-
name: string; // e.g. 'hq-pack-gstack'
|
|
33
|
-
strategy: 'package';
|
|
34
|
-
source: string; // '@scope/name@ver' | git URL#sha | local path
|
|
35
|
-
version?: string; // semver from the pack's package.yaml
|
|
36
|
-
installed_at?: string; // e.g. 'packages/hq-pack-gstack'
|
|
37
|
-
installed_at_iso?: string; // ISO timestamp
|
|
38
|
-
resolved_sha?: string; // git transport only
|
|
39
|
-
access?: AccessLevel;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export type ModuleDefinition = LegacyModuleDefinition | PackModuleDefinition;
|
|
43
|
-
|
|
44
|
-
export interface ModulesManifest {
|
|
45
|
-
version: '1';
|
|
46
|
-
modules: ModuleDefinition[];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface ModuleLock {
|
|
50
|
-
version: '1';
|
|
51
|
-
locked: Record<string, string>; // module name -> commit SHA
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface SyncState {
|
|
55
|
-
version: '1';
|
|
56
|
-
files: Record<string, {
|
|
57
|
-
hash: string; // SHA256 of file content at sync time
|
|
58
|
-
syncedAt: string; // ISO timestamp
|
|
59
|
-
fromModule: string; // Module that provided this file
|
|
60
|
-
}>;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export interface SyncResult {
|
|
64
|
-
module: string;
|
|
65
|
-
success: boolean;
|
|
66
|
-
action: 'cloned' | 'fetched' | 'synced' | 'skipped';
|
|
67
|
-
message?: string;
|
|
68
|
-
filesChanged?: number;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ---------------------------------------------------------------------------
|
|
72
|
-
// hq-pack content-pack manifest (packages/{name}/package.yaml).
|
|
73
|
-
//
|
|
74
|
-
// Distinct from packages/hq-cli/src/schemas/hq-package.schema.json, which
|
|
75
|
-
// covers the entitlement-gated registry flow (`hq packages install <slug>`).
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* The `contributes.*` keys a pack may declare. DERIVED (US-003) from the single
|
|
80
|
-
* declarative contribution registry in `utils/contribution-table.ts` -- adding
|
|
81
|
-
* a contribution type is one row there, and this union updates automatically.
|
|
82
|
-
* Do NOT restate the keys here.
|
|
83
|
-
*/
|
|
84
|
-
export type PackContributeKey = ContributionKey;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Pack authorship attribution (US-001). OPTIONAL and backwards-compatible —
|
|
88
|
-
* packs published before this field still validate. When present, install can
|
|
89
|
-
* attribute the pack to a creator: `uid` is the HQ person UID, `handle` the
|
|
90
|
-
* creator's marketplace handle, `displayName` the human-readable name.
|
|
91
|
-
*/
|
|
92
|
-
export interface PackAuthor {
|
|
93
|
-
uid: string;
|
|
94
|
-
handle: string;
|
|
95
|
-
displayName: string;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* A pack-to-pack dependency (M0). OPTIONAL and backwards-compatible — packs
|
|
100
|
-
* published before this field omit `requires.packs` and install unchanged. When
|
|
101
|
-
* present, each entry names another content pack that MUST already be installed
|
|
102
|
-
* before this one (enforced at install time by `assertPackDependencies`, which
|
|
103
|
-
* tracks installed packs by FILESYSTEM PRESENCE — not `modules.yaml`). `version`
|
|
104
|
-
* is an optional semver RANGE the installed dependency must satisfy.
|
|
105
|
-
*/
|
|
106
|
-
export interface PackDependency {
|
|
107
|
-
name: string; // ^hq-pack-[a-z0-9][a-z0-9-]*$
|
|
108
|
-
version?: string; // optional semver range
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export interface PackManifest {
|
|
112
|
-
name: string; // ^hq-pack-[a-z0-9][a-z0-9-]*$
|
|
113
|
-
version: string; // semver
|
|
114
|
-
publisher: string; // @scope
|
|
115
|
-
access: 'public' | 'private';
|
|
116
|
-
/**
|
|
117
|
-
* Host + pack prerequisites. `hqCore` is a required semver RANGE the host HQ
|
|
118
|
-
* must satisfy. `packs` (M0) is an OPTIONAL list of other content packs that
|
|
119
|
-
* must be installed first — see PackDependency.
|
|
120
|
-
*/
|
|
121
|
-
requires: { hqCore: string; packs?: PackDependency[] };
|
|
122
|
-
contributes: Partial<Record<PackContributeKey, string[]>>;
|
|
123
|
-
description?: string;
|
|
124
|
-
license?: string;
|
|
125
|
-
repository?: string;
|
|
126
|
-
keywords?: string[];
|
|
127
|
-
conditional?: string; // bash predicate; skip install if exits non-zero
|
|
128
|
-
/**
|
|
129
|
-
* Pack authorship attribution (US-001). Optional — absent on legacy packs.
|
|
130
|
-
*/
|
|
131
|
-
author?: PackAuthor;
|
|
132
|
-
/**
|
|
133
|
-
* Declared capabilities the pack touches (US-001), e.g. hooks/scripts/
|
|
134
|
-
* network/fs/secrets. Optional, free-form string entries — surfaced to the
|
|
135
|
-
* user at install time for an at-a-glance trust signal. Reserved here; not
|
|
136
|
-
* yet enforced.
|
|
137
|
-
*/
|
|
138
|
-
capabilities?: string[];
|
|
139
|
-
/**
|
|
140
|
-
* Post-install initialization (US-004/US-005). Optional — absent on legacy
|
|
141
|
-
* packs. `entrypoint` names a declared `contributes.skills`/`commands` entry
|
|
142
|
-
* (slash-normalized); `prompt` is optional free-text prose (PHASE 1 does NOT
|
|
143
|
-
* render the prose — only an auto-generated get-started line from entrypoint).
|
|
144
|
-
*/
|
|
145
|
-
initialization?: { entrypoint: string; prompt?: string };
|
|
146
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# >>> BEGIN GENERATED contribution table (US-003) — do not edit by hand
|
|
2
|
-
# Generated from hq-cli/src/utils/contribution-table.ts by
|
|
3
|
-
# hq-cli/scripts/generate-scan-packages-table.mjs. Regenerate with:
|
|
4
|
-
# node scripts/generate-scan-packages-table.mjs --write core/scripts/scan-packages.sh
|
|
5
|
-
# Each row is "payload|host|wire" (payload uses {item} for the name).
|
|
6
|
-
# bash 3.2-compatible (macOS default bash has no associative arrays):
|
|
7
|
-
# a plain indexed key list + a generated case-lookup function.
|
|
8
|
-
CONTRIB_KEYS=(workers knowledge skills commands hooks policies scripts mcp)
|
|
9
|
-
# contrib_row <key> -> echoes "payload|host|wire", empty if unknown.
|
|
10
|
-
contrib_row() {
|
|
11
|
-
case "$1" in
|
|
12
|
-
workers) printf '%s' 'workers/{item}|core/workers/public|symlink' ;;
|
|
13
|
-
knowledge) printf '%s' 'knowledge/{item}|core/knowledge/public|symlink' ;;
|
|
14
|
-
skills) printf '%s' 'skills/{item}|.claude/skills|symlink' ;;
|
|
15
|
-
commands) printf '%s' 'commands/{item}.md|.claude/commands|symlink' ;;
|
|
16
|
-
hooks) printf '%s' 'hooks/{item}.sh|.claude/hooks|symlink' ;;
|
|
17
|
-
policies) printf '%s' 'policies/{item}.md|core/policies|symlink' ;;
|
|
18
|
-
scripts) printf '%s' 'scripts/{item}|core/scripts|symlink' ;;
|
|
19
|
-
mcp) printf '%s' 'mcp/{item}.json|merge:claude+codex|merge' ;;
|
|
20
|
-
*) return 0 ;;
|
|
21
|
-
esac
|
|
22
|
-
}
|
|
23
|
-
# <<< END GENERATED contribution table
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { AuthError, isAuthError } from "./auth-error.js";
|
|
3
|
-
|
|
4
|
-
describe("isAuthError", () => {
|
|
5
|
-
// HQ-CLI-8: an expired HQ session surfaced as a vault 401 during company
|
|
6
|
-
// slug resolution. The user fixes it with `hq login`; it should skip Sentry.
|
|
7
|
-
it("classifies an AuthError as an expected auth state (skip Sentry)", () => {
|
|
8
|
-
expect(isAuthError(new AuthError())).toBe(true);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it("uses an actionable default message", () => {
|
|
12
|
-
expect(new AuthError().message).toMatch(/hq login/);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("preserves a custom user-facing message verbatim", () => {
|
|
16
|
-
const msg = "Sign in again before continuing.";
|
|
17
|
-
expect(new AuthError(msg).message).toBe(msg);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("keeps instanceof across the transpile target", () => {
|
|
21
|
-
const err = new AuthError();
|
|
22
|
-
expect(err).toBeInstanceOf(AuthError);
|
|
23
|
-
expect(err).toBeInstanceOf(Error);
|
|
24
|
-
expect(err.name).toBe("AuthError");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// A genuine defect must still reach Sentry — only the typed auth class is
|
|
28
|
-
// diverted, so real bugs are never silently swallowed.
|
|
29
|
-
it("does NOT match a plain Error (so real faults still report)", () => {
|
|
30
|
-
expect(isAuthError(new Error("Unauthorized"))).toBe(false);
|
|
31
|
-
expect(isAuthError(new Error("Your HQ session has expired. Run `hq login`."))).toBe(false);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("does NOT match non-error values", () => {
|
|
35
|
-
expect(isAuthError(null)).toBe(false);
|
|
36
|
-
expect(isAuthError(undefined)).toBe(false);
|
|
37
|
-
expect(isAuthError("Unauthorized")).toBe(false);
|
|
38
|
-
expect(isAuthError({ message: "Unauthorized" })).toBe(false);
|
|
39
|
-
});
|
|
40
|
-
});
|
package/src/utils/auth-error.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// src/utils/auth-error.ts
|
|
2
|
-
//
|
|
3
|
-
// Classify expired or missing HQ session conditions surfaced by the vault API
|
|
4
|
-
// (HQ-CLI-8). These are expected, user-actionable auth states — NOT hq-cli
|
|
5
|
-
// defects — so the top-level catch prints the message and exits non-zero but
|
|
6
|
-
// SKIPS Sentry capture, mirroring the company-selection (HQ-CLI-7),
|
|
7
|
-
// expected-user-error (HQ-CLI-6), and environmental-FS (HQ-CLI-2) carve-outs.
|
|
8
|
-
//
|
|
9
|
-
// HQ-CLI-8: a user ran `hq integrations list --company liverecover --json`
|
|
10
|
-
// with an expired HQ session. Company-slug resolution tried the caller-scoped
|
|
11
|
-
// `/entity/check-slug/me` lookup and the global `/entity/by-slug/company/...`
|
|
12
|
-
// fallback; both returned 401 Unauthorized. The plain Error that bubbled up
|
|
13
|
-
// looked like a company-resolution defect and was shipped to Sentry as a fatal.
|
|
14
|
-
// A 401 from vault resolution means the caller needs to run `hq login`; the
|
|
15
|
-
// code cannot repair an expired token, so this is normal auth state, not a
|
|
16
|
-
// crash to triage.
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Thrown when the vault API reports the caller's HQ session is expired or
|
|
20
|
-
* missing. The `message` is user-facing and actionable; the top-level handler
|
|
21
|
-
* prints it verbatim and skips Sentry capture.
|
|
22
|
-
*/
|
|
23
|
-
export class AuthError extends Error {
|
|
24
|
-
constructor(
|
|
25
|
-
message = "Your HQ session has expired or you're not signed in. Run `hq login` and try again.",
|
|
26
|
-
) {
|
|
27
|
-
super(message);
|
|
28
|
-
this.name = "AuthError";
|
|
29
|
-
// Preserve `instanceof` across the TS→ES5/ES2015 transpile target.
|
|
30
|
-
Object.setPrototypeOf(this, AuthError.prototype);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* True when `err` is an expected auth-state failure the user must resolve with
|
|
36
|
-
* `hq login`. Callers should print `err.message` and SKIP Sentry capture while
|
|
37
|
-
* preserving a non-zero exit. Genuine faults are plain `Error`s and return
|
|
38
|
-
* `false`, so real bugs still report.
|
|
39
|
-
*/
|
|
40
|
-
export function isAuthError(err: unknown): boolean {
|
|
41
|
-
return err instanceof AuthError;
|
|
42
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the billing-gate helpers (pure parsers + price formatting).
|
|
3
|
-
* The command-level flows are covered in commands/{agents,outposts,billing}.test.ts;
|
|
4
|
-
* this pins the trust-boundary parsing and the cost string.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, expect, it } from "vitest";
|
|
8
|
-
import {
|
|
9
|
-
AGENT_PRICE_CENTS,
|
|
10
|
-
OUTPOST_PRICE_CENTS,
|
|
11
|
-
formatUsd,
|
|
12
|
-
isBillingSetupAction,
|
|
13
|
-
parseBillingPayload,
|
|
14
|
-
} from "./billing-gate.js";
|
|
15
|
-
|
|
16
|
-
describe("prices", () => {
|
|
17
|
-
it("match the console's authoritative constants", () => {
|
|
18
|
-
expect(AGENT_PRICE_CENTS).toBe(10_000);
|
|
19
|
-
expect(OUTPOST_PRICE_CENTS).toBe(8_000);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe("formatUsd", () => {
|
|
24
|
-
it("renders cents as $X.YZ", () => {
|
|
25
|
-
expect(formatUsd(10_000)).toBe("$100.00");
|
|
26
|
-
expect(formatUsd(8_000)).toBe("$80.00");
|
|
27
|
-
expect(formatUsd(0)).toBe("$0.00");
|
|
28
|
-
});
|
|
29
|
-
it("clamps negatives to zero", () => {
|
|
30
|
-
expect(formatUsd(-500)).toBe("$0.00");
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe("isBillingSetupAction", () => {
|
|
35
|
-
it("accepts a valid org setup", () => {
|
|
36
|
-
expect(
|
|
37
|
-
isBillingSetupAction({
|
|
38
|
-
payerType: "company",
|
|
39
|
-
path: "/v1/billing/checkout/org",
|
|
40
|
-
method: "POST",
|
|
41
|
-
body: { companyUid: "cmp_x" },
|
|
42
|
-
}),
|
|
43
|
-
).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
it("accepts a valid person setup with no body", () => {
|
|
46
|
-
expect(
|
|
47
|
-
isBillingSetupAction({
|
|
48
|
-
payerType: "person",
|
|
49
|
-
path: "/v1/billing/checkout/person",
|
|
50
|
-
method: "POST",
|
|
51
|
-
}),
|
|
52
|
-
).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
it("rejects unknown paths / shapes", () => {
|
|
55
|
-
expect(
|
|
56
|
-
isBillingSetupAction({
|
|
57
|
-
payerType: "company",
|
|
58
|
-
path: "/v1/billing/checkout/evil",
|
|
59
|
-
method: "POST",
|
|
60
|
-
}),
|
|
61
|
-
).toBe(false);
|
|
62
|
-
expect(isBillingSetupAction(null)).toBe(false);
|
|
63
|
-
expect(isBillingSetupAction({ payerType: "company" })).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe("parseBillingPayload", () => {
|
|
68
|
-
it("extracts status + setup from a 402 body", () => {
|
|
69
|
-
const parsed = parseBillingPayload({
|
|
70
|
-
error: "payment required",
|
|
71
|
-
billing: {
|
|
72
|
-
status: "billing_required",
|
|
73
|
-
setup: {
|
|
74
|
-
payerType: "company",
|
|
75
|
-
path: "/v1/billing/checkout/org",
|
|
76
|
-
method: "POST",
|
|
77
|
-
body: { companyUid: "cmp_x" },
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
expect(parsed?.status).toBe("billing_required");
|
|
82
|
-
expect(parsed?.setup?.path).toBe("/v1/billing/checkout/org");
|
|
83
|
-
});
|
|
84
|
-
it("returns status with no setup when the setup is malformed", () => {
|
|
85
|
-
const parsed = parseBillingPayload({
|
|
86
|
-
billing: { status: "billing_required", setup: { bogus: true } },
|
|
87
|
-
});
|
|
88
|
-
expect(parsed?.status).toBe("billing_required");
|
|
89
|
-
expect(parsed?.setup).toBeUndefined();
|
|
90
|
-
});
|
|
91
|
-
it("returns undefined for a non-billing error body", () => {
|
|
92
|
-
expect(parseBillingPayload({ error: "forbidden" })).toBeUndefined();
|
|
93
|
-
expect(parseBillingPayload(null)).toBeUndefined();
|
|
94
|
-
});
|
|
95
|
-
});
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Billing gate for paid provisioning (agents & Outposts).
|
|
3
|
-
*
|
|
4
|
-
* hq-pro enforces billing server-side: a provision call for a resource whose
|
|
5
|
-
* payer has no card on file returns `402` with a `billing` payload carrying a
|
|
6
|
-
* `setup` action that names EXACTLY which checkout endpoint to hit. That
|
|
7
|
-
* endpoint returns `{ url }` — a Stripe-hosted card-capture link.
|
|
8
|
-
*
|
|
9
|
-
* This module wires the client-side experience around that contract:
|
|
10
|
-
* 1. `confirmChargeOrExit` — print the monthly cost and require `--yes`
|
|
11
|
-
* BEFORE any paid provisioning call is made.
|
|
12
|
-
* 2. `surfaceBillingRequired` — when the backend answers `402 billing_required`,
|
|
13
|
-
* mint the card-capture link and print a plain, shareable message instead
|
|
14
|
-
* of an opaque failure.
|
|
15
|
-
* 3. `hq billing checkout` (see commands/billing.ts) reuses `mintPaymentLink`
|
|
16
|
-
* to hand out the same link proactively.
|
|
17
|
-
*
|
|
18
|
-
* Prices mirror the console's authoritative constants
|
|
19
|
-
* (hq-console `src/lib/billing-pricing.ts`): amounts are in the smallest
|
|
20
|
-
* currency unit (US cents) to match Stripe's wire shape.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import chalk from "chalk";
|
|
24
|
-
import { vaultApiFetch } from "./vault-api.js";
|
|
25
|
-
|
|
26
|
-
/** Per-Agent monthly list price, in cents ($100.00 / agent / month). */
|
|
27
|
-
export const AGENT_PRICE_CENTS = 10_000;
|
|
28
|
-
/** Per-Outpost monthly list price, in cents ($80.00 / Outpost / month). */
|
|
29
|
-
export const OUTPOST_PRICE_CENTS = 8_000;
|
|
30
|
-
|
|
31
|
-
/** Render a cents amount as a `$X.YZ` USD string. */
|
|
32
|
-
export function formatUsd(cents: number): string {
|
|
33
|
-
return `$${(Math.max(0, Math.round(cents)) / 100).toFixed(2)}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The server-supplied "how to add a card" action on a `402 billing_required`.
|
|
38
|
-
* Mirrors hq-pro `src/billing/activation-billing.ts` `BillingSetupAction` and
|
|
39
|
-
* the console's `src/lib/billing-setup.ts`.
|
|
40
|
-
*/
|
|
41
|
-
export interface BillingSetupAction {
|
|
42
|
-
payerType: "company" | "person";
|
|
43
|
-
path: "/v1/billing/checkout/org" | "/v1/billing/checkout/person";
|
|
44
|
-
method: "POST";
|
|
45
|
-
body?: { companyUid?: string };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** The `billing` envelope hq-pro attaches to a billing-blocked provision. */
|
|
49
|
-
export interface BillingErrorPayload {
|
|
50
|
-
status: string;
|
|
51
|
-
setup?: BillingSetupAction;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Narrow an unknown value to a usable `BillingSetupAction`. */
|
|
55
|
-
export function isBillingSetupAction(value: unknown): value is BillingSetupAction {
|
|
56
|
-
if (!value || typeof value !== "object") return false;
|
|
57
|
-
const setup = value as Partial<BillingSetupAction>;
|
|
58
|
-
const body = setup.body;
|
|
59
|
-
return (
|
|
60
|
-
(setup.payerType === "company" || setup.payerType === "person") &&
|
|
61
|
-
(setup.path === "/v1/billing/checkout/org" ||
|
|
62
|
-
setup.path === "/v1/billing/checkout/person") &&
|
|
63
|
-
setup.method === "POST" &&
|
|
64
|
-
(body === undefined || (typeof body === "object" && body !== null))
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Pull a `BillingErrorPayload` out of a decoded hq-pro error body, if present. */
|
|
69
|
-
export function parseBillingPayload(body: unknown): BillingErrorPayload | undefined {
|
|
70
|
-
if (!body || typeof body !== "object") return undefined;
|
|
71
|
-
const raw = (body as { billing?: unknown }).billing;
|
|
72
|
-
if (!raw || typeof raw !== "object") return undefined;
|
|
73
|
-
const status = (raw as { status?: unknown }).status;
|
|
74
|
-
if (typeof status !== "string") return undefined;
|
|
75
|
-
const setup = (raw as { setup?: unknown }).setup;
|
|
76
|
-
return {
|
|
77
|
-
status,
|
|
78
|
-
...(isBillingSetupAction(setup) ? { setup } : {}),
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export type PaidResource = "agent" | "Outpost";
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Gate a paid provisioning call behind an explicit, informed `--yes`. Prints the
|
|
86
|
-
* monthly charge in full prose (this is a paid, outward action — Auto-Clarity)
|
|
87
|
-
* and exits non-zero when the caller has not passed `--yes`. Returns normally
|
|
88
|
-
* once approved so the caller proceeds to provision.
|
|
89
|
-
*/
|
|
90
|
-
export function confirmChargeOrExit(opts: {
|
|
91
|
-
resource: PaidResource;
|
|
92
|
-
unitCents: number;
|
|
93
|
-
quantity?: number;
|
|
94
|
-
yes?: boolean;
|
|
95
|
-
}): void {
|
|
96
|
-
const quantity = opts.quantity ?? 1;
|
|
97
|
-
const total = opts.unitCents * quantity;
|
|
98
|
-
const each =
|
|
99
|
-
quantity > 1 ? ` (${formatUsd(opts.unitCents)} each × ${quantity})` : "";
|
|
100
|
-
const noun = quantity > 1 ? `${opts.resource}s` : opts.resource;
|
|
101
|
-
const article = /^[aeiou]/i.test(opts.resource) ? "an" : "a";
|
|
102
|
-
const subject = quantity > 1 ? `${quantity} ${noun}` : `${article} ${noun}`;
|
|
103
|
-
if (!opts.yes) {
|
|
104
|
-
console.error(
|
|
105
|
-
chalk.yellow(
|
|
106
|
-
`Provisioning ${subject} adds a recurring charge of ` +
|
|
107
|
-
`${formatUsd(total)}/month${each} to the payer on file. This is a ` +
|
|
108
|
-
`paid resource.\n` +
|
|
109
|
-
`Re-run with --yes to confirm you want to incur this charge.`,
|
|
110
|
-
),
|
|
111
|
-
);
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
|
-
console.log(
|
|
115
|
-
chalk.dim(
|
|
116
|
-
`Confirmed: ${formatUsd(total)}/month for ${quantity} ${noun}. Provisioning…`,
|
|
117
|
-
),
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Mint a Stripe-hosted card-capture link for a `BillingSetupAction`. Returns the
|
|
123
|
-
* hosted URL the payer opens to add a card. Throws on a non-2xx (the caller
|
|
124
|
-
* decides how to surface it) — never swallows.
|
|
125
|
-
*/
|
|
126
|
-
export async function mintPaymentLink(
|
|
127
|
-
token: string,
|
|
128
|
-
setup: BillingSetupAction,
|
|
129
|
-
): Promise<string> {
|
|
130
|
-
const res = await vaultApiFetch({
|
|
131
|
-
token,
|
|
132
|
-
path: setup.path,
|
|
133
|
-
method: "POST",
|
|
134
|
-
body: setup.body?.companyUid ? { companyUid: setup.body.companyUid } : {},
|
|
135
|
-
});
|
|
136
|
-
if (!res.ok) {
|
|
137
|
-
const body = (await res.json().catch(() => ({}))) as {
|
|
138
|
-
error?: string;
|
|
139
|
-
message?: string;
|
|
140
|
-
};
|
|
141
|
-
throw new Error(
|
|
142
|
-
`Failed to mint a payment link: ${body.error ?? body.message ?? res.statusText}`,
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
const data = (await res.json()) as { url?: string };
|
|
146
|
-
if (!data.url) {
|
|
147
|
-
throw new Error("Payment link response did not include a checkout URL.");
|
|
148
|
-
}
|
|
149
|
-
return data.url;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* The provision was blocked because the payer has no card on file. Mint the
|
|
154
|
-
* card-capture link the backend pointed us at and print a plain, shareable
|
|
155
|
-
* message so the user can add a card (or hand the link to whoever owns billing)
|
|
156
|
-
* and re-run. Returns the URL, or `null` when the server gave no `setup` action
|
|
157
|
-
* to act on. Never prints tokens or secrets.
|
|
158
|
-
*/
|
|
159
|
-
export async function surfaceBillingRequired(
|
|
160
|
-
token: string,
|
|
161
|
-
billing: BillingErrorPayload,
|
|
162
|
-
): Promise<string | null> {
|
|
163
|
-
if (!billing.setup) {
|
|
164
|
-
console.error(
|
|
165
|
-
chalk.yellow(
|
|
166
|
-
"Provisioning is blocked on billing, but the server didn't return a " +
|
|
167
|
-
"payment link. Add a card in the console billing page, then re-run.",
|
|
168
|
-
),
|
|
169
|
-
);
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
const url = await mintPaymentLink(token, billing.setup);
|
|
173
|
-
console.log(
|
|
174
|
-
chalk.yellow("No card on file — provisioning is blocked until one is added."),
|
|
175
|
-
);
|
|
176
|
-
console.log(
|
|
177
|
-
"Add a card here (safe to share with whoever owns billing):\n " +
|
|
178
|
-
chalk.cyan(url),
|
|
179
|
-
);
|
|
180
|
-
console.log(chalk.dim("Once a card is added, re-run the same command."));
|
|
181
|
-
return url;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Status-aware surface for a 402 billing block. hq-pro's envelope carries two
|
|
186
|
-
* distinct remediations that must never be conflated (mirroring the server's
|
|
187
|
-
* own P1-C classification):
|
|
188
|
-
* - `payment_failed` — a card EXISTS and the charge was DECLINED. The
|
|
189
|
-
* server's `message` already carries the friendly decline copy ("Your
|
|
190
|
-
* card was declined…", "insufficient funds", …). Telling this user
|
|
191
|
-
* "No card on file" sends them down the wrong remediation path entirely
|
|
192
|
-
* (observed live 2026-07-20: a declined $80 Outpost proration surfaced
|
|
193
|
-
* as "no card", triggering a hunt for a missing card that existed).
|
|
194
|
-
* The capture link still surfaces — as the way to UPDATE the card.
|
|
195
|
-
* - anything else (`billing_required`) — genuinely no usable card on
|
|
196
|
-
* file; the existing add-a-card copy is correct.
|
|
197
|
-
*/
|
|
198
|
-
export async function surfaceBillingBlocked(
|
|
199
|
-
token: string,
|
|
200
|
-
billing: BillingErrorPayload,
|
|
201
|
-
serverMessage?: string,
|
|
202
|
-
): Promise<string | null> {
|
|
203
|
-
if (billing.status !== "payment_failed") {
|
|
204
|
-
return surfaceBillingRequired(token, billing);
|
|
205
|
-
}
|
|
206
|
-
console.error(
|
|
207
|
-
chalk.yellow(
|
|
208
|
-
serverMessage?.trim() ||
|
|
209
|
-
"Your payment failed. Try a different card or contact your bank.",
|
|
210
|
-
),
|
|
211
|
-
);
|
|
212
|
-
if (!billing.setup) return null;
|
|
213
|
-
try {
|
|
214
|
-
const url = await mintPaymentLink(token, billing.setup);
|
|
215
|
-
console.log(
|
|
216
|
-
"Update or replace the card here (safe to share with whoever owns billing):\n " +
|
|
217
|
-
chalk.cyan(url),
|
|
218
|
-
);
|
|
219
|
-
console.log(
|
|
220
|
-
chalk.dim("Once the payment method is sorted, re-run the same command."),
|
|
221
|
-
);
|
|
222
|
-
return url;
|
|
223
|
-
} catch {
|
|
224
|
-
// Link minting is best-effort on the decline path — the decline reason
|
|
225
|
-
// above is the essential part; the console billing page also works.
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { Breadcrumb } from "@sentry/node";
|
|
2
|
-
|
|
3
|
-
const BUFFER_SIZE = 20;
|
|
4
|
-
const _buffer: Breadcrumb[] = [];
|
|
5
|
-
|
|
6
|
-
// Sentry beforeBreadcrumb hook: records every breadcrumb in a ring buffer
|
|
7
|
-
// and returns it unchanged so Sentry still processes it normally.
|
|
8
|
-
export function beforeBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb | null {
|
|
9
|
-
_buffer.push(breadcrumb);
|
|
10
|
-
if (_buffer.length > BUFFER_SIZE) {
|
|
11
|
-
_buffer.shift();
|
|
12
|
-
}
|
|
13
|
-
return breadcrumb;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function getRecentBreadcrumbs(): Breadcrumb[] {
|
|
17
|
-
return [..._buffer];
|
|
18
|
-
}
|