@indigoai-us/hq-cli 5.59.0 → 5.61.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/dist/commands/meetings.js +127 -14
- package/dist/commands/pack-install.d.ts +7 -1
- package/dist/commands/pack-install.js +86 -15
- package/dist/commands/packs.d.ts +2 -1
- package/dist/commands/packs.js +13 -8
- package/dist/commands/secrets.d.ts +5 -0
- package/dist/commands/secrets.js +134 -6
- package/dist/index.d.ts +5 -3
- package/dist/index.js +14 -240
- package/dist/main.d.ts +7 -0
- package/dist/main.js +247 -0
- package/dist/utils/sandbox-runner-client.d.ts +13 -0
- package/dist/utils/sandbox-runner-client.js +82 -5
- package/dist/utils/version-check.d.ts +6 -0
- package/dist/utils/version-check.js +78 -2
- package/package.json +1 -1
- package/src/commands/meetings.test.ts +116 -2
- package/src/commands/meetings.ts +206 -37
- package/src/commands/pack-install.ts +115 -18
- package/src/commands/pack-update-cache.test.ts +149 -0
- package/src/commands/packs.ts +28 -7
- package/src/commands/secrets.test.ts +407 -0
- package/src/commands/secrets.ts +203 -5
- package/src/index.test.ts +32 -0
- package/src/index.ts +11 -274
- package/src/main.ts +283 -0
- package/src/utils/sandbox-runner-client.test.ts +128 -0
- package/src/utils/sandbox-runner-client.ts +99 -3
- package/src/utils/version-check.test.ts +30 -0
- package/src/utils/version-check.ts +72 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
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]="
|
|
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]="6bb33e65-134a-5b0a-917b-ca084d4c8366")}catch(e){}}();
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
5
5
|
import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
|
|
@@ -13,6 +13,17 @@ function formatDuration(seconds) {
|
|
|
13
13
|
return `${m}m ${s}s`;
|
|
14
14
|
return `${s}s`;
|
|
15
15
|
}
|
|
16
|
+
function safeFormatDuration(seconds) {
|
|
17
|
+
return typeof seconds === "number" && Number.isFinite(seconds) ? formatDuration(seconds) : "-";
|
|
18
|
+
}
|
|
19
|
+
function safeFormatDate(iso, options) {
|
|
20
|
+
if (typeof iso !== "string" || iso.length === 0)
|
|
21
|
+
return "-";
|
|
22
|
+
const date = new Date(iso);
|
|
23
|
+
if (Number.isNaN(date.getTime()))
|
|
24
|
+
return "-";
|
|
25
|
+
return options ? date.toLocaleString("en-US", options) : date.toLocaleString();
|
|
26
|
+
}
|
|
16
27
|
function formatTimestamp(ts) {
|
|
17
28
|
const m = Math.floor(ts / 60);
|
|
18
29
|
const s = Math.floor(ts % 60);
|
|
@@ -32,6 +43,38 @@ function statusBadge(status) {
|
|
|
32
43
|
return chalk.dim(status);
|
|
33
44
|
}
|
|
34
45
|
}
|
|
46
|
+
function isMarkdownShape(x) {
|
|
47
|
+
if (!x || typeof x !== "object")
|
|
48
|
+
return false;
|
|
49
|
+
const candidate = x;
|
|
50
|
+
return candidate.sourceShape === "markdown" || Boolean(candidate.source?.frontmatter);
|
|
51
|
+
}
|
|
52
|
+
function hasSignals(signals) {
|
|
53
|
+
return Boolean(signals &&
|
|
54
|
+
typeof signals === "object" &&
|
|
55
|
+
!Array.isArray(signals) &&
|
|
56
|
+
Object.keys(signals).length > 0);
|
|
57
|
+
}
|
|
58
|
+
function renderSignals(signals) {
|
|
59
|
+
console.log(chalk.bold("Signals"));
|
|
60
|
+
for (const [key, value] of Object.entries(signals)) {
|
|
61
|
+
if (value === null || value === undefined) {
|
|
62
|
+
console.log(` ${key}: -`);
|
|
63
|
+
}
|
|
64
|
+
else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
65
|
+
console.log(` ${key}: ${String(value)}`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const rendered = JSON.stringify(value, null, 2)
|
|
69
|
+
.split("\n")
|
|
70
|
+
.map((line) => ` ${line}`)
|
|
71
|
+
.join("\n");
|
|
72
|
+
console.log(` ${key}:`);
|
|
73
|
+
console.log(rendered);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log();
|
|
77
|
+
}
|
|
35
78
|
async function resolveShortId(token, prefix, query) {
|
|
36
79
|
if (prefix.includes("-") && prefix.length > 8)
|
|
37
80
|
return prefix;
|
|
@@ -99,25 +142,28 @@ function printMeetingTable(meetings) {
|
|
|
99
142
|
const id = m.meetingId.slice(0, 8);
|
|
100
143
|
const fullTitle = displayTitle(m);
|
|
101
144
|
const title = fullTitle.length > TITLE_W ? fullTitle.slice(0, TITLE_W - 1) + "…" : fullTitle;
|
|
102
|
-
const date =
|
|
145
|
+
const date = safeFormatDate(m.startTime, {
|
|
103
146
|
month: "short",
|
|
104
147
|
day: "numeric",
|
|
105
148
|
hour: "2-digit",
|
|
106
149
|
minute: "2-digit",
|
|
107
150
|
});
|
|
108
|
-
const dur =
|
|
151
|
+
const dur = safeFormatDuration(m.duration);
|
|
152
|
+
const status = m.status ? statusBadge(m.status) : chalk.dim("-");
|
|
153
|
+
const parts = typeof m.participantCount === "number" ? String(m.participantCount) : "-";
|
|
109
154
|
const flags = [
|
|
110
155
|
m.hasTranscript ? "T" : "",
|
|
111
156
|
m.hasNotes ? "N" : "",
|
|
157
|
+
m.hasSignals ? "S" : "",
|
|
112
158
|
].filter(Boolean).join("") || "-";
|
|
113
159
|
console.log([
|
|
114
160
|
chalk.cyan(id.padEnd(ID_W)),
|
|
115
161
|
title.padEnd(TITLE_W),
|
|
116
162
|
chalk.dim(date.padEnd(DATE_W)),
|
|
117
163
|
dur.padEnd(DUR_W),
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
flags,
|
|
164
|
+
status.padEnd(STATUS_W + 10), // chalk adds escape chars
|
|
165
|
+
parts.padEnd(PARTS_W),
|
|
166
|
+
flags.padEnd(FLAGS_W),
|
|
121
167
|
].join(" "));
|
|
122
168
|
}
|
|
123
169
|
}
|
|
@@ -188,16 +234,34 @@ export function registerMeetingsCommand(program) {
|
|
|
188
234
|
console.log(JSON.stringify(detail, null, 2));
|
|
189
235
|
return;
|
|
190
236
|
}
|
|
191
|
-
|
|
237
|
+
if (isMarkdownShape(detail)) {
|
|
238
|
+
const fm = detail.source.frontmatter ?? {};
|
|
239
|
+
console.log(chalk.bold(`\n${fm.title || "(untitled)"}\n`));
|
|
240
|
+
console.log(` ID: ${chalk.cyan(detail.meetingId)}`);
|
|
241
|
+
console.log(` Status: ${fm.bot_status || "-"}`);
|
|
242
|
+
console.log(` Date: ${safeFormatDate(fm.scheduled_start_time || fm.created_at)}`);
|
|
243
|
+
console.log(` Platform: ${fm.meeting_platform || "-"}`);
|
|
244
|
+
console.log(` Origin: ${fm.origin || "-"}`);
|
|
245
|
+
console.log(` Company: ${fm.company_id || "-"}`);
|
|
246
|
+
if (fm.meeting_url)
|
|
247
|
+
console.log(` Meeting URL: ${fm.meeting_url}`);
|
|
248
|
+
if (hasSignals(detail.signals)) {
|
|
249
|
+
console.log(` Signals: ${Object.keys(detail.signals).length}`);
|
|
250
|
+
}
|
|
251
|
+
console.log(chalk.dim(`\n This meeting is stored as a markdown document. Use \`hq meetings transcript ${detail.meetingId.slice(0, 8)}\` to view the full document.`));
|
|
252
|
+
console.log();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
console.log(chalk.bold(`\n${detail.title ?? "(untitled)"}\n`));
|
|
192
256
|
console.log(` ID: ${chalk.cyan(detail.meetingId)}`);
|
|
193
|
-
console.log(` Status: ${statusBadge(detail.status)}`);
|
|
194
|
-
console.log(` Date: ${
|
|
195
|
-
console.log(` Duration: ${
|
|
196
|
-
console.log(` Source: ${detail.sourceApp} (${detail.botProvider})`);
|
|
257
|
+
console.log(` Status: ${detail.status ? statusBadge(detail.status) : chalk.dim("-")}`);
|
|
258
|
+
console.log(` Date: ${safeFormatDate(detail.startTime)}`);
|
|
259
|
+
console.log(` Duration: ${safeFormatDuration(detail.duration)}`);
|
|
260
|
+
console.log(` Source: ${detail.sourceApp ?? "-"} (${detail.botProvider ?? "-"})`);
|
|
197
261
|
console.log(` Shared: ${detail.isShared ? "yes" : "no"}`);
|
|
198
|
-
if (detail.participants
|
|
262
|
+
if ((detail.participants?.length ?? 0) > 0) {
|
|
199
263
|
console.log(chalk.bold("\n Participants:"));
|
|
200
|
-
for (const p of detail.participants) {
|
|
264
|
+
for (const p of detail.participants ?? []) {
|
|
201
265
|
const name = p.name ?? p.email;
|
|
202
266
|
const role = p.role === "organizer" ? chalk.yellow(" (organizer)") : "";
|
|
203
267
|
console.log(` - ${name}${role}`);
|
|
@@ -325,6 +389,26 @@ export function registerMeetingsCommand(program) {
|
|
|
325
389
|
if (!res.ok)
|
|
326
390
|
await handleApiError(res);
|
|
327
391
|
const detail = (await res.json());
|
|
392
|
+
if (isMarkdownShape(detail)) {
|
|
393
|
+
const documentUrl = detail.source.presigned_url;
|
|
394
|
+
if (!documentUrl) {
|
|
395
|
+
console.error(chalk.red("No document available for this meeting."));
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
const docRes = await fetch(documentUrl);
|
|
399
|
+
if (!docRes.ok) {
|
|
400
|
+
console.error(chalk.red(`Failed to download meeting document (${docRes.status})`));
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
const markdown = await docRes.text();
|
|
404
|
+
if (meetings.opts().json) {
|
|
405
|
+
console.log(JSON.stringify({ meetingId: detail.meetingId, sourceShape: "markdown", markdown }, null, 2));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
console.log(chalk.bold(`\nTranscript: ${detail.source.frontmatter?.title || "(untitled)"}\n`));
|
|
409
|
+
console.log(markdown);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
328
412
|
if (!detail.documentUrl) {
|
|
329
413
|
console.error(chalk.red("No document URL available for this meeting."));
|
|
330
414
|
process.exit(1);
|
|
@@ -375,6 +459,35 @@ export function registerMeetingsCommand(program) {
|
|
|
375
459
|
if (!res.ok)
|
|
376
460
|
await handleApiError(res);
|
|
377
461
|
const detail = (await res.json());
|
|
462
|
+
if (isMarkdownShape(detail)) {
|
|
463
|
+
if (hasSignals(detail.signals)) {
|
|
464
|
+
if (meetings.opts().json) {
|
|
465
|
+
console.log(JSON.stringify(detail.signals, null, 2));
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
console.log(chalk.bold(`\nMeeting Notes: ${detail.source.frontmatter?.title || "(untitled)"}\n`));
|
|
469
|
+
renderSignals(detail.signals);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const documentUrl = detail.source.presigned_url;
|
|
473
|
+
if (!documentUrl) {
|
|
474
|
+
console.log(chalk.yellow("No notes available for this meeting."));
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const docRes = await fetch(documentUrl);
|
|
478
|
+
if (!docRes.ok) {
|
|
479
|
+
console.error(chalk.red(`Failed to download meeting document (${docRes.status})`));
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
const markdown = await docRes.text();
|
|
483
|
+
if (meetings.opts().json) {
|
|
484
|
+
console.log(JSON.stringify({ meetingId: detail.meetingId, sourceShape: "markdown", markdown }, null, 2));
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
console.log(chalk.bold(`\nMeeting Notes: ${detail.source.frontmatter?.title || "(untitled)"}\n`));
|
|
488
|
+
console.log(markdown);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
378
491
|
if (!detail.documentUrl) {
|
|
379
492
|
console.error(chalk.red("No document URL available for this meeting."));
|
|
380
493
|
process.exit(1);
|
|
@@ -437,4 +550,4 @@ export function registerMeetingsCommand(program) {
|
|
|
437
550
|
});
|
|
438
551
|
}
|
|
439
552
|
//# sourceMappingURL=meetings.js.map
|
|
440
|
-
//# debugId=
|
|
553
|
+
//# debugId=6bb33e65-134a-5b0a-917b-ca084d4c8366
|
|
@@ -36,6 +36,12 @@
|
|
|
36
36
|
import { type KeyObject } from 'node:crypto';
|
|
37
37
|
import { type SecretResolver } from './mcp-registration.js';
|
|
38
38
|
import type { PackManifest } from '../types.js';
|
|
39
|
+
export interface ResolveLatestOptions {
|
|
40
|
+
forceRefresh?: boolean;
|
|
41
|
+
now?: number;
|
|
42
|
+
cacheTtlMs?: number;
|
|
43
|
+
fetchImpl?: typeof fetch;
|
|
44
|
+
}
|
|
39
45
|
export type Transport = 'npm' | 'git' | 'local' | 'marketplace';
|
|
40
46
|
/** Prefix that routes a source through the HQ marketplace transport (US-006). */
|
|
41
47
|
export declare const MARKETPLACE_PREFIX = "marketplace:";
|
|
@@ -181,7 +187,7 @@ export interface LatestResult {
|
|
|
181
187
|
* @param source the stamped `source:` from the installed package.yaml
|
|
182
188
|
* @param installedVersion the installed pack's manifest `version` (npm compare)
|
|
183
189
|
*/
|
|
184
|
-
export declare function resolveLatest(source: string, installedVersion?: string): LatestResult
|
|
190
|
+
export declare function resolveLatest(source: string, installedVersion?: string, opts?: ResolveLatestOptions): Promise<LatestResult>;
|
|
185
191
|
/**
|
|
186
192
|
* Async marketplace update probe (US-006): resolve the slug's latest approved
|
|
187
193
|
* listing version and compare it to the installed version. Reuses the same
|
|
@@ -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]="a90272ca-94a1-5905-85f8-af12a01cb58a")}catch(e){}}();
|
|
38
38
|
import * as fs from 'fs';
|
|
39
39
|
import * as os from 'os';
|
|
40
40
|
import * as path from 'path';
|
|
@@ -54,6 +54,58 @@ import { safeExtractTarball } from './safe-extract.js';
|
|
|
54
54
|
import { vaultApiFetchPublic } from '../utils/vault-api.js';
|
|
55
55
|
import { redactSecrets, SECRET_REDACTION, registerMcpServers, McpManifestError, } from './mcp-registration.js';
|
|
56
56
|
import { readCache, listSecretCacheScopes } from '../utils/secrets-cache.js';
|
|
57
|
+
const PACK_UPDATE_CACHE_TTL_MS = 12 * 60 * 60 * 1000;
|
|
58
|
+
const PACK_UPDATE_FETCH_TIMEOUT_MS = 3_000;
|
|
59
|
+
const gitLsRemoteMemo = new Map();
|
|
60
|
+
function packUpdateCachePath() {
|
|
61
|
+
return path.join(os.homedir(), '.hq', 'pack-update-cache.json');
|
|
62
|
+
}
|
|
63
|
+
function readPackUpdateCache() {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(fs.readFileSync(packUpdateCachePath(), 'utf-8'));
|
|
66
|
+
if (!parsed || typeof parsed !== 'object' || !parsed.entries || typeof parsed.entries !== 'object') {
|
|
67
|
+
return { entries: {} };
|
|
68
|
+
}
|
|
69
|
+
return { entries: parsed.entries };
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return { entries: {} };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function writePackUpdateCache(cache) {
|
|
76
|
+
try {
|
|
77
|
+
const file = packUpdateCachePath();
|
|
78
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
79
|
+
fs.writeFileSync(file, JSON.stringify(cache));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// best-effort; update checks must never fail because the cache is unwritable
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function cachedLatest(cacheKey, opts) {
|
|
86
|
+
if (opts.forceRefresh)
|
|
87
|
+
return undefined;
|
|
88
|
+
const entry = readPackUpdateCache().entries[cacheKey];
|
|
89
|
+
if (!entry || typeof entry.latest !== 'string' || typeof entry.fetchedAt !== 'number')
|
|
90
|
+
return undefined;
|
|
91
|
+
const now = opts.now ?? Date.now();
|
|
92
|
+
const ttl = opts.cacheTtlMs ?? PACK_UPDATE_CACHE_TTL_MS;
|
|
93
|
+
return now - entry.fetchedAt <= ttl ? entry.latest : undefined;
|
|
94
|
+
}
|
|
95
|
+
function storeCachedLatest(cacheKey, latest, opts) {
|
|
96
|
+
const cache = readPackUpdateCache();
|
|
97
|
+
cache.entries[cacheKey] = { latest, fetchedAt: opts.now ?? Date.now() };
|
|
98
|
+
writePackUpdateCache(cache);
|
|
99
|
+
}
|
|
100
|
+
async function latestWithDiskCache(cacheKey, opts, refresh) {
|
|
101
|
+
const cached = cachedLatest(cacheKey, opts);
|
|
102
|
+
if (cached)
|
|
103
|
+
return cached;
|
|
104
|
+
const latest = await refresh();
|
|
105
|
+
if (latest)
|
|
106
|
+
storeCachedLatest(cacheKey, latest, opts);
|
|
107
|
+
return latest;
|
|
108
|
+
}
|
|
57
109
|
/** Prefix that routes a source through the HQ marketplace transport (US-006). */
|
|
58
110
|
export const MARKETPLACE_PREFIX = 'marketplace:';
|
|
59
111
|
export function classify(source) {
|
|
@@ -489,13 +541,33 @@ function rsyncDir(src, dest) {
|
|
|
489
541
|
*/
|
|
490
542
|
function isNamedRef(url, ref) {
|
|
491
543
|
try {
|
|
492
|
-
const out =
|
|
544
|
+
const out = gitLsRemote(['--heads', '--tags', url, ref]);
|
|
493
545
|
return out.trim().length > 0;
|
|
494
546
|
}
|
|
495
547
|
catch {
|
|
496
548
|
return false;
|
|
497
549
|
}
|
|
498
550
|
}
|
|
551
|
+
function gitLsRemote(args) {
|
|
552
|
+
const key = args.join('\0');
|
|
553
|
+
const cached = gitLsRemoteMemo.get(key);
|
|
554
|
+
if (cached !== undefined)
|
|
555
|
+
return cached;
|
|
556
|
+
const out = execFileSync('git', ['ls-remote', ...args], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
557
|
+
gitLsRemoteMemo.set(key, out);
|
|
558
|
+
return out;
|
|
559
|
+
}
|
|
560
|
+
async function fetchLatestNpmVersion(pkg, opts) {
|
|
561
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
562
|
+
const res = await fetchImpl(`https://registry.npmjs.org/${encodeURIComponent(pkg)}/latest`, {
|
|
563
|
+
headers: { Accept: 'application/json' },
|
|
564
|
+
signal: AbortSignal.timeout(PACK_UPDATE_FETCH_TIMEOUT_MS),
|
|
565
|
+
});
|
|
566
|
+
if (!res.ok)
|
|
567
|
+
throw new Error(`registry returned ${res.status}`);
|
|
568
|
+
const body = (await res.json());
|
|
569
|
+
return typeof body.version === 'string' ? body.version : undefined;
|
|
570
|
+
}
|
|
499
571
|
/** Extract the ref (sha or named ref) recorded in a stamped git source. */
|
|
500
572
|
function gitRefFromSource(source) {
|
|
501
573
|
const { subpath, ref } = parseGitFragment(source);
|
|
@@ -504,6 +576,9 @@ function gitRefFromSource(source) {
|
|
|
504
576
|
void subpath;
|
|
505
577
|
return ref;
|
|
506
578
|
}
|
|
579
|
+
function isFullGitSha(ref) {
|
|
580
|
+
return /^[0-9a-f]{40}$/i.test(ref);
|
|
581
|
+
}
|
|
507
582
|
/**
|
|
508
583
|
* Probe whether a newer version of an already-installed pack is available,
|
|
509
584
|
* WITHOUT fetching or installing. Reuses the same git/npm primitives as the
|
|
@@ -513,7 +588,7 @@ function gitRefFromSource(source) {
|
|
|
513
588
|
* @param source the stamped `source:` from the installed package.yaml
|
|
514
589
|
* @param installedVersion the installed pack's manifest `version` (npm compare)
|
|
515
590
|
*/
|
|
516
|
-
export function resolveLatest(source, installedVersion) {
|
|
591
|
+
export async function resolveLatest(source, installedVersion, opts = {}) {
|
|
517
592
|
let transport;
|
|
518
593
|
try {
|
|
519
594
|
transport = classify(source);
|
|
@@ -542,15 +617,12 @@ export function resolveLatest(source, installedVersion) {
|
|
|
542
617
|
const pkg = stripVersion(source);
|
|
543
618
|
const current = installedVersion ?? (source.lastIndexOf('@') > 0 ? source.slice(source.lastIndexOf('@') + 1) : undefined);
|
|
544
619
|
try {
|
|
545
|
-
const latest =
|
|
546
|
-
encoding: 'utf-8',
|
|
547
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
548
|
-
}).trim();
|
|
620
|
+
const latest = await latestWithDiskCache(`npm:${pkg}`, opts, () => fetchLatestNpmVersion(pkg, opts));
|
|
549
621
|
const updateAvailable = current && latest ? semverGt(latest, current) : null;
|
|
550
622
|
return { transport, current, latest, updateAvailable };
|
|
551
623
|
}
|
|
552
624
|
catch (e) {
|
|
553
|
-
return { transport, current, updateAvailable: null, error: `npm
|
|
625
|
+
return { transport, current, updateAvailable: null, error: `npm registry check failed: ${e.message}` };
|
|
554
626
|
}
|
|
555
627
|
}
|
|
556
628
|
// git
|
|
@@ -565,13 +637,12 @@ export function resolveLatest(source, installedVersion) {
|
|
|
565
637
|
const current = gitRefFromSource(source);
|
|
566
638
|
// If install followed a named ref (branch/tag), compare that ref's tip;
|
|
567
639
|
// otherwise (default SHA-pin) compare the default branch HEAD.
|
|
568
|
-
const refArg = current && isNamedRef(url, current) ? current : 'HEAD';
|
|
640
|
+
const refArg = current && !isFullGitSha(current) && isNamedRef(url, current) ? current : 'HEAD';
|
|
569
641
|
try {
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
})
|
|
574
|
-
const latest = out.split(/\s+/)[0] || undefined;
|
|
642
|
+
const latest = await latestWithDiskCache(`git:${url}#${refArg}`, opts, () => {
|
|
643
|
+
const out = gitLsRemote([url, refArg]).trim();
|
|
644
|
+
return out.split(/\s+/)[0] || undefined;
|
|
645
|
+
});
|
|
575
646
|
const updateAvailable = current && latest ? !latest.startsWith(current) && !current.startsWith(latest) : null;
|
|
576
647
|
return { transport, current, latest, updateAvailable };
|
|
577
648
|
}
|
|
@@ -1629,4 +1700,4 @@ export async function installPack(source, opts = {}) {
|
|
|
1629
1700
|
}
|
|
1630
1701
|
}
|
|
1631
1702
|
//# sourceMappingURL=pack-install.js.map
|
|
1632
|
-
//# debugId=
|
|
1703
|
+
//# debugId=a90272ca-94a1-5905-85f8-af12a01cb58a
|
package/dist/commands/packs.d.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* Spec: knowledge/public/hq-core/package-yaml-spec.md.
|
|
19
19
|
*/
|
|
20
20
|
import { Command } from 'commander';
|
|
21
|
+
import { type LatestResult } from './pack-install.js';
|
|
21
22
|
import { type InstalledPack, type LinkStatus } from '../utils/pack-contributions.js';
|
|
22
23
|
import type { PackContributeKey } from '../types.js';
|
|
23
24
|
interface InstalledPackView {
|
|
@@ -48,7 +49,7 @@ interface InstalledPackView {
|
|
|
48
49
|
};
|
|
49
50
|
error?: string;
|
|
50
51
|
}
|
|
51
|
-
export declare function buildInstalledView(hqRoot: string, hqVersion: string | null, pack: InstalledPack, installedSources: Set<string>, checkUpdates: boolean): InstalledPackView;
|
|
52
|
+
export declare function buildInstalledView(hqRoot: string, hqVersion: string | null, pack: InstalledPack, installedSources: Set<string>, checkUpdates: boolean, latestProbe?: LatestResult): InstalledPackView;
|
|
52
53
|
export declare function registerPacksCommand(parent: Command): void;
|
|
53
54
|
export {};
|
|
54
55
|
//# 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]="f8ee3de6-a097-5e0f-8afa-63381f29032d")}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
|
-
export function buildInstalledView(hqRoot, hqVersion, pack, installedSources, checkUpdates) {
|
|
64
|
+
export function buildInstalledView(hqRoot, hqVersion, pack, installedSources, checkUpdates, latestProbe) {
|
|
65
65
|
if (!pack.manifest) {
|
|
66
66
|
return {
|
|
67
67
|
name: pack.name,
|
|
@@ -95,7 +95,7 @@ export function buildInstalledView(hqRoot, hqVersion, pack, installedSources, ch
|
|
|
95
95
|
const hqCoreSatisfied = hqVersion && requiresHqCore ? semverSatisfies(hqVersion, requiresHqCore) : null;
|
|
96
96
|
let updateAvailable = null;
|
|
97
97
|
if (checkUpdates && m.source) {
|
|
98
|
-
updateAvailable =
|
|
98
|
+
updateAvailable = latestProbe?.updateAvailable ?? null;
|
|
99
99
|
}
|
|
100
100
|
// US-005 — surface the pack's `initialization` block so the HQ Sync
|
|
101
101
|
// "Installed" panel can render its get-started affordance. `readPackManifest`
|
|
@@ -129,13 +129,16 @@ export function buildInstalledView(hqRoot, hqVersion, pack, installedSources, ch
|
|
|
129
129
|
...(initialization ? { initialization } : {}),
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
|
-
function buildListView(hqRoot, checkUpdates, evalConditionals) {
|
|
132
|
+
async function buildListView(hqRoot, checkUpdates, evalConditionals, refreshUpdates) {
|
|
133
133
|
const hqVersion = readHqVersion(hqRoot);
|
|
134
134
|
const packs = listInstalledPacks(hqRoot);
|
|
135
135
|
const catalog = readRecommendedPackages(hqRoot);
|
|
136
136
|
const catalogSources = new Set(catalog.map((c) => c.source));
|
|
137
137
|
const warnings = [];
|
|
138
|
-
const
|
|
138
|
+
const latestProbes = await Promise.all(packs.map((p) => checkUpdates && p.manifest?.source
|
|
139
|
+
? resolveLatest(p.manifest.source, p.manifest.version, { forceRefresh: refreshUpdates })
|
|
140
|
+
: Promise.resolve(undefined)));
|
|
141
|
+
const installed = packs.map((p, i) => buildInstalledView(hqRoot, hqVersion, p, catalogSources, checkUpdates, latestProbes[i]));
|
|
139
142
|
for (const p of installed) {
|
|
140
143
|
if (p.error)
|
|
141
144
|
warnings.push(`${p.name}: ${p.error}`);
|
|
@@ -217,7 +220,7 @@ async function runUpdate(name, opts) {
|
|
|
217
220
|
// other transport keeps the existing synchronous probe unchanged.
|
|
218
221
|
const probe = safeClassify(source) === 'marketplace'
|
|
219
222
|
? await resolveLatestMarketplace(source, m.version)
|
|
220
|
-
: resolveLatest(source, m.version);
|
|
223
|
+
: await resolveLatest(source, m.version, { forceRefresh: true });
|
|
221
224
|
const base = {
|
|
222
225
|
name: pname,
|
|
223
226
|
transport: probe.transport,
|
|
@@ -374,10 +377,11 @@ export function registerPacksCommand(parent) {
|
|
|
374
377
|
.option('--json', 'Machine-readable JSON output')
|
|
375
378
|
.option('--hq-root <path>', 'HQ root (default: auto-detect)')
|
|
376
379
|
.option('--check-updates', 'Probe each pack for available updates (network I/O)')
|
|
380
|
+
.option('--refresh', 'Bypass cached update probes')
|
|
377
381
|
.option('--eval-conditionals', 'Evaluate catalog conditional predicates (runs bash)')
|
|
378
382
|
.action(async (opts) => {
|
|
379
383
|
try {
|
|
380
|
-
const view = buildListView(resolveRoot(opts), !!opts.checkUpdates, !!opts.evalConditionals);
|
|
384
|
+
const view = await buildListView(resolveRoot(opts), !!opts.checkUpdates, !!opts.evalConditionals, !!opts.refresh);
|
|
381
385
|
if (wantsJson(opts))
|
|
382
386
|
emitJson(view);
|
|
383
387
|
else
|
|
@@ -394,6 +398,7 @@ export function registerPacksCommand(parent) {
|
|
|
394
398
|
.option('--json', 'Machine-readable JSON output')
|
|
395
399
|
.option('--hq-root <path>', 'HQ root (default: auto-detect)')
|
|
396
400
|
.option('--check-only', 'Report availability without installing')
|
|
401
|
+
.option('--refresh', 'Bypass cached update probes (update refreshes by default)')
|
|
397
402
|
.option('-y, --yes', 'Non-interactive (implies --allow-hooks and --allow-mcp)')
|
|
398
403
|
.option('--allow-hooks', 'Install pack hooks without prompting')
|
|
399
404
|
.option('--allow-mcp', 'Register pack MCP servers without prompting')
|
|
@@ -463,4 +468,4 @@ export function registerPacksCommand(parent) {
|
|
|
463
468
|
});
|
|
464
469
|
}
|
|
465
470
|
//# sourceMappingURL=packs.js.map
|
|
466
|
-
//# debugId=
|
|
471
|
+
//# debugId=f8ee3de6-a097-5e0f-8afa-63381f29032d
|
|
@@ -36,6 +36,11 @@ export interface SecretLoadResponse {
|
|
|
36
36
|
message?: string;
|
|
37
37
|
}>;
|
|
38
38
|
}
|
|
39
|
+
export interface SecretInjectionRecipe {
|
|
40
|
+
header: string;
|
|
41
|
+
scheme: "raw" | "bearer";
|
|
42
|
+
extraHeaders?: Record<string, string>;
|
|
43
|
+
}
|
|
39
44
|
export declare function scrubSandboxOutput(text: string, secretNames?: string[]): string;
|
|
40
45
|
export declare function loadRevealedSecrets(token: string, companyUid: string, keys: string[], usage?: SecretUsage): Promise<Map<string, string>>;
|
|
41
46
|
export declare function registerSecretsCommand(program: Command): void;
|