@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.
@@ -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]="f2fc6ee0-c7c4-5012-85ce-aea36030aa92")}catch(e){}}();
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 = new Date(m.startTime).toLocaleDateString("en-US", {
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 = formatDuration(m.duration);
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
- statusBadge(m.status).padEnd(STATUS_W + 10), // chalk adds escape chars
119
- String(m.participantCount).padEnd(PARTS_W),
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
- console.log(chalk.bold(`\n${detail.title}\n`));
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: ${new Date(detail.startTime).toLocaleString()}`);
195
- console.log(` Duration: ${formatDuration(detail.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.length > 0) {
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=f2fc6ee0-c7c4-5012-85ce-aea36030aa92
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]="0ec07d3a-ee86-51fd-adbd-2608c620ae99")}catch(e){}}();
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 = execFileSync('git', ['ls-remote', '--heads', '--tags', url, ref], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
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 = execFileSync('npm', ['view', pkg, 'version'], {
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 view failed: ${e.message}` };
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 out = execFileSync('git', ['ls-remote', url, refArg], {
571
- encoding: 'utf-8',
572
- stdio: ['ignore', 'pipe', 'ignore'],
573
- }).trim();
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=0ec07d3a-ee86-51fd-adbd-2608c620ae99
1703
+ //# debugId=a90272ca-94a1-5905-85f8-af12a01cb58a
@@ -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
@@ -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]="69746edd-f9d8-528e-8902-de799c58bd8a")}catch(e){}}();
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 = resolveLatest(m.source, m.version).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 installed = packs.map((p) => buildInstalledView(hqRoot, hqVersion, p, catalogSources, checkUpdates));
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=69746edd-f9d8-528e-8902-de799c58bd8a
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;