@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
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root resolution for CLI-hosted HQ scaffold code.
|
|
3
|
+
*
|
|
4
|
+
* When shell that used to live inside the HQ scaffold moves into this npm
|
|
5
|
+
* package, it loses the one thing it relied on: `BASH_SOURCE`/`$0` pointed at
|
|
6
|
+
* the user's live HQ tree, so "where am I" and "where is HQ" were the same
|
|
7
|
+
* answer. Inside a globally installed package they are three different answers,
|
|
8
|
+
* and conflating any two of them silently corrupts the wrong tree.
|
|
9
|
+
*
|
|
10
|
+
* liveRoot The user's HQ installation — companies/, workspace/, personal/,
|
|
11
|
+
* the installed .claude/ and core/. This is runtime state. Hidden
|
|
12
|
+
* runtime commands operate here.
|
|
13
|
+
*
|
|
14
|
+
* packageRoot This npm package's own directory, where bundled scaffold assets
|
|
15
|
+
* ship. Read-only in practice: a global install may sit in a
|
|
16
|
+
* root-owned prefix. NEVER usable as liveRoot.
|
|
17
|
+
*
|
|
18
|
+
* There is deliberately no third "source root". Release-source tooling — the
|
|
19
|
+
* linters, generators, and validators that rewrite checked-in artifacts — stays
|
|
20
|
+
* scaffold-owned, so nothing here ever needs to resolve an hq-core checkout. See
|
|
21
|
+
* the scaffold-vs-cli-code-ownership policy for why.
|
|
22
|
+
*
|
|
23
|
+
* The invariant that matters most: `resolveLiveRoot` never consults
|
|
24
|
+
* `import.meta.url`. A relocated script asking "where is HQ" must get the live
|
|
25
|
+
* tree, not the package it happens to be bundled in.
|
|
26
|
+
*/
|
|
27
|
+
import * as fs from "fs";
|
|
28
|
+
import * as path from "path";
|
|
29
|
+
import { fileURLToPath } from "url";
|
|
30
|
+
/** This package's name, used to identify its root when walking upward. */
|
|
31
|
+
const CLI_PACKAGE_NAME = "@indigoai-us/hq-cli";
|
|
32
|
+
/**
|
|
33
|
+
* A root that cannot be resolved is the caller's state — a wrong `--hq-root`, or a
|
|
34
|
+
* command run from outside any HQ tree. Marking
|
|
35
|
+
* these `expected` makes the top-level handler print the message and skip Sentry;
|
|
36
|
+
* an unmarked error there prints NOTHING and only ships a crash report, which is
|
|
37
|
+
* the worst possible outcome for a misconfiguration the user can fix.
|
|
38
|
+
*
|
|
39
|
+
* Deliberately not used for packaging faults — a missing bundled asset IS a
|
|
40
|
+
* defect and should reach Sentry.
|
|
41
|
+
*/
|
|
42
|
+
function rootError(message) {
|
|
43
|
+
const err = new Error(message);
|
|
44
|
+
err.expected = true;
|
|
45
|
+
return err;
|
|
46
|
+
}
|
|
47
|
+
/** Where bundled scaffold assets live, relative to the package root. */
|
|
48
|
+
export const BUNDLED_ASSET_DIR = path.join("assets", "scaffold");
|
|
49
|
+
/**
|
|
50
|
+
* Does this directory look like an HQ tree root?
|
|
51
|
+
*
|
|
52
|
+
* `companies/` is the load-bearing marker — it is the source of truth for
|
|
53
|
+
* tenants and exists in every real install. The second clause tolerates the
|
|
54
|
+
* three shapes a tree can legitimately be in: a full install, a scaffold
|
|
55
|
+
* checkout without a personal overlay, and a personal-only overlay.
|
|
56
|
+
*/
|
|
57
|
+
export function isHqRoot(dir) {
|
|
58
|
+
return (fs.existsSync(path.join(dir, "companies")) &&
|
|
59
|
+
(fs.existsSync(path.join(dir, ".claude")) ||
|
|
60
|
+
fs.existsSync(path.join(dir, "core")) ||
|
|
61
|
+
fs.existsSync(path.join(dir, "personal"))));
|
|
62
|
+
}
|
|
63
|
+
function realpathOrResolve(p) {
|
|
64
|
+
try {
|
|
65
|
+
return fs.realpathSync(p);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return path.resolve(p);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
let cachedPackageRoot;
|
|
72
|
+
/**
|
|
73
|
+
* This package's installed root — the directory holding its `package.json`.
|
|
74
|
+
*
|
|
75
|
+
* Walks up from the compiled module (`dist/utils/hq-roots.js`) until it finds a
|
|
76
|
+
* `package.json` whose name matches, rather than hard-coding `..` hops, so the
|
|
77
|
+
* answer survives a change in output layout.
|
|
78
|
+
*/
|
|
79
|
+
export function packageRoot() {
|
|
80
|
+
if (cachedPackageRoot === null) {
|
|
81
|
+
throw new Error(`Could not locate the ${CLI_PACKAGE_NAME} package root from ${import.meta.url}.`);
|
|
82
|
+
}
|
|
83
|
+
if (cachedPackageRoot !== undefined)
|
|
84
|
+
return cachedPackageRoot;
|
|
85
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
86
|
+
for (;;) {
|
|
87
|
+
const manifest = path.join(dir, "package.json");
|
|
88
|
+
if (fs.existsSync(manifest)) {
|
|
89
|
+
try {
|
|
90
|
+
const parsed = JSON.parse(fs.readFileSync(manifest, "utf8"));
|
|
91
|
+
if (parsed.name === CLI_PACKAGE_NAME) {
|
|
92
|
+
cachedPackageRoot = realpathOrResolve(dir);
|
|
93
|
+
return cachedPackageRoot;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// A malformed package.json on the way up is not our package; keep going.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const parent = path.dirname(dir);
|
|
101
|
+
if (parent === dir)
|
|
102
|
+
break;
|
|
103
|
+
dir = parent;
|
|
104
|
+
}
|
|
105
|
+
cachedPackageRoot = null;
|
|
106
|
+
throw new Error(`Could not locate the ${CLI_PACKAGE_NAME} package root from ${import.meta.url}.`);
|
|
107
|
+
}
|
|
108
|
+
/** Reset the memoized package root. Tests only. */
|
|
109
|
+
export function __resetPackageRootCache() {
|
|
110
|
+
cachedPackageRoot = undefined;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Resolve the user's live HQ installation.
|
|
114
|
+
*
|
|
115
|
+
* Precedence: `--hq-root` → `HQ_ROOT` → `CLAUDE_PROJECT_DIR` → walk up from cwd.
|
|
116
|
+
*
|
|
117
|
+
* ## An explicit root is trusted; a discovered one must be recognized
|
|
118
|
+
*
|
|
119
|
+
* The FIRST explicit candidate present wins outright — there is no fall-through
|
|
120
|
+
* past it, because silently operating on a different tree than the caller named
|
|
121
|
+
* is the worst available outcome.
|
|
122
|
+
*
|
|
123
|
+
* That explicit candidate is checked only for the two things that are always
|
|
124
|
+
* wrong: it must be a directory, and it must not be inside {@link packageRoot}
|
|
125
|
+
* (that is this package, not anyone's HQ). It is deliberately NOT required to
|
|
126
|
+
* satisfy {@link isHqRoot}.
|
|
127
|
+
*
|
|
128
|
+
* Requiring markers here was tried and was wrong: the scripts these roots feed
|
|
129
|
+
* accepted any `HQ_ROOT` before they moved, and several callers legitimately pass
|
|
130
|
+
* a minimal tree — e.g. `rebuild-threads-index` against a fixture holding nothing
|
|
131
|
+
* but `workspace/threads/`. Enforcing markers on an explicit value turned a
|
|
132
|
+
* relocation into a silent contract change and broke those callers.
|
|
133
|
+
*
|
|
134
|
+
* The cwd walk is the opposite case: there the marker check is the only thing
|
|
135
|
+
* that can identify a root at all, so it stays mandatory.
|
|
136
|
+
*/
|
|
137
|
+
export function resolveLiveRoot(opts = {}) {
|
|
138
|
+
const env = opts.env ?? process.env;
|
|
139
|
+
const candidates = [];
|
|
140
|
+
if (opts.hqRoot) {
|
|
141
|
+
candidates.push({ value: opts.hqRoot, source: "--hq-root" });
|
|
142
|
+
}
|
|
143
|
+
if (env.HQ_ROOT) {
|
|
144
|
+
candidates.push({ value: env.HQ_ROOT, source: "$HQ_ROOT" });
|
|
145
|
+
}
|
|
146
|
+
if (env.CLAUDE_PROJECT_DIR) {
|
|
147
|
+
candidates.push({
|
|
148
|
+
value: env.CLAUDE_PROJECT_DIR,
|
|
149
|
+
source: "$CLAUDE_PROJECT_DIR",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const explicit = candidates[0];
|
|
153
|
+
if (explicit) {
|
|
154
|
+
const resolved = realpathOrResolve(explicit.value);
|
|
155
|
+
if (isWithinPackage(resolved)) {
|
|
156
|
+
throw rootError(`${explicit.source} points inside the ${CLI_PACKAGE_NAME} package ` +
|
|
157
|
+
`(${resolved}). That is the CLI's own install directory, not an HQ tree.`);
|
|
158
|
+
}
|
|
159
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
|
|
160
|
+
throw rootError(`${explicit.source} is not a directory: ${resolved}`);
|
|
161
|
+
}
|
|
162
|
+
return resolved;
|
|
163
|
+
}
|
|
164
|
+
// Walk up from cwd. Unlike the explicit tiers this is a guess, so failing to
|
|
165
|
+
// find a root here is reported as "run this from inside HQ" rather than as a
|
|
166
|
+
// bad value.
|
|
167
|
+
let dir = realpathOrResolve(opts.cwd ?? process.cwd());
|
|
168
|
+
for (;;) {
|
|
169
|
+
if (isHqRoot(dir) && !isWithinPackage(dir))
|
|
170
|
+
return dir;
|
|
171
|
+
const parent = path.dirname(dir);
|
|
172
|
+
if (parent === dir)
|
|
173
|
+
break;
|
|
174
|
+
dir = parent;
|
|
175
|
+
}
|
|
176
|
+
throw rootError("Could not find your HQ installation. Run this from inside your HQ " +
|
|
177
|
+
"directory, or pass --hq-root <path> / set $HQ_ROOT.");
|
|
178
|
+
}
|
|
179
|
+
function isWithinPackage(candidate) {
|
|
180
|
+
let root;
|
|
181
|
+
try {
|
|
182
|
+
root = packageRoot();
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// If we cannot locate our own package we cannot be inside it either.
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
const rel = path.relative(root, candidate);
|
|
189
|
+
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Absolute path to a scaffold asset bundled in this package.
|
|
193
|
+
*
|
|
194
|
+
* Refuses to resolve outside the asset directory so a manifest entry can never
|
|
195
|
+
* be coaxed into executing an arbitrary path, and fails loudly when the asset
|
|
196
|
+
* is missing — an absent asset means the packlist dropped it, which is a
|
|
197
|
+
* packaging bug worth surfacing rather than a runtime fallback.
|
|
198
|
+
*/
|
|
199
|
+
export function resolveBundledAsset(relativePath) {
|
|
200
|
+
const assetDir = path.join(packageRoot(), BUNDLED_ASSET_DIR);
|
|
201
|
+
const resolved = path.resolve(assetDir, relativePath);
|
|
202
|
+
const rel = path.relative(assetDir, resolved);
|
|
203
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
204
|
+
throw new Error(`Bundled asset path escapes ${BUNDLED_ASSET_DIR}: ${relativePath}`);
|
|
205
|
+
}
|
|
206
|
+
if (!fs.existsSync(resolved)) {
|
|
207
|
+
throw new Error(`Bundled asset is missing from the installed package: ${BUNDLED_ASSET_DIR}/${relativePath}\n` +
|
|
208
|
+
"This is a packaging fault — the file exists in source but was not published.");
|
|
209
|
+
}
|
|
210
|
+
return resolved;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=hq-roots.js.map
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
/** Vault API keys issued by `hq api-keys create` (hq-pro). */
|
|
2
2
|
export declare const HQ_API_KEY_PREFIX = "hqk_";
|
|
3
|
+
/**
|
|
4
|
+
* Capability catalog for identity-bound vault API keys. MUST match hq-pro's
|
|
5
|
+
* `src/api-keys/capabilities.ts` — the server is authoritative for what a key
|
|
6
|
+
* may do; this copy exists so the CLI can name capabilities in `api-keys
|
|
7
|
+
* create` and in its own fail-closed messages.
|
|
8
|
+
*/
|
|
9
|
+
export declare const API_KEY_CAPABILITIES: readonly ["secrets:read", "secrets:write", "secrets:share", "deploy:write", "deploy:read", "files:read", "files:write", "search:query", "agents:use", "mcp:call", "packs:install", "members:read", "members:write"];
|
|
10
|
+
export type ApiKeyCapability = (typeof API_KEY_CAPABILITIES)[number];
|
|
11
|
+
/** Deploy apps wildcard: every app the key owner can reach at use time. */
|
|
12
|
+
export declare const DEPLOY_APP_ALL = "*";
|
|
13
|
+
export declare function isApiKeyCapability(value: string): value is ApiKeyCapability;
|
|
14
|
+
/**
|
|
15
|
+
* Capabilities hq-cli can exercise with an `HQ_API_KEY`. Vault surfaces use
|
|
16
|
+
* parallel Bearer `hqk_` routes under `/v1/keys/…` (rewritten in
|
|
17
|
+
* {@link vaultApiFetch}); `deploy:*` is consumed by hq-deploy via SigV4
|
|
18
|
+
* introspect. Surfaces without a keyed route still fail closed.
|
|
19
|
+
*/
|
|
20
|
+
export declare const CLI_API_KEY_CAPABILITIES: readonly ApiKeyCapability[];
|
|
21
|
+
/** True when hq-cli has a Bearer `hqk_` route for this capability. */
|
|
22
|
+
export declare function apiKeyCapabilityIsUsable(capability: ApiKeyCapability): boolean;
|
|
23
|
+
/** Catalog capabilities hq-cli cannot use with an API key yet. */
|
|
24
|
+
export declare function unusableApiKeyCapabilities(): ApiKeyCapability[];
|
|
3
25
|
export type VaultCredential = {
|
|
4
26
|
kind: "api-key";
|
|
5
27
|
token: string;
|
|
@@ -23,8 +45,46 @@ export declare function resolveVaultCredential(options?: {
|
|
|
23
45
|
interactive?: boolean;
|
|
24
46
|
}): Promise<VaultCredential>;
|
|
25
47
|
/**
|
|
26
|
-
*
|
|
27
|
-
* (
|
|
48
|
+
* Fail-closed message for a surface an `HQ_API_KEY` cannot drive. Always names
|
|
49
|
+
* what an API key CAN do (granted) next to what it cannot (missing), so the
|
|
50
|
+
* caller can decide between minting a broader key and unsetting HQ_API_KEY.
|
|
51
|
+
*/
|
|
52
|
+
export declare function apiKeyCapabilityGateMessage(capability: ApiKeyCapability | null, commandLabel: string, options?: {
|
|
53
|
+
routeAvailable?: boolean;
|
|
54
|
+
}): string;
|
|
55
|
+
/**
|
|
56
|
+
* Capability gate for a command about to run under `HQ_API_KEY`.
|
|
57
|
+
*
|
|
58
|
+
* No-op when HQ_API_KEY is unset (the Cognito path is unchanged) and when the
|
|
59
|
+
* capability has a CLI-reachable Bearer `hqk_` route. Otherwise throws a
|
|
60
|
+
* fail-closed error listing granted vs missing capabilities.
|
|
61
|
+
*
|
|
62
|
+
* - `capability: null` — the surface has no capability at all (key
|
|
63
|
+
* administration, interactive session flows).
|
|
64
|
+
* - `routeAvailable: false` — the capability exists in the catalog but this
|
|
65
|
+
* specific surface has no `/v1/keys` route (e.g. the secrets HEAD probe).
|
|
66
|
+
*
|
|
67
|
+
* The server re-checks the key's real grants on every call; this gate only
|
|
68
|
+
* stops the CLI from quietly using a Cognito session instead of the key.
|
|
69
|
+
*/
|
|
70
|
+
export declare function requireApiKeyCapability(capability: ApiKeyCapability | null, commandLabel: string, options?: {
|
|
71
|
+
routeAvailable?: boolean;
|
|
72
|
+
}): void;
|
|
73
|
+
/**
|
|
74
|
+
* {@link resolveVaultCredential} behind {@link requireApiKeyCapability}: the
|
|
75
|
+
* gate runs BEFORE any credential work so a key that cannot drive the surface
|
|
76
|
+
* never reaches the network and never triggers an interactive login.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveVaultCredentialForCapability(capability: ApiKeyCapability | null, commandLabel: string, options?: {
|
|
79
|
+
interactive?: boolean;
|
|
80
|
+
routeAvailable?: boolean;
|
|
81
|
+
}): Promise<VaultCredential>;
|
|
82
|
+
/**
|
|
83
|
+
* Render hq-pro's capability-denial body (`_api-key-auth.ts` returns
|
|
84
|
+
* `{ error, granted: string[], missing: string }` on a 403) as a message that
|
|
85
|
+
* names the key's REAL granted set next to what the call needed. This is the
|
|
86
|
+
* authoritative granted-vs-missing report; the client-side gate above can only
|
|
87
|
+
* describe what hq-cli supports.
|
|
28
88
|
*/
|
|
29
|
-
export declare function
|
|
89
|
+
export declare function formatApiKeyCapabilityDenial(body: unknown, fallback: string): string;
|
|
30
90
|
//# sourceMappingURL=resolve-vault-credential.d.ts.map
|
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
import { ensureCognitoToken } from "./cognito-session.js";
|
|
2
2
|
/** Vault API keys issued by `hq api-keys create` (hq-pro). */
|
|
3
3
|
export const HQ_API_KEY_PREFIX = "hqk_";
|
|
4
|
+
/**
|
|
5
|
+
* Capability catalog for identity-bound vault API keys. MUST match hq-pro's
|
|
6
|
+
* `src/api-keys/capabilities.ts` — the server is authoritative for what a key
|
|
7
|
+
* may do; this copy exists so the CLI can name capabilities in `api-keys
|
|
8
|
+
* create` and in its own fail-closed messages.
|
|
9
|
+
*/
|
|
10
|
+
export const API_KEY_CAPABILITIES = [
|
|
11
|
+
"secrets:read",
|
|
12
|
+
"secrets:write",
|
|
13
|
+
"secrets:share",
|
|
14
|
+
"deploy:write",
|
|
15
|
+
"deploy:read",
|
|
16
|
+
"files:read",
|
|
17
|
+
"files:write",
|
|
18
|
+
"search:query",
|
|
19
|
+
"agents:use",
|
|
20
|
+
"mcp:call",
|
|
21
|
+
"packs:install",
|
|
22
|
+
"members:read",
|
|
23
|
+
"members:write",
|
|
24
|
+
];
|
|
25
|
+
/** Deploy apps wildcard: every app the key owner can reach at use time. */
|
|
26
|
+
export const DEPLOY_APP_ALL = "*";
|
|
27
|
+
const CAPABILITY_SET = new Set(API_KEY_CAPABILITIES);
|
|
28
|
+
export function isApiKeyCapability(value) {
|
|
29
|
+
return CAPABILITY_SET.has(value);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Capabilities hq-cli can exercise with an `HQ_API_KEY`. Vault surfaces use
|
|
33
|
+
* parallel Bearer `hqk_` routes under `/v1/keys/…` (rewritten in
|
|
34
|
+
* {@link vaultApiFetch}); `deploy:*` is consumed by hq-deploy via SigV4
|
|
35
|
+
* introspect. Surfaces without a keyed route still fail closed.
|
|
36
|
+
*/
|
|
37
|
+
export const CLI_API_KEY_CAPABILITIES = [
|
|
38
|
+
"secrets:read",
|
|
39
|
+
"secrets:write",
|
|
40
|
+
"secrets:share",
|
|
41
|
+
"deploy:write",
|
|
42
|
+
"deploy:read",
|
|
43
|
+
"files:read",
|
|
44
|
+
"files:write",
|
|
45
|
+
"search:query",
|
|
46
|
+
"agents:use",
|
|
47
|
+
"mcp:call",
|
|
48
|
+
"packs:install",
|
|
49
|
+
"members:read",
|
|
50
|
+
"members:write",
|
|
51
|
+
];
|
|
52
|
+
const CLI_CAPABILITY_SET = new Set(CLI_API_KEY_CAPABILITIES);
|
|
53
|
+
/** True when hq-cli has a Bearer `hqk_` route for this capability. */
|
|
54
|
+
export function apiKeyCapabilityIsUsable(capability) {
|
|
55
|
+
return CLI_CAPABILITY_SET.has(capability);
|
|
56
|
+
}
|
|
57
|
+
/** Catalog capabilities hq-cli cannot use with an API key yet. */
|
|
58
|
+
export function unusableApiKeyCapabilities() {
|
|
59
|
+
return API_KEY_CAPABILITIES.filter((cap) => !CLI_CAPABILITY_SET.has(cap));
|
|
60
|
+
}
|
|
4
61
|
/**
|
|
5
62
|
* Raw HQ_API_KEY from the environment, trimmed. Undefined when unset/empty.
|
|
6
63
|
* Does not validate prefix — use {@link resolveVaultCredential} for that.
|
|
@@ -34,15 +91,104 @@ export async function resolveVaultCredential(options) {
|
|
|
34
91
|
});
|
|
35
92
|
return { kind: "cognito", token };
|
|
36
93
|
}
|
|
94
|
+
function expectedError(message) {
|
|
95
|
+
// Marked expected so main.ts prints the message and skips Sentry: a key
|
|
96
|
+
// presented at a surface it cannot serve is the caller's configuration,
|
|
97
|
+
// not an hq-cli defect.
|
|
98
|
+
return Object.assign(new Error(message), { expected: true });
|
|
99
|
+
}
|
|
100
|
+
function formatCapabilityList(capabilities) {
|
|
101
|
+
return capabilities.length > 0 ? capabilities.join(", ") : "(none)";
|
|
102
|
+
}
|
|
37
103
|
/**
|
|
38
|
-
*
|
|
39
|
-
* (
|
|
104
|
+
* Fail-closed message for a surface an `HQ_API_KEY` cannot drive. Always names
|
|
105
|
+
* what an API key CAN do (granted) next to what it cannot (missing), so the
|
|
106
|
+
* caller can decide between minting a broader key and unsetting HQ_API_KEY.
|
|
40
107
|
*/
|
|
41
|
-
export function
|
|
108
|
+
export function apiKeyCapabilityGateMessage(capability, commandLabel, options) {
|
|
109
|
+
const head = capability === null
|
|
110
|
+
? `HQ_API_KEY is set; '${commandLabel}' is not supported for API keys — ` +
|
|
111
|
+
`no capability grants it (it needs your Cognito session).`
|
|
112
|
+
: options?.routeAvailable === false
|
|
113
|
+
? `HQ_API_KEY is set; '${commandLabel}' is not supported for API keys — ` +
|
|
114
|
+
`capability '${capability}' would apply but hq-pro serves no vault ` +
|
|
115
|
+
`API-key route for it yet.`
|
|
116
|
+
: `HQ_API_KEY is set; '${commandLabel}' is not supported for API keys — ` +
|
|
117
|
+
`it needs capability '${capability}'.`;
|
|
118
|
+
return [
|
|
119
|
+
head,
|
|
120
|
+
` Granted to hq-cli with an API key: ${formatCapabilityList(CLI_API_KEY_CAPABILITIES)}`,
|
|
121
|
+
` Missing (Cognito session required): ${formatCapabilityList(unusableApiKeyCapabilities())}`,
|
|
122
|
+
` Unset HQ_API_KEY to use your Cognito session.`,
|
|
123
|
+
].join("\n");
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Capability gate for a command about to run under `HQ_API_KEY`.
|
|
127
|
+
*
|
|
128
|
+
* No-op when HQ_API_KEY is unset (the Cognito path is unchanged) and when the
|
|
129
|
+
* capability has a CLI-reachable Bearer `hqk_` route. Otherwise throws a
|
|
130
|
+
* fail-closed error listing granted vs missing capabilities.
|
|
131
|
+
*
|
|
132
|
+
* - `capability: null` — the surface has no capability at all (key
|
|
133
|
+
* administration, interactive session flows).
|
|
134
|
+
* - `routeAvailable: false` — the capability exists in the catalog but this
|
|
135
|
+
* specific surface has no `/v1/keys` route (e.g. the secrets HEAD probe).
|
|
136
|
+
*
|
|
137
|
+
* The server re-checks the key's real grants on every call; this gate only
|
|
138
|
+
* stops the CLI from quietly using a Cognito session instead of the key.
|
|
139
|
+
*/
|
|
140
|
+
export function requireApiKeyCapability(capability, commandLabel, options) {
|
|
42
141
|
if (peekHqApiKey() === undefined)
|
|
43
142
|
return;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
143
|
+
if (capability !== null &&
|
|
144
|
+
options?.routeAvailable !== false &&
|
|
145
|
+
apiKeyCapabilityIsUsable(capability)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
throw expectedError(apiKeyCapabilityGateMessage(capability, commandLabel, options));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* {@link resolveVaultCredential} behind {@link requireApiKeyCapability}: the
|
|
152
|
+
* gate runs BEFORE any credential work so a key that cannot drive the surface
|
|
153
|
+
* never reaches the network and never triggers an interactive login.
|
|
154
|
+
*/
|
|
155
|
+
export async function resolveVaultCredentialForCapability(capability, commandLabel, options) {
|
|
156
|
+
requireApiKeyCapability(capability, commandLabel, {
|
|
157
|
+
routeAvailable: options?.routeAvailable,
|
|
158
|
+
});
|
|
159
|
+
return resolveVaultCredential({ interactive: options?.interactive });
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Render hq-pro's capability-denial body (`_api-key-auth.ts` returns
|
|
163
|
+
* `{ error, granted: string[], missing: string }` on a 403) as a message that
|
|
164
|
+
* names the key's REAL granted set next to what the call needed. This is the
|
|
165
|
+
* authoritative granted-vs-missing report; the client-side gate above can only
|
|
166
|
+
* describe what hq-cli supports.
|
|
167
|
+
*/
|
|
168
|
+
export function formatApiKeyCapabilityDenial(body, fallback) {
|
|
169
|
+
const record = body !== null && typeof body === "object"
|
|
170
|
+
? body
|
|
171
|
+
: {};
|
|
172
|
+
const head = typeof record.error === "string"
|
|
173
|
+
? record.error
|
|
174
|
+
: typeof record.message === "string"
|
|
175
|
+
? record.message
|
|
176
|
+
: fallback;
|
|
177
|
+
const granted = Array.isArray(record.granted)
|
|
178
|
+
? record.granted.filter((cap) => typeof cap === "string")
|
|
179
|
+
: [];
|
|
180
|
+
const missing = Array.isArray(record.missing)
|
|
181
|
+
? record.missing.filter((cap) => typeof cap === "string")
|
|
182
|
+
: typeof record.missing === "string"
|
|
183
|
+
? [record.missing]
|
|
184
|
+
: [];
|
|
185
|
+
if (granted.length === 0 && missing.length === 0) {
|
|
186
|
+
return head;
|
|
187
|
+
}
|
|
188
|
+
return [
|
|
189
|
+
head,
|
|
190
|
+
` Granted: ${formatCapabilityList(granted)}`,
|
|
191
|
+
` Missing: ${formatCapabilityList(missing)}`,
|
|
192
|
+
].join("\n");
|
|
47
193
|
}
|
|
48
194
|
//# sourceMappingURL=resolve-vault-credential.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner for scaffold shell bundled inside this package.
|
|
3
|
+
*
|
|
4
|
+
* The scripts this executes used to be invoked directly by skills, hooks, and
|
|
5
|
+
* other scripts. Their callers observe argv, stdin, stdout, stderr, and the exit
|
|
6
|
+
* code — so this runner's job is to be transparent on all five, and to hand the
|
|
7
|
+
* script the same environment it used to get from living inside the HQ tree.
|
|
8
|
+
*
|
|
9
|
+
* Deliberate choices:
|
|
10
|
+
*
|
|
11
|
+
* - `stdio: "inherit"`. Not piped. Piping would buffer output that callers
|
|
12
|
+
* stream, reorder stdout against stderr, and require re-emitting bytes we
|
|
13
|
+
* have no reason to touch. Inheriting also means a script that reads stdin
|
|
14
|
+
* (the hook payload pattern) gets the real fd, unbuffered and unconsumed.
|
|
15
|
+
*
|
|
16
|
+
* - an explicit interpreter (`bash` or `node`) rather than executing the file
|
|
17
|
+
* directly. The executable bit does survive `npm publish` for files that
|
|
18
|
+
* carry it, but relying on it makes correctness depend on a mode that a
|
|
19
|
+
* repack, a filesystem, or a zip round-trip can quietly drop. Naming the
|
|
20
|
+
* interpreter removes that dependency without changing script semantics.
|
|
21
|
+
* The interpreter is chosen by extension — two of the relocated assets are
|
|
22
|
+
* Node programs, and running those through bash fails with a shell syntax
|
|
23
|
+
* error on the first `const`.
|
|
24
|
+
*
|
|
25
|
+
* - the exit code is returned, never mapped. A script that exits 2 to block a
|
|
26
|
+
* tool call must still exit 2 through this path.
|
|
27
|
+
*
|
|
28
|
+
* - signals are surfaced as the conventional 128+n. A caller that kills a
|
|
29
|
+
* script needs to see termination, not a success code.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Which interpreter runs a bundled asset, chosen by extension.
|
|
33
|
+
*
|
|
34
|
+
* Exported so the manifest test can assert every declared asset maps to one,
|
|
35
|
+
* rather than discovering a mismatch at runtime on a user's machine.
|
|
36
|
+
*/
|
|
37
|
+
export declare function interpreterFor(asset: string): "node" | "bash";
|
|
38
|
+
export type RunBundledScriptOptions = {
|
|
39
|
+
/** Path of the script under `assets/scaffold/`, e.g. `core/scripts/foo.sh`. */
|
|
40
|
+
asset: string;
|
|
41
|
+
/** Arguments passed through to the script, unchanged. */
|
|
42
|
+
args?: string[];
|
|
43
|
+
/** Working directory for the child. Defaults to the resolved live root. */
|
|
44
|
+
cwd: string;
|
|
45
|
+
/**
|
|
46
|
+
* The live HQ root. Exported as both `HQ_ROOT` and `CLAUDE_PROJECT_DIR`
|
|
47
|
+
* because relocated scripts read one, the other, or both — and inside the CLI
|
|
48
|
+
* neither is guaranteed to already be set by the caller.
|
|
49
|
+
*/
|
|
50
|
+
hqRoot?: string;
|
|
51
|
+
/** Extra environment for the child, applied last. */
|
|
52
|
+
env?: Record<string, string | undefined>;
|
|
53
|
+
};
|
|
54
|
+
export type RunBundledScriptResult = {
|
|
55
|
+
/** The child's exit code, or 128+signal if it was terminated by one. */
|
|
56
|
+
code: number;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Execute a bundled scaffold script and return its exit code.
|
|
60
|
+
*
|
|
61
|
+
* Does not throw on a non-zero exit — a non-zero code is data here, not an
|
|
62
|
+
* error, because several relocated scripts use exit codes as their interface.
|
|
63
|
+
* Throws only when the script could not be started at all.
|
|
64
|
+
*/
|
|
65
|
+
export declare function runBundledScript(opts: RunBundledScriptOptions): RunBundledScriptResult;
|
|
66
|
+
//# sourceMappingURL=run-bundled-script.d.ts.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner for scaffold shell bundled inside this package.
|
|
3
|
+
*
|
|
4
|
+
* The scripts this executes used to be invoked directly by skills, hooks, and
|
|
5
|
+
* other scripts. Their callers observe argv, stdin, stdout, stderr, and the exit
|
|
6
|
+
* code — so this runner's job is to be transparent on all five, and to hand the
|
|
7
|
+
* script the same environment it used to get from living inside the HQ tree.
|
|
8
|
+
*
|
|
9
|
+
* Deliberate choices:
|
|
10
|
+
*
|
|
11
|
+
* - `stdio: "inherit"`. Not piped. Piping would buffer output that callers
|
|
12
|
+
* stream, reorder stdout against stderr, and require re-emitting bytes we
|
|
13
|
+
* have no reason to touch. Inheriting also means a script that reads stdin
|
|
14
|
+
* (the hook payload pattern) gets the real fd, unbuffered and unconsumed.
|
|
15
|
+
*
|
|
16
|
+
* - an explicit interpreter (`bash` or `node`) rather than executing the file
|
|
17
|
+
* directly. The executable bit does survive `npm publish` for files that
|
|
18
|
+
* carry it, but relying on it makes correctness depend on a mode that a
|
|
19
|
+
* repack, a filesystem, or a zip round-trip can quietly drop. Naming the
|
|
20
|
+
* interpreter removes that dependency without changing script semantics.
|
|
21
|
+
* The interpreter is chosen by extension — two of the relocated assets are
|
|
22
|
+
* Node programs, and running those through bash fails with a shell syntax
|
|
23
|
+
* error on the first `const`.
|
|
24
|
+
*
|
|
25
|
+
* - the exit code is returned, never mapped. A script that exits 2 to block a
|
|
26
|
+
* tool call must still exit 2 through this path.
|
|
27
|
+
*
|
|
28
|
+
* - signals are surfaced as the conventional 128+n. A caller that kills a
|
|
29
|
+
* script needs to see termination, not a success code.
|
|
30
|
+
*/
|
|
31
|
+
import { spawnSync } from "child_process";
|
|
32
|
+
import * as path from "path";
|
|
33
|
+
import { resolveBundledAsset } from "./hq-roots.js";
|
|
34
|
+
/**
|
|
35
|
+
* Which interpreter runs a bundled asset, chosen by extension.
|
|
36
|
+
*
|
|
37
|
+
* Exported so the manifest test can assert every declared asset maps to one,
|
|
38
|
+
* rather than discovering a mismatch at runtime on a user's machine.
|
|
39
|
+
*/
|
|
40
|
+
export function interpreterFor(asset) {
|
|
41
|
+
const ext = path.extname(asset).toLowerCase();
|
|
42
|
+
return ext === ".mjs" || ext === ".js" || ext === ".cjs" ? "node" : "bash";
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Execute a bundled scaffold script and return its exit code.
|
|
46
|
+
*
|
|
47
|
+
* Does not throw on a non-zero exit — a non-zero code is data here, not an
|
|
48
|
+
* error, because several relocated scripts use exit codes as their interface.
|
|
49
|
+
* Throws only when the script could not be started at all.
|
|
50
|
+
*/
|
|
51
|
+
export function runBundledScript(opts) {
|
|
52
|
+
const script = resolveBundledAsset(opts.asset);
|
|
53
|
+
const env = { ...process.env };
|
|
54
|
+
if (opts.hqRoot) {
|
|
55
|
+
env.HQ_ROOT = opts.hqRoot;
|
|
56
|
+
env.CLAUDE_PROJECT_DIR = opts.hqRoot;
|
|
57
|
+
}
|
|
58
|
+
Object.assign(env, opts.env ?? {});
|
|
59
|
+
const result = spawnSync(interpreterFor(opts.asset), [script, ...(opts.args ?? [])], {
|
|
60
|
+
cwd: opts.cwd,
|
|
61
|
+
env,
|
|
62
|
+
stdio: "inherit",
|
|
63
|
+
});
|
|
64
|
+
if (result.error) {
|
|
65
|
+
throw new Error(`Failed to run bundled script ${opts.asset}: ${result.error.message}`);
|
|
66
|
+
}
|
|
67
|
+
if (result.signal) {
|
|
68
|
+
// Conventional shell encoding, so a caller checking for termination sees it.
|
|
69
|
+
const signals = {
|
|
70
|
+
SIGHUP: 1,
|
|
71
|
+
SIGINT: 2,
|
|
72
|
+
SIGQUIT: 3,
|
|
73
|
+
SIGKILL: 9,
|
|
74
|
+
SIGTERM: 15,
|
|
75
|
+
};
|
|
76
|
+
return { code: 128 + (signals[result.signal] ?? 0) };
|
|
77
|
+
}
|
|
78
|
+
return { code: result.status ?? 0 };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=run-bundled-script.js.map
|
|
@@ -13,6 +13,27 @@ export interface VaultApiOptions {
|
|
|
13
13
|
query?: Record<string, string>;
|
|
14
14
|
signal?: AbortSignal;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Map Cognito JWT paths onto parallel Bearer `hqk_` routes under `/v1/keys/…`.
|
|
18
|
+
* Returns null when no keyed route exists (caller must fail closed).
|
|
19
|
+
*
|
|
20
|
+
* The table mirrors hq-pro's gateway routes, NOT its path shapes: a rewrite is
|
|
21
|
+
* listed only where an `hqk_`-authorized route actually exists. Two surfaces
|
|
22
|
+
* are therefore unmapped ON PURPOSE, and adding a rewrite for them would turn
|
|
23
|
+
* an explicit fail-closed error into a 404/401 that reads like a login problem:
|
|
24
|
+
*
|
|
25
|
+
* - the unversioned file ACL + share surfaces (`/files/{companyUid}/acl…`,
|
|
26
|
+
* `/files/{companyUid}/share-session`) — hq-pro serves these under JWT
|
|
27
|
+
* only. `/v1/keys/files/…` exists but covers the VERSIONED family
|
|
28
|
+
* (`list`, `versions`, `versions-list`, `grants`, `tombstones`, `presign`,
|
|
29
|
+
* `delete`, `restore`), which is a different set of handlers.
|
|
30
|
+
* - the notify surface (`/v1/notify/dm`, `/v1/notify/thread`) behind
|
|
31
|
+
* `hq agents message|thread` — there is no `/v1/keys/notify…` route.
|
|
32
|
+
*
|
|
33
|
+
* Both are additionally gated at the command layer so they fail before any
|
|
34
|
+
* network call; this stays the backstop for a new call site that forgets to.
|
|
35
|
+
*/
|
|
36
|
+
export declare function rewritePathForApiKey(path: string): string | null;
|
|
16
37
|
export declare function vaultApiFetch(opts: VaultApiOptions): Promise<Response>;
|
|
17
38
|
/**
|
|
18
39
|
* Public (NONE-auth) GET against the vault API — no bearer token. The
|