@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,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hq packages install <slug> — download, verify, and extract a package (US-005)
|
|
3
|
-
*
|
|
4
|
-
* Flow:
|
|
5
|
-
* 1. Check auth (prompt login if needed)
|
|
6
|
-
* 2. Check entitlement via API
|
|
7
|
-
* 3. Download tarball via presigned URL
|
|
8
|
-
* 4. Verify SHA256 hash
|
|
9
|
-
* 5. Verify RSA signature (if provided)
|
|
10
|
-
* 6. Extract to packages/installed/<slug>/
|
|
11
|
-
* 7. Validate extracted package.yaml slug matches
|
|
12
|
-
* 8. Update packages/registry.yaml
|
|
13
|
-
* 9. Print next-step message
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import * as fs from 'fs';
|
|
17
|
-
import * as os from 'os';
|
|
18
|
-
import * as path from 'path';
|
|
19
|
-
import { parseUserYaml } from '../utils/user-yaml-error.js';
|
|
20
|
-
import { execSync } from 'child_process';
|
|
21
|
-
import { Command } from 'commander';
|
|
22
|
-
import chalk from 'chalk';
|
|
23
|
-
import { ensureCognitoToken } from '../utils/cognito-session.js';
|
|
24
|
-
import { resolveDefaultHqRoot } from '../utils/cognito-session.js';
|
|
25
|
-
import {
|
|
26
|
-
getRegistryUrl,
|
|
27
|
-
RegistryClient,
|
|
28
|
-
} from '../utils/registry-client.js';
|
|
29
|
-
import { verifySha256, verifyRsaSignature } from '../utils/integrity.js';
|
|
30
|
-
import { addToRegistry } from '../utils/registry.js';
|
|
31
|
-
import { assertCognitoOnlyCommand } from '../utils/resolve-vault-credential.js';
|
|
32
|
-
import {
|
|
33
|
-
MARKETPLACE_PREFIX,
|
|
34
|
-
installPack,
|
|
35
|
-
sourceMatchesPackPattern,
|
|
36
|
-
} from './pack-install.js';
|
|
37
|
-
|
|
38
|
-
export function registerPackageInstallCommand(parent: Command): void {
|
|
39
|
-
parent
|
|
40
|
-
.command('install <source>')
|
|
41
|
-
.description(
|
|
42
|
-
'Install a package. Sources: bare slug (registry, Cognito-gated), ' +
|
|
43
|
-
'@scope/name[@ver] (npm pack), git URL[#ref], or local path.'
|
|
44
|
-
)
|
|
45
|
-
.option('--company <co>', 'Scope package secret authorization to a specific company')
|
|
46
|
-
.option('--allow-hooks', 'Skip the hooks confirmation prompt (content-pack flow)')
|
|
47
|
-
.option('--allow-mcp', 'Skip the MCP server confirmation prompt (content-pack flow)')
|
|
48
|
-
.option('--branch', 'Follow a ref instead of SHA-pinning (git content-pack flow)')
|
|
49
|
-
.action(
|
|
50
|
-
async (
|
|
51
|
-
source: string,
|
|
52
|
-
opts: { company?: string; allowHooks?: boolean; allowMcp?: boolean; branch?: boolean }
|
|
53
|
-
) => {
|
|
54
|
-
try {
|
|
55
|
-
// Gate the top-level `hq install` entrypoint only — `installPack`
|
|
56
|
-
// itself is also called by `hq packs update` (packs.ts), which must
|
|
57
|
-
// stay able to fail closed at its OWN call site (before it un-wires
|
|
58
|
-
// an existing pack) rather than mid-flight inside installPack.
|
|
59
|
-
assertCognitoOnlyCommand('hq install');
|
|
60
|
-
if (sourceMatchesPackPattern(source)) {
|
|
61
|
-
await installPack(source, {
|
|
62
|
-
company: opts.company,
|
|
63
|
-
allowHooks: opts.allowHooks,
|
|
64
|
-
allowMcp: opts.allowMcp,
|
|
65
|
-
followBranch: opts.branch,
|
|
66
|
-
});
|
|
67
|
-
} else {
|
|
68
|
-
// Bare slugs: the legacy Cognito-gated registry backend
|
|
69
|
-
// (/packages, /entitlements) was never deployed, so a bare slug
|
|
70
|
-
// would fail at registry setup. Resolve it through the marketplace
|
|
71
|
-
// listings transport — the live install path — instead.
|
|
72
|
-
await installPack(`${MARKETPLACE_PREFIX}${source}`, {
|
|
73
|
-
company: opts.company,
|
|
74
|
-
allowHooks: opts.allowHooks,
|
|
75
|
-
allowMcp: opts.allowMcp,
|
|
76
|
-
followBranch: opts.branch,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
} catch (error) {
|
|
80
|
-
console.error(
|
|
81
|
-
chalk.red('Install failed:'),
|
|
82
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
83
|
-
);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function installPackage(
|
|
91
|
-
slug: string,
|
|
92
|
-
company?: string
|
|
93
|
-
): Promise<void> {
|
|
94
|
-
// 1. Auth
|
|
95
|
-
const accessToken = await ensureCognitoToken();
|
|
96
|
-
const registryUrl = getRegistryUrl();
|
|
97
|
-
const client = new RegistryClient(registryUrl, accessToken);
|
|
98
|
-
|
|
99
|
-
// 2. Check entitlement
|
|
100
|
-
console.log(chalk.dim(`Checking entitlement for ${slug}...`));
|
|
101
|
-
const entitlement = await client.checkEntitlement(slug);
|
|
102
|
-
if (!entitlement.entitled) {
|
|
103
|
-
throw new Error(
|
|
104
|
-
`You are not entitled to package "${slug}". Visit the registry to purchase or request access.`
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 3. Get download URL
|
|
109
|
-
console.log(chalk.dim('Fetching download URL...'));
|
|
110
|
-
const download = await client.getDownloadUrl(slug);
|
|
111
|
-
|
|
112
|
-
// 4. Download to temp file
|
|
113
|
-
const tmpDir = os.tmpdir();
|
|
114
|
-
const tmpFile = path.resolve(tmpDir, `hq-pkg-${slug}-${Date.now()}.tar.gz`);
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
console.log(chalk.dim('Downloading package...'));
|
|
118
|
-
const response = await fetch(download.url, {
|
|
119
|
-
signal: AbortSignal.timeout(120_000),
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
if (!response.ok) {
|
|
123
|
-
throw new Error(`Download failed (${response.status})`);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
127
|
-
fs.writeFileSync(tmpFile, buffer);
|
|
128
|
-
|
|
129
|
-
// 5. Verify SHA256
|
|
130
|
-
console.log(chalk.dim('Verifying integrity...'));
|
|
131
|
-
const hashValid = await verifySha256(tmpFile, download.sha256);
|
|
132
|
-
if (!hashValid) {
|
|
133
|
-
throw new Error(
|
|
134
|
-
'SHA256 hash mismatch — the downloaded file may be corrupted or tampered with.'
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 6. Verify RSA signature (if provided)
|
|
139
|
-
if (download.signature) {
|
|
140
|
-
const sigValid = verifyRsaSignature(download.sha256, download.signature);
|
|
141
|
-
if (!sigValid) {
|
|
142
|
-
throw new Error(
|
|
143
|
-
'RSA signature verification failed — the package may have been tampered with or the public key is missing/invalid. Aborting install.'
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 7. Extract
|
|
149
|
-
const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
|
|
150
|
-
const installDir = path.resolve(hqRoot, 'packages', 'installed', slug);
|
|
151
|
-
|
|
152
|
-
// Clean existing installation
|
|
153
|
-
if (fs.existsSync(installDir)) {
|
|
154
|
-
fs.rmSync(installDir, { recursive: true, force: true });
|
|
155
|
-
}
|
|
156
|
-
fs.mkdirSync(installDir, { recursive: true });
|
|
157
|
-
|
|
158
|
-
execSync(`tar -xzf "${tmpFile}" -C "${installDir}"`, {
|
|
159
|
-
stdio: 'pipe',
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// 8. Validate package.yaml slug
|
|
163
|
-
const packageYamlPath = path.resolve(installDir, 'package.yaml');
|
|
164
|
-
if (fs.existsSync(packageYamlPath)) {
|
|
165
|
-
const pkgContent = fs.readFileSync(packageYamlPath, 'utf-8');
|
|
166
|
-
const pkgMeta = parseUserYaml<{ slug?: string } | null>(pkgContent, packageYamlPath);
|
|
167
|
-
if (pkgMeta?.slug && pkgMeta.slug !== slug) {
|
|
168
|
-
// Mismatch — clean up and abort
|
|
169
|
-
fs.rmSync(installDir, { recursive: true, force: true });
|
|
170
|
-
throw new Error(
|
|
171
|
-
`Package slug mismatch: expected "${slug}", got "${pkgMeta.slug}"`
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// 9. Get package info for registry entry
|
|
177
|
-
let version = 'unknown';
|
|
178
|
-
let name = slug;
|
|
179
|
-
try {
|
|
180
|
-
const pkgInfo = await client.getPackage(slug);
|
|
181
|
-
version = pkgInfo.package.latest_version;
|
|
182
|
-
name = pkgInfo.package.name;
|
|
183
|
-
} catch {
|
|
184
|
-
// Best-effort — fall back to package.yaml if present
|
|
185
|
-
const packageYaml = path.resolve(installDir, 'package.yaml');
|
|
186
|
-
if (fs.existsSync(packageYaml)) {
|
|
187
|
-
const content = fs.readFileSync(packageYaml, 'utf-8');
|
|
188
|
-
const meta = parseUserYaml<{
|
|
189
|
-
version?: string;
|
|
190
|
-
name?: string;
|
|
191
|
-
} | null>(content, packageYaml);
|
|
192
|
-
if (meta?.version) version = meta.version;
|
|
193
|
-
if (meta?.name) name = meta.name;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// 10. Update registry.yaml
|
|
198
|
-
const now = new Date().toISOString();
|
|
199
|
-
addToRegistry(hqRoot, {
|
|
200
|
-
name,
|
|
201
|
-
slug,
|
|
202
|
-
version,
|
|
203
|
-
source: registryUrl,
|
|
204
|
-
scope: company,
|
|
205
|
-
installed_at: now,
|
|
206
|
-
updated_at: now,
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
console.log(chalk.green(`\nInstalled ${slug}@${version} to packages/installed/${slug}/`));
|
|
210
|
-
console.log(
|
|
211
|
-
chalk.cyan(
|
|
212
|
-
'Run /package-install ' + slug + ' in Claude to merge into your HQ.'
|
|
213
|
-
)
|
|
214
|
-
);
|
|
215
|
-
} finally {
|
|
216
|
-
// Clean up temp file — never leave partial downloads
|
|
217
|
-
if (fs.existsSync(tmpFile)) {
|
|
218
|
-
fs.unlinkSync(tmpFile);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// @vitest-environment node
|
|
2
|
-
/**
|
|
3
|
-
* Regression: `hq packages list` must use the same filesystem-backed pack
|
|
4
|
-
* discovery as `hq packs list`, matching the live package install path.
|
|
5
|
-
*/
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as os from 'os';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
10
|
-
import { Command } from 'commander';
|
|
11
|
-
|
|
12
|
-
vi.mock('./pack-install.js', () => ({
|
|
13
|
-
classify: vi.fn(() => 'local'),
|
|
14
|
-
resolveLatest: vi.fn(),
|
|
15
|
-
resolveLatestMarketplace: vi.fn(),
|
|
16
|
-
runScanPackages: vi.fn(),
|
|
17
|
-
installPack: vi.fn(),
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
import { registerPackageListCommand } from './pkg-list.js';
|
|
21
|
-
import { registerPacksCommand } from './packs.js';
|
|
22
|
-
|
|
23
|
-
describe('hq packages list content-pack discovery', () => {
|
|
24
|
-
let hqRoot: string;
|
|
25
|
-
let previousCwd: string;
|
|
26
|
-
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'hq-pkg-list-'));
|
|
29
|
-
previousCwd = process.cwd();
|
|
30
|
-
fs.mkdirSync(path.join(hqRoot, '.claude'));
|
|
31
|
-
fs.mkdirSync(path.join(hqRoot, 'companies'));
|
|
32
|
-
fs.mkdirSync(path.join(hqRoot, 'core', 'packages', 'hq-pack-fixture'), {
|
|
33
|
-
recursive: true,
|
|
34
|
-
});
|
|
35
|
-
fs.writeFileSync(path.join(hqRoot, 'core', 'core.yaml'), 'hqVersion: 14.0.0\n');
|
|
36
|
-
fs.writeFileSync(
|
|
37
|
-
path.join(hqRoot, 'core', 'packages', 'hq-pack-fixture', 'package.yaml'),
|
|
38
|
-
'name: hq-pack-fixture\nversion: 1.2.3\n',
|
|
39
|
-
);
|
|
40
|
-
process.chdir(hqRoot);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
afterEach(() => {
|
|
44
|
-
process.chdir(previousCwd);
|
|
45
|
-
fs.rmSync(hqRoot, { recursive: true, force: true });
|
|
46
|
-
vi.restoreAllMocks();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
async function captureJson(register: (program: Command) => void, args: string[]) {
|
|
50
|
-
const stdout = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
51
|
-
const program = new Command();
|
|
52
|
-
program.exitOverride();
|
|
53
|
-
register(program);
|
|
54
|
-
await program.parseAsync(args, { from: 'user' });
|
|
55
|
-
const output = stdout.mock.calls.map((call) => String(call[0])).join('');
|
|
56
|
-
stdout.mockRestore();
|
|
57
|
-
return JSON.parse(output) as {
|
|
58
|
-
installed: Array<{ name: string; version?: string }>;
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
it('reports a pack installed under core/packages through both list commands', async () => {
|
|
63
|
-
const packagesView = await captureJson(
|
|
64
|
-
(program) => {
|
|
65
|
-
const packages = program.command('packages');
|
|
66
|
-
registerPackageListCommand(packages);
|
|
67
|
-
},
|
|
68
|
-
['packages', 'list', '--json'],
|
|
69
|
-
);
|
|
70
|
-
const packsView = await captureJson(registerPacksCommand, ['packs', 'list', '--json']);
|
|
71
|
-
|
|
72
|
-
expect(packagesView.installed).toContainEqual(
|
|
73
|
-
expect.objectContaining({ name: 'hq-pack-fixture', version: '1.2.3' }),
|
|
74
|
-
);
|
|
75
|
-
expect(packsView.installed).toContainEqual(
|
|
76
|
-
expect.objectContaining({ name: 'hq-pack-fixture', version: '1.2.3' }),
|
|
77
|
-
);
|
|
78
|
-
});
|
|
79
|
-
});
|
package/src/commands/pkg-list.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hq packages list — list filesystem-backed content packs.
|
|
3
|
-
*
|
|
4
|
-
* Package installs use the content-pack installer, so this command delegates
|
|
5
|
-
* to the same list view as `hq packs list`.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { Command } from 'commander';
|
|
9
|
-
import { registerPacksListCommand } from './packs.js';
|
|
10
|
-
|
|
11
|
-
export function registerPackageListCommand(parent: Command): void {
|
|
12
|
-
registerPacksListCommand(parent);
|
|
13
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hq packages remove <slug> — archive and unregister a package (US-005)
|
|
3
|
-
*
|
|
4
|
-
* 1. Archive packages/installed/<slug>/ to packages/.archive/<slug>-<timestamp>/
|
|
5
|
-
* 2. Remove entry from packages/registry.yaml
|
|
6
|
-
* 3. Print next-step message
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import * as fs from 'fs';
|
|
10
|
-
import * as path from 'path';
|
|
11
|
-
import { Command } from 'commander';
|
|
12
|
-
import chalk from 'chalk';
|
|
13
|
-
import { resolveDefaultHqRoot } from '../utils/cognito-session.js';
|
|
14
|
-
import { removeFromRegistry, readRegistry } from '../utils/registry.js';
|
|
15
|
-
|
|
16
|
-
export function registerPackageRemoveCommand(parent: Command): void {
|
|
17
|
-
parent
|
|
18
|
-
.command('remove <slug>')
|
|
19
|
-
.description('Remove an installed package (archives it first)')
|
|
20
|
-
.action(async (slug: string) => {
|
|
21
|
-
try {
|
|
22
|
-
await removePackage(slug);
|
|
23
|
-
} catch (error) {
|
|
24
|
-
console.error(
|
|
25
|
-
chalk.red('Remove failed:'),
|
|
26
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
27
|
-
);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function removePackage(slug: string): Promise<void> {
|
|
34
|
-
const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
|
|
35
|
-
const installDir = path.resolve(hqRoot, 'packages', 'installed', slug);
|
|
36
|
-
|
|
37
|
-
// Verify it is actually installed
|
|
38
|
-
const entries = readRegistry(hqRoot);
|
|
39
|
-
const entry = entries.find((e) => e.slug === slug);
|
|
40
|
-
if (!entry && !fs.existsSync(installDir)) {
|
|
41
|
-
throw new Error(`Package "${slug}" is not installed.`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// 1. Archive
|
|
45
|
-
if (fs.existsSync(installDir)) {
|
|
46
|
-
const timestamp = new Date()
|
|
47
|
-
.toISOString()
|
|
48
|
-
.replace(/[:.]/g, '-')
|
|
49
|
-
.replace('T', '_')
|
|
50
|
-
.slice(0, 19);
|
|
51
|
-
const archiveDir = path.resolve(
|
|
52
|
-
hqRoot,
|
|
53
|
-
'packages',
|
|
54
|
-
'.archive',
|
|
55
|
-
`${slug}-${timestamp}`
|
|
56
|
-
);
|
|
57
|
-
fs.mkdirSync(path.dirname(archiveDir), { recursive: true });
|
|
58
|
-
fs.renameSync(installDir, archiveDir);
|
|
59
|
-
console.log(chalk.dim(`Archived to packages/.archive/${slug}-${timestamp}/`));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 2. Remove from registry.yaml
|
|
63
|
-
removeFromRegistry(hqRoot, slug);
|
|
64
|
-
|
|
65
|
-
console.log(chalk.green(`\nRemoved ${slug}.`));
|
|
66
|
-
console.log(
|
|
67
|
-
chalk.cyan(
|
|
68
|
-
'Run /package-remove ' + slug + ' in Claude to clean up merged content.'
|
|
69
|
-
)
|
|
70
|
-
);
|
|
71
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hq packages update [slug] — check for and apply package updates (US-005)
|
|
3
|
-
*
|
|
4
|
-
* If no slug: check all installed packages for updates.
|
|
5
|
-
* If slug given: update only that package.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from 'fs';
|
|
9
|
-
import * as os from 'os';
|
|
10
|
-
import * as path from 'path';
|
|
11
|
-
import { parseUserYaml } from '../utils/user-yaml-error.js';
|
|
12
|
-
import { execSync } from 'child_process';
|
|
13
|
-
import { Command } from 'commander';
|
|
14
|
-
import chalk from 'chalk';
|
|
15
|
-
import { ensureCognitoToken } from '../utils/cognito-session.js';
|
|
16
|
-
import { resolveDefaultHqRoot } from '../utils/cognito-session.js';
|
|
17
|
-
import {
|
|
18
|
-
getRegistryUrl,
|
|
19
|
-
RegistryClient,
|
|
20
|
-
} from '../utils/registry-client.js';
|
|
21
|
-
import { verifySha256, verifyRsaSignature } from '../utils/integrity.js';
|
|
22
|
-
import {
|
|
23
|
-
readRegistry,
|
|
24
|
-
addToRegistry,
|
|
25
|
-
type RegistryEntry,
|
|
26
|
-
} from '../utils/registry.js';
|
|
27
|
-
|
|
28
|
-
export function registerPackageUpdateCommand(parent: Command): void {
|
|
29
|
-
parent
|
|
30
|
-
.command('update [slug]')
|
|
31
|
-
.description('Check for and apply package updates')
|
|
32
|
-
.action(async (slug?: string) => {
|
|
33
|
-
try {
|
|
34
|
-
await updatePackages(slug);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
console.error(
|
|
37
|
-
chalk.red('Update failed:'),
|
|
38
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
39
|
-
);
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function updatePackages(slug?: string): Promise<void> {
|
|
46
|
-
const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
|
|
47
|
-
const entries = readRegistry(hqRoot);
|
|
48
|
-
|
|
49
|
-
if (entries.length === 0) {
|
|
50
|
-
console.log('No packages installed. Use "hq packages install <slug>" to install one.');
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const accessToken = await ensureCognitoToken();
|
|
55
|
-
const registryUrl = getRegistryUrl();
|
|
56
|
-
const client = new RegistryClient(registryUrl, accessToken);
|
|
57
|
-
|
|
58
|
-
const toCheck = slug
|
|
59
|
-
? entries.filter((e) => e.slug === slug)
|
|
60
|
-
: entries;
|
|
61
|
-
|
|
62
|
-
if (slug && toCheck.length === 0) {
|
|
63
|
-
throw new Error(`Package "${slug}" is not installed.`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let updatedCount = 0;
|
|
67
|
-
|
|
68
|
-
for (const entry of toCheck) {
|
|
69
|
-
try {
|
|
70
|
-
const pkgInfo = await client.getPackage(entry.slug);
|
|
71
|
-
const latestVersion = pkgInfo.package.latest_version;
|
|
72
|
-
|
|
73
|
-
if (latestVersion === entry.version) {
|
|
74
|
-
console.log(chalk.dim(`${entry.slug}@${entry.version} — up to date`));
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
console.log(
|
|
79
|
-
chalk.yellow(
|
|
80
|
-
`${entry.slug}: ${entry.version} → ${latestVersion}`
|
|
81
|
-
)
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
// Download new version
|
|
85
|
-
const download = await client.getDownloadUrl(
|
|
86
|
-
entry.slug,
|
|
87
|
-
latestVersion
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const tmpFile = path.resolve(
|
|
91
|
-
os.tmpdir(),
|
|
92
|
-
`hq-pkg-${entry.slug}-${Date.now()}.tar.gz`
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
const response = await fetch(download.url, {
|
|
97
|
-
signal: AbortSignal.timeout(120_000),
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
if (!response.ok) {
|
|
101
|
-
throw new Error(`Download failed (${response.status})`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
105
|
-
fs.writeFileSync(tmpFile, buffer);
|
|
106
|
-
|
|
107
|
-
// Verify SHA256
|
|
108
|
-
const hashValid = await verifySha256(tmpFile, download.sha256);
|
|
109
|
-
if (!hashValid) {
|
|
110
|
-
throw new Error('SHA256 hash mismatch');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Verify RSA signature (if provided)
|
|
114
|
-
if (download.signature) {
|
|
115
|
-
const sigValid = verifyRsaSignature(
|
|
116
|
-
download.sha256,
|
|
117
|
-
download.signature
|
|
118
|
-
);
|
|
119
|
-
if (!sigValid) {
|
|
120
|
-
console.warn(
|
|
121
|
-
chalk.yellow(
|
|
122
|
-
` Warning: RSA signature verification failed for ${entry.slug}. Proceeding with SHA256-only.`
|
|
123
|
-
)
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Extract over existing
|
|
129
|
-
const installDir = path.resolve(
|
|
130
|
-
hqRoot,
|
|
131
|
-
'packages',
|
|
132
|
-
'installed',
|
|
133
|
-
entry.slug
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
if (fs.existsSync(installDir)) {
|
|
137
|
-
fs.rmSync(installDir, { recursive: true, force: true });
|
|
138
|
-
}
|
|
139
|
-
fs.mkdirSync(installDir, { recursive: true });
|
|
140
|
-
|
|
141
|
-
execSync(`tar -xzf "${tmpFile}" -C "${installDir}"`, {
|
|
142
|
-
stdio: 'pipe',
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Validate package.yaml slug
|
|
146
|
-
const packageYamlPath = path.resolve(installDir, 'package.yaml');
|
|
147
|
-
if (fs.existsSync(packageYamlPath)) {
|
|
148
|
-
const pkgContent = fs.readFileSync(packageYamlPath, 'utf-8');
|
|
149
|
-
const pkgMeta = parseUserYaml<{
|
|
150
|
-
slug?: string;
|
|
151
|
-
} | null>(pkgContent, packageYamlPath);
|
|
152
|
-
if (pkgMeta?.slug && pkgMeta.slug !== entry.slug) {
|
|
153
|
-
fs.rmSync(installDir, { recursive: true, force: true });
|
|
154
|
-
throw new Error(
|
|
155
|
-
`Package slug mismatch: expected "${entry.slug}", got "${pkgMeta.slug}"`
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Update registry entry — preserve scope
|
|
161
|
-
const now = new Date().toISOString();
|
|
162
|
-
const updated: RegistryEntry = {
|
|
163
|
-
...entry,
|
|
164
|
-
version: latestVersion,
|
|
165
|
-
updated_at: now,
|
|
166
|
-
};
|
|
167
|
-
addToRegistry(hqRoot, updated);
|
|
168
|
-
|
|
169
|
-
console.log(
|
|
170
|
-
chalk.green(` Updated ${entry.slug} to ${latestVersion}`)
|
|
171
|
-
);
|
|
172
|
-
updatedCount++;
|
|
173
|
-
} finally {
|
|
174
|
-
if (fs.existsSync(tmpFile)) {
|
|
175
|
-
fs.unlinkSync(tmpFile);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
} catch (error) {
|
|
179
|
-
console.error(
|
|
180
|
-
chalk.red(` Error updating ${entry.slug}:`),
|
|
181
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
console.log(
|
|
187
|
-
`\n${updatedCount} package(s) updated${updatedCount > 0 ? '.' : ' — everything is current.'}`
|
|
188
|
-
);
|
|
189
|
-
}
|