@indigoai-us/hq-cli 5.47.13 → 5.47.15
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/dist/commands/api-keys.d.ts +3 -0
- package/dist/commands/api-keys.js +198 -0
- package/dist/commands/pack-install.d.ts +14 -0
- package/dist/commands/pack-install.js +76 -2
- package/dist/commands/packs.d.ts +32 -0
- package/dist/commands/packs.js +20 -3
- package/dist/index.js +5 -2
- package/dist/types.d.ts +10 -0
- package/package.json +1 -1
- package/src/commands/api-keys.test.ts +217 -0
- package/src/commands/api-keys.ts +306 -0
- package/src/commands/pack-install.test.ts +213 -0
- package/src/commands/pack-install.ts +82 -0
- package/src/commands/packs.test.ts +88 -0
- package/src/commands/packs.ts +24 -1
- package/src/index.ts +4 -0
- package/src/schemas/hq-package.schema.json +18 -0
- package/src/types.ts +7 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c9f6fe34-9e38-5001-97b7-14b35c956330")}catch(e){}}();
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
5
|
+
import { getCompanyUid, vaultApiFetch } from "./secrets.js";
|
|
6
|
+
function collectRepeatedOption(value, previous) {
|
|
7
|
+
return [...previous, value];
|
|
8
|
+
}
|
|
9
|
+
function formatMaybe(value) {
|
|
10
|
+
return value ?? "-";
|
|
11
|
+
}
|
|
12
|
+
function formatPrefixes(prefixes) {
|
|
13
|
+
return prefixes.length > 0 ? prefixes.join(", ") : "-";
|
|
14
|
+
}
|
|
15
|
+
function parsePermission(value) {
|
|
16
|
+
if (value === "read" || value === "write" || value === "admin") {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
throw new Error(`Invalid permission '${value}'. Use one of: read, write, admin.`);
|
|
20
|
+
}
|
|
21
|
+
function parseExpires(expires) {
|
|
22
|
+
if (expires === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (Number.isNaN(Date.parse(expires))) {
|
|
25
|
+
throw new Error(`Invalid --expires value '${expires}'. Use an ISO-8601 timestamp.`);
|
|
26
|
+
}
|
|
27
|
+
return expires;
|
|
28
|
+
}
|
|
29
|
+
async function readApiError(res, action) {
|
|
30
|
+
const body = (await res.json().catch(() => ({})));
|
|
31
|
+
const message = typeof body.message === "string"
|
|
32
|
+
? body.message
|
|
33
|
+
: typeof body.error === "string"
|
|
34
|
+
? body.error
|
|
35
|
+
: res.statusText;
|
|
36
|
+
if (res.status === 401) {
|
|
37
|
+
return "Not authenticated - please run `hq login`";
|
|
38
|
+
}
|
|
39
|
+
if (res.status === 403) {
|
|
40
|
+
return `Not authorized to ${action}`;
|
|
41
|
+
}
|
|
42
|
+
if (res.status === 404) {
|
|
43
|
+
return message || "API key not found";
|
|
44
|
+
}
|
|
45
|
+
if (res.status >= 500) {
|
|
46
|
+
return `Server error: ${message}`;
|
|
47
|
+
}
|
|
48
|
+
return message || `Request failed (${res.status})`;
|
|
49
|
+
}
|
|
50
|
+
function renderApiKeysTable(apiKeys) {
|
|
51
|
+
const rows = apiKeys.map((apiKey) => ({
|
|
52
|
+
keyId: apiKey.keyId,
|
|
53
|
+
name: apiKey.name,
|
|
54
|
+
permission: apiKey.scope.permission,
|
|
55
|
+
prefixes: formatPrefixes(apiKey.scope.allowedPrefixes),
|
|
56
|
+
status: apiKey.status,
|
|
57
|
+
lastUsedAt: formatMaybe(apiKey.lastUsedAt),
|
|
58
|
+
expiresAt: formatMaybe(apiKey.expiresAt),
|
|
59
|
+
}));
|
|
60
|
+
const keyIdWidth = Math.max(6, ...rows.map((row) => row.keyId.length));
|
|
61
|
+
const nameWidth = Math.max(4, ...rows.map((row) => row.name.length));
|
|
62
|
+
const permissionWidth = Math.max(10, ...rows.map((row) => row.permission.length));
|
|
63
|
+
const prefixesWidth = Math.max(8, ...rows.map((row) => row.prefixes.length));
|
|
64
|
+
const statusWidth = Math.max(6, ...rows.map((row) => row.status.length));
|
|
65
|
+
const lastUsedWidth = Math.max(11, ...rows.map((row) => row.lastUsedAt.length));
|
|
66
|
+
const expiresWidth = Math.max(10, ...rows.map((row) => row.expiresAt.length));
|
|
67
|
+
const header = [
|
|
68
|
+
"KEY ID".padEnd(keyIdWidth),
|
|
69
|
+
"NAME".padEnd(nameWidth),
|
|
70
|
+
"PERMISSION".padEnd(permissionWidth),
|
|
71
|
+
"PREFIXES".padEnd(prefixesWidth),
|
|
72
|
+
"STATUS".padEnd(statusWidth),
|
|
73
|
+
"LAST USED".padEnd(lastUsedWidth),
|
|
74
|
+
"EXPIRES".padEnd(expiresWidth),
|
|
75
|
+
].join(" ");
|
|
76
|
+
console.log(chalk.bold(header));
|
|
77
|
+
for (const row of rows) {
|
|
78
|
+
console.log([
|
|
79
|
+
row.keyId.padEnd(keyIdWidth),
|
|
80
|
+
row.name.padEnd(nameWidth),
|
|
81
|
+
row.permission.padEnd(permissionWidth),
|
|
82
|
+
row.prefixes.padEnd(prefixesWidth),
|
|
83
|
+
row.status.padEnd(statusWidth),
|
|
84
|
+
row.lastUsedAt.padEnd(lastUsedWidth),
|
|
85
|
+
row.expiresAt.padEnd(expiresWidth),
|
|
86
|
+
].join(" "));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function registerApiKeysCommand(program) {
|
|
90
|
+
const apiKeys = program
|
|
91
|
+
.command("api-keys")
|
|
92
|
+
.description("Manage vault API keys for CI and automation")
|
|
93
|
+
.option("--company <slug>", "Company slug (resolves to companyUid)");
|
|
94
|
+
apiKeys
|
|
95
|
+
.command("create")
|
|
96
|
+
.description("Create a new API key")
|
|
97
|
+
.requiredOption("--name <label>", "Human-readable label for the API key")
|
|
98
|
+
.option("--scope <prefix>", "Allowed prefix (repeatable)", collectRepeatedOption, [])
|
|
99
|
+
.option("--permission <level>", "Permission level: read | write | admin", "read")
|
|
100
|
+
.option("--expires <ISO8601>", "Optional ISO-8601 expiry timestamp")
|
|
101
|
+
.action(async (opts) => {
|
|
102
|
+
try {
|
|
103
|
+
if (opts.scope.length === 0) {
|
|
104
|
+
console.error(chalk.red("Error: at least one --scope <prefix> is required."));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
const permission = parsePermission(opts.permission);
|
|
108
|
+
const expiresAt = parseExpires(opts.expires);
|
|
109
|
+
const token = await ensureCognitoToken();
|
|
110
|
+
const companyUid = await getCompanyUid(token, apiKeys.opts().company);
|
|
111
|
+
const res = await vaultApiFetch({
|
|
112
|
+
token,
|
|
113
|
+
path: "/v1/api-keys",
|
|
114
|
+
method: "POST",
|
|
115
|
+
body: {
|
|
116
|
+
companyUid,
|
|
117
|
+
name: opts.name,
|
|
118
|
+
allowedPrefixes: opts.scope,
|
|
119
|
+
permission,
|
|
120
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
if (!res.ok) {
|
|
124
|
+
throw new Error(await readApiError(res, "create API keys"));
|
|
125
|
+
}
|
|
126
|
+
const data = (await res.json());
|
|
127
|
+
console.log(chalk.green("API key created."));
|
|
128
|
+
console.log(chalk.yellow("Store this key now - it won't be shown again:"));
|
|
129
|
+
console.log(`\n ${data.key.value}\n`);
|
|
130
|
+
console.log(chalk.bold("Metadata"));
|
|
131
|
+
console.log(` Key ID: ${data.apiKey.keyId}`);
|
|
132
|
+
console.log(` Name: ${data.apiKey.name}`);
|
|
133
|
+
console.log(` Company: ${data.apiKey.companyUid}`);
|
|
134
|
+
console.log(` Permission: ${data.apiKey.scope.permission}`);
|
|
135
|
+
console.log(` Prefixes: ${formatPrefixes(data.apiKey.scope.allowedPrefixes)}`);
|
|
136
|
+
console.log(` Status: ${data.apiKey.status}`);
|
|
137
|
+
console.log(` Created: ${data.apiKey.createdAt}`);
|
|
138
|
+
console.log(` Last used: ${formatMaybe(data.apiKey.lastUsedAt)}`);
|
|
139
|
+
console.log(` Expires: ${formatMaybe(data.apiKey.expiresAt)}`);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
apiKeys
|
|
147
|
+
.command("list")
|
|
148
|
+
.description("List API keys for a company")
|
|
149
|
+
.action(async () => {
|
|
150
|
+
try {
|
|
151
|
+
const token = await ensureCognitoToken();
|
|
152
|
+
const companyUid = await getCompanyUid(token, apiKeys.opts().company);
|
|
153
|
+
const res = await vaultApiFetch({
|
|
154
|
+
token,
|
|
155
|
+
path: "/v1/api-keys",
|
|
156
|
+
query: { companyUid },
|
|
157
|
+
});
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
throw new Error(await readApiError(res, "list API keys"));
|
|
160
|
+
}
|
|
161
|
+
const data = (await res.json());
|
|
162
|
+
if (data.apiKeys.length === 0) {
|
|
163
|
+
console.log(chalk.dim("No API keys found."));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
renderApiKeysTable(data.apiKeys);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
apiKeys
|
|
174
|
+
.command("revoke <keyId>")
|
|
175
|
+
.description("Revoke an API key")
|
|
176
|
+
.action(async (keyId) => {
|
|
177
|
+
try {
|
|
178
|
+
const token = await ensureCognitoToken();
|
|
179
|
+
const res = await vaultApiFetch({
|
|
180
|
+
token,
|
|
181
|
+
path: `/v1/api-keys/${encodeURIComponent(keyId)}/revoke`,
|
|
182
|
+
method: "POST",
|
|
183
|
+
});
|
|
184
|
+
if (!res.ok) {
|
|
185
|
+
throw new Error(await readApiError(res, "revoke this API key"));
|
|
186
|
+
}
|
|
187
|
+
const data = (await res.json());
|
|
188
|
+
console.log(chalk.green(`Revoked API key '${data.apiKey.keyId}'`));
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
return apiKeys;
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=api-keys.js.map
|
|
198
|
+
//# debugId=c9f6fe34-9e38-5001-97b7-14b35c956330
|
|
@@ -238,6 +238,20 @@ export declare function computeArtifactHash(tarballBytes: Uint8Array): string;
|
|
|
238
238
|
*/
|
|
239
239
|
export declare function verifyArtifact(input: VerifyArtifactInput): void;
|
|
240
240
|
export declare function validateManifest(payloadDir: string, hqVersion: string | null): PackManifest;
|
|
241
|
+
/**
|
|
242
|
+
* Derive the safe, auto-generated "get started" line for a freshly installed
|
|
243
|
+
* pack from its `initialization.entrypoint` ONLY. PHASE 1 deliberately ignores
|
|
244
|
+
* the free-text `initialization.prompt` prose (rendering/moderation is a later
|
|
245
|
+
* story) so untrusted prose can't reach the operator's terminal.
|
|
246
|
+
*
|
|
247
|
+
* The command is slash-normalized to exactly one leading slash regardless of
|
|
248
|
+
* whether `entrypoint` was stored with or without one, matching the HQ Sync
|
|
249
|
+
* desktop render: ``Run `/email-assistant` to get started``.
|
|
250
|
+
*
|
|
251
|
+
* Returns `null` when there is no initialization block (backwards-compatible —
|
|
252
|
+
* the caller prints nothing extra).
|
|
253
|
+
*/
|
|
254
|
+
export declare function getStartedLine(initialization?: PackManifest['initialization']): string | null;
|
|
241
255
|
/**
|
|
242
256
|
* Install the fetched payload to `<hqRoot>/core/packages/<pkg.name>/` (HQ
|
|
243
257
|
* v12+ layout). The HQ template (`hq-core` / `hq-core-staging`) ships
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* from each pack's package.yaml; rationale lives in the layout-fix PR.)
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
37
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="cb8b5583-b60f-52a5-802d-befc33f2a07b")}catch(e){}}();
|
|
38
38
|
import * as fs from 'fs';
|
|
39
39
|
import * as os from 'os';
|
|
40
40
|
import * as path from 'path';
|
|
@@ -807,9 +807,75 @@ export function validateManifest(payloadDir, hqVersion) {
|
|
|
807
807
|
throw new Error('capabilities must be a list of strings');
|
|
808
808
|
}
|
|
809
809
|
}
|
|
810
|
+
// initialization (US-004) — OPTIONAL and backwards-compatible. Absent → fine
|
|
811
|
+
// (legacy packs). Present → the `entrypoint` is REQUIRED and MUST resolve to a
|
|
812
|
+
// declared `contributes.skills` or `contributes.commands` entry (this is the
|
|
813
|
+
// content-pack system, so entries are named under `contributes.*` — NOT the
|
|
814
|
+
// registry `exposes.*` system). The post-install initialization prompt is
|
|
815
|
+
// rendered/moderated in a later story; here we only validate shape so a
|
|
816
|
+
// malformed block can't slip through to install.
|
|
817
|
+
if (m.initialization !== undefined) {
|
|
818
|
+
const init = m.initialization;
|
|
819
|
+
if (!init || typeof init !== 'object' || Array.isArray(init)) {
|
|
820
|
+
throw new Error('initialization must be a mapping with an entrypoint');
|
|
821
|
+
}
|
|
822
|
+
const initObj = init;
|
|
823
|
+
const entrypoint = initObj.entrypoint;
|
|
824
|
+
if (typeof entrypoint !== 'string' || entrypoint.trim() === '') {
|
|
825
|
+
throw new Error('initialization.entrypoint is required and must be a non-empty string');
|
|
826
|
+
}
|
|
827
|
+
// Resolve the entrypoint against declared skills/commands. Normalize a
|
|
828
|
+
// leading slash on BOTH sides so `/email-assistant` matches a contributes
|
|
829
|
+
// entry named `email-assistant` and vice-versa.
|
|
830
|
+
const stripSlash = (s) => (s.startsWith('/') ? s.slice(1) : s);
|
|
831
|
+
const target = stripSlash(entrypoint.trim());
|
|
832
|
+
const declared = [
|
|
833
|
+
...(contributes.skills ?? []),
|
|
834
|
+
...(contributes.commands ?? []),
|
|
835
|
+
];
|
|
836
|
+
const resolves = declared.some((d) => stripSlash(d) === target);
|
|
837
|
+
if (!resolves) {
|
|
838
|
+
throw new Error(`initialization.entrypoint "${entrypoint}" does not resolve to a declared ` +
|
|
839
|
+
`contributes.skills or contributes.commands entry. ` +
|
|
840
|
+
`Valid entries: ${declared.length ? declared.join(', ') : '(none declared)'}`);
|
|
841
|
+
}
|
|
842
|
+
// initialization.prompt — OPTIONAL. When present it must be a string ≤ 2000
|
|
843
|
+
// chars. (Rendering/moderation is a later story; we only validate type/length.)
|
|
844
|
+
if (initObj.prompt !== undefined) {
|
|
845
|
+
if (typeof initObj.prompt !== 'string') {
|
|
846
|
+
throw new Error('initialization.prompt must be a string');
|
|
847
|
+
}
|
|
848
|
+
if (initObj.prompt.length > 2000) {
|
|
849
|
+
throw new Error(`initialization.prompt must be ≤ 2000 characters (got ${initObj.prompt.length})`);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
810
853
|
return m;
|
|
811
854
|
}
|
|
812
855
|
// ---------------------------------------------------------------------------
|
|
856
|
+
// Post-install get-started line (US-005)
|
|
857
|
+
// ---------------------------------------------------------------------------
|
|
858
|
+
/**
|
|
859
|
+
* Derive the safe, auto-generated "get started" line for a freshly installed
|
|
860
|
+
* pack from its `initialization.entrypoint` ONLY. PHASE 1 deliberately ignores
|
|
861
|
+
* the free-text `initialization.prompt` prose (rendering/moderation is a later
|
|
862
|
+
* story) so untrusted prose can't reach the operator's terminal.
|
|
863
|
+
*
|
|
864
|
+
* The command is slash-normalized to exactly one leading slash regardless of
|
|
865
|
+
* whether `entrypoint` was stored with or without one, matching the HQ Sync
|
|
866
|
+
* desktop render: ``Run `/email-assistant` to get started``.
|
|
867
|
+
*
|
|
868
|
+
* Returns `null` when there is no initialization block (backwards-compatible —
|
|
869
|
+
* the caller prints nothing extra).
|
|
870
|
+
*/
|
|
871
|
+
export function getStartedLine(initialization) {
|
|
872
|
+
const entrypoint = initialization?.entrypoint;
|
|
873
|
+
if (typeof entrypoint !== 'string' || entrypoint.trim() === '')
|
|
874
|
+
return null;
|
|
875
|
+
const command = '/' + entrypoint.trim().replace(/^\/+/, '');
|
|
876
|
+
return `Run \`${command}\` to get started`;
|
|
877
|
+
}
|
|
878
|
+
// ---------------------------------------------------------------------------
|
|
813
879
|
// Hooks confirmation
|
|
814
880
|
// ---------------------------------------------------------------------------
|
|
815
881
|
async function confirmHooks(pkg, allowHooks) {
|
|
@@ -1070,10 +1136,18 @@ export async function installPack(source, opts = {}) {
|
|
|
1070
1136
|
say(chalk.green(`\nOK Installed ${pkg.name}@${pkg.version} -> ${path.relative(hqRoot, destDir)}/`));
|
|
1071
1137
|
say(chalk.dim(` Wired ${Object.values(pkg.contributes).flat().filter(Boolean).length} ` +
|
|
1072
1138
|
`contribution(s) into host-side paths.`));
|
|
1139
|
+
// US-005 — when the pack declares an `initialization` block, print a safe,
|
|
1140
|
+
// auto-generated "get started" line right after the success output. PHASE 1
|
|
1141
|
+
// derives the line from `initialization.entrypoint` ONLY (never the
|
|
1142
|
+
// free-text `initialization.prompt` prose), and matches the HQ Sync desktop
|
|
1143
|
+
// wording for consistency. Absent block → nothing extra (backwards-compat).
|
|
1144
|
+
const getStarted = getStartedLine(pkg.initialization);
|
|
1145
|
+
if (getStarted)
|
|
1146
|
+
say(chalk.cyan(getStarted));
|
|
1073
1147
|
}
|
|
1074
1148
|
finally {
|
|
1075
1149
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
1076
1150
|
}
|
|
1077
1151
|
}
|
|
1078
1152
|
//# sourceMappingURL=pack-install.js.map
|
|
1079
|
-
//# debugId=
|
|
1153
|
+
//# debugId=cb8b5583-b60f-52a5-802d-befc33f2a07b
|
package/dist/commands/packs.d.ts
CHANGED
|
@@ -18,5 +18,37 @@
|
|
|
18
18
|
* Spec: knowledge/public/hq-core/package-yaml-spec.md.
|
|
19
19
|
*/
|
|
20
20
|
import { Command } from 'commander';
|
|
21
|
+
import { type InstalledPack, type LinkStatus } from '../utils/pack-contributions.js';
|
|
22
|
+
import type { PackContributeKey } from '../types.js';
|
|
23
|
+
interface InstalledPackView {
|
|
24
|
+
name: string;
|
|
25
|
+
version?: string;
|
|
26
|
+
publisher?: string;
|
|
27
|
+
source?: string;
|
|
28
|
+
transport: string | null;
|
|
29
|
+
requiresHqCore?: string;
|
|
30
|
+
hqCoreSatisfied: boolean | null;
|
|
31
|
+
contributes: Partial<Record<PackContributeKey, number>>;
|
|
32
|
+
links: Record<LinkStatus, number>;
|
|
33
|
+
brokenLinks: Array<{
|
|
34
|
+
key: PackContributeKey;
|
|
35
|
+
item: string;
|
|
36
|
+
dst: string;
|
|
37
|
+
}>;
|
|
38
|
+
inCatalog: boolean;
|
|
39
|
+
updateAvailable: boolean | null;
|
|
40
|
+
/**
|
|
41
|
+
* Post-install initialization (US-005). Present only when the pack's
|
|
42
|
+
* package.yaml declares an `initialization` block — drives the HQ Sync
|
|
43
|
+
* "Installed" panel get-started affordance. Absent → omitted (no null noise).
|
|
44
|
+
*/
|
|
45
|
+
initialization?: {
|
|
46
|
+
entrypoint: string;
|
|
47
|
+
prompt?: string;
|
|
48
|
+
};
|
|
49
|
+
error?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function buildInstalledView(hqRoot: string, hqVersion: string | null, pack: InstalledPack, installedSources: Set<string>, checkUpdates: boolean): InstalledPackView;
|
|
21
52
|
export declare function registerPacksCommand(parent: Command): void;
|
|
53
|
+
export {};
|
|
22
54
|
//# sourceMappingURL=packs.d.ts.map
|
package/dist/commands/packs.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* Spec: knowledge/public/hq-core/package-yaml-spec.md.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
21
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="527d0e7d-2101-5438-8251-2579a40453e6")}catch(e){}}();
|
|
22
22
|
import * as fs from 'fs';
|
|
23
23
|
import * as path from 'path';
|
|
24
24
|
import * as readline from 'readline';
|
|
@@ -61,7 +61,7 @@ async function confirm(question) {
|
|
|
61
61
|
});
|
|
62
62
|
return /^(y|yes)$/i.test(answer.trim());
|
|
63
63
|
}
|
|
64
|
-
function buildInstalledView(hqRoot, hqVersion, pack, installedSources, checkUpdates) {
|
|
64
|
+
export function buildInstalledView(hqRoot, hqVersion, pack, installedSources, checkUpdates) {
|
|
65
65
|
if (!pack.manifest) {
|
|
66
66
|
return {
|
|
67
67
|
name: pack.name,
|
|
@@ -97,6 +97,22 @@ function buildInstalledView(hqRoot, hqVersion, pack, installedSources, checkUpda
|
|
|
97
97
|
if (checkUpdates && m.source) {
|
|
98
98
|
updateAvailable = resolveLatest(m.source, m.version).updateAvailable;
|
|
99
99
|
}
|
|
100
|
+
// US-005 — surface the pack's `initialization` block so the HQ Sync
|
|
101
|
+
// "Installed" panel can render its get-started affordance. `readPackManifest`
|
|
102
|
+
// already parses the full package.yaml, so `m.initialization` is available;
|
|
103
|
+
// we still shape it defensively (tolerate a malformed/absent block) and omit
|
|
104
|
+
// the field entirely when absent so the JSON carries no null noise.
|
|
105
|
+
let initialization;
|
|
106
|
+
const rawInit = m.initialization;
|
|
107
|
+
if (rawInit && typeof rawInit === 'object' && !Array.isArray(rawInit)) {
|
|
108
|
+
const initObj = rawInit;
|
|
109
|
+
const entrypoint = initObj.entrypoint;
|
|
110
|
+
if (typeof entrypoint === 'string' && entrypoint.trim() !== '') {
|
|
111
|
+
initialization = { entrypoint };
|
|
112
|
+
if (typeof initObj.prompt === 'string')
|
|
113
|
+
initialization.prompt = initObj.prompt;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
100
116
|
return {
|
|
101
117
|
name: m.name ?? pack.name,
|
|
102
118
|
version: m.version,
|
|
@@ -110,6 +126,7 @@ function buildInstalledView(hqRoot, hqVersion, pack, installedSources, checkUpda
|
|
|
110
126
|
brokenLinks,
|
|
111
127
|
inCatalog: m.source ? installedSources.has(m.source) : false,
|
|
112
128
|
updateAvailable,
|
|
129
|
+
...(initialization ? { initialization } : {}),
|
|
113
130
|
};
|
|
114
131
|
}
|
|
115
132
|
function buildListView(hqRoot, checkUpdates, evalConditionals) {
|
|
@@ -408,4 +425,4 @@ export function registerPacksCommand(parent) {
|
|
|
408
425
|
});
|
|
409
426
|
}
|
|
410
427
|
//# sourceMappingURL=packs.js.map
|
|
411
|
-
//# debugId=
|
|
428
|
+
//# debugId=527d0e7d-2101-5438-8251-2579a40453e6
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* HQ CLI - Module management, package management, and cloud sync for HQ
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
6
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="964b8c71-056c-51fb-893a-d51f8f6ad5d9")}catch(e){}}();
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import { initSentry, Sentry } from "./sentry.js";
|
|
9
9
|
import { registerAddCommand } from "./commands/add.js";
|
|
@@ -28,6 +28,7 @@ import { registerPublishCommand } from "./commands/publish.js";
|
|
|
28
28
|
import { registerCreatorsCommand } from "./commands/creators.js";
|
|
29
29
|
import { registerTeamSyncCommand } from "./commands/team-sync.js";
|
|
30
30
|
import { registerAuthCommands } from "./commands/auth.js";
|
|
31
|
+
import { registerApiKeysCommand } from "./commands/api-keys.js";
|
|
31
32
|
import { registerSecretsCommand } from "./commands/secrets.js";
|
|
32
33
|
import { registerRunCommand } from "./commands/run.js";
|
|
33
34
|
import { registerGroupsCommand } from "./commands/groups.js";
|
|
@@ -117,6 +118,8 @@ registerWhoamiCommand(program);
|
|
|
117
118
|
registerAuthCommands(program);
|
|
118
119
|
// Secrets management (subcommand group — hq secrets set|get|list|delete|exec|generate-link|cache)
|
|
119
120
|
registerSecretsCommand(program);
|
|
121
|
+
// API key management (subcommand group — hq api-keys create|list|revoke)
|
|
122
|
+
registerApiKeysCommand(program);
|
|
120
123
|
// Schema-driven dev runner — hq run [options] -- <cmd>
|
|
121
124
|
registerRunCommand(program);
|
|
122
125
|
// Groups management (subcommand group — hq groups create|delete|add|remove|list|members)
|
|
@@ -177,4 +180,4 @@ registerRescueCommand(program);
|
|
|
177
180
|
}
|
|
178
181
|
})();
|
|
179
182
|
//# sourceMappingURL=index.js.map
|
|
180
|
-
//# debugId=
|
|
183
|
+
//# debugId=964b8c71-056c-51fb-893a-d51f8f6ad5d9
|
package/dist/types.d.ts
CHANGED
|
@@ -93,5 +93,15 @@ export interface PackManifest {
|
|
|
93
93
|
* yet enforced.
|
|
94
94
|
*/
|
|
95
95
|
capabilities?: string[];
|
|
96
|
+
/**
|
|
97
|
+
* Post-install initialization (US-004/US-005). Optional — absent on legacy
|
|
98
|
+
* packs. `entrypoint` names a declared `contributes.skills`/`commands` entry
|
|
99
|
+
* (slash-normalized); `prompt` is optional free-text prose (PHASE 1 does NOT
|
|
100
|
+
* render the prose — only an auto-generated get-started line from entrypoint).
|
|
101
|
+
*/
|
|
102
|
+
initialization?: {
|
|
103
|
+
entrypoint: string;
|
|
104
|
+
prompt?: string;
|
|
105
|
+
};
|
|
96
106
|
}
|
|
97
107
|
//# sourceMappingURL=types.d.ts.map
|