@indigoai-us/hq-cli 5.273.0-alpha.0 → 5.274.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +65 -2
  2. package/README.md +32 -1
  3. package/dist/command-catalog.generated.d.ts +83 -536
  4. package/dist/command-catalog.generated.js +129 -714
  5. package/dist/command-registration-plan.d.ts +12 -0
  6. package/dist/command-registration-plan.js +2 -0
  7. package/dist/commands/cloud.js +40 -0
  8. package/dist/commands/core-checkpoint.js +3 -1
  9. package/dist/commands/core-command-catalog.d.ts +32 -0
  10. package/dist/commands/core-command-catalog.js +58 -0
  11. package/dist/commands/core-ontology.js +9 -4
  12. package/dist/commands/core.d.ts +4 -0
  13. package/dist/commands/core.js +79 -75
  14. package/dist/commands/daemon.d.ts +0 -3
  15. package/dist/commands/daemon.js +1 -173
  16. package/dist/commands/deploy-comments.d.ts +3 -0
  17. package/dist/commands/deploy-comments.js +159 -0
  18. package/dist/commands/lanes-checks.d.ts +0 -1
  19. package/dist/commands/lanes-checks.js +217 -54
  20. package/dist/commands/lanes-hold.js +40 -2
  21. package/dist/commands/lanes-message.js +98 -14
  22. package/dist/commands/lanes-move.d.ts +12 -0
  23. package/dist/commands/lanes-move.js +342 -0
  24. package/dist/commands/lanes-resume.js +3 -1
  25. package/dist/commands/lanes-shared.d.ts +39 -3
  26. package/dist/commands/lanes-shared.js +59 -0
  27. package/dist/commands/lanes.d.ts +3 -5
  28. package/dist/commands/lanes.js +206 -478
  29. package/dist/commands/report.js +2 -14
  30. package/dist/commands/sync-mode.d.ts +0 -11
  31. package/dist/commands/sync-mode.js +0 -30
  32. package/dist/fast-core.js +1 -1
  33. package/dist/{commands/scaffold-fast.d.ts → fast-scaffold.d.ts} +1 -1
  34. package/dist/{commands/scaffold-fast.js → fast-scaffold.js} +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.js +2 -2
  37. package/dist/lib/checkpoint-ontology/apply.d.ts +8 -0
  38. package/dist/lib/checkpoint-ontology/apply.js +33 -0
  39. package/dist/lib/checkpoint-ontology/plan.d.ts +15 -0
  40. package/dist/lib/checkpoint-ontology/plan.js +15 -0
  41. package/dist/lib/daemon/catalog.d.ts +2 -18
  42. package/dist/lib/daemon/catalog.js +2 -18
  43. package/dist/lib/daemon/config.d.ts +0 -6
  44. package/dist/lib/daemon/config.js +2 -15
  45. package/dist/lib/daemon/requests.d.ts +0 -4
  46. package/dist/lib/daemon/requests.js +0 -2
  47. package/dist/lib/daemon/run.js +19 -78
  48. package/dist/lib/daemon/supervisor.d.ts +0 -8
  49. package/dist/lib/daemon/supervisor.js +1 -101
  50. package/dist/lib/deploy-api.d.ts +6 -0
  51. package/dist/lib/deploy-api.js +21 -0
  52. package/dist/lib/doctor/checks/scaffold-cli-floor.d.ts +7 -0
  53. package/dist/lib/doctor/checks/scaffold-cli-floor.js +73 -0
  54. package/dist/lib/doctor/fix/apply.d.ts +17 -2
  55. package/dist/lib/doctor/fix/apply.js +111 -57
  56. package/dist/lib/doctor/fix/json-output.d.ts +9 -1
  57. package/dist/lib/doctor/fix/json-output.js +11 -0
  58. package/dist/lib/doctor/registry.js +2 -0
  59. package/dist/lib/first-sync-next-step.d.ts +37 -0
  60. package/dist/lib/first-sync-next-step.js +189 -0
  61. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  62. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  63. package/dist/lib/lanes/hire-grant-dispatch.d.ts +6 -1
  64. package/dist/lib/lanes/hire-grant-dispatch.js +78 -3
  65. package/dist/lib/lanes/keep-alive.d.ts +3 -2
  66. package/dist/lib/lanes/keep-alive.js +2 -2
  67. package/dist/lib/lanes/merge-command.d.ts +1 -0
  68. package/dist/lib/lanes/merge-command.js +299 -33
  69. package/dist/lib/lanes/orphans.d.ts +7 -0
  70. package/dist/lib/lanes/orphans.js +39 -4
  71. package/dist/lib/lanes/tracker-store.d.ts +2 -18
  72. package/dist/lib/lanes/tracker-store.js +1 -172
  73. package/dist/lib/scaffold-cli-floor.d.ts +9 -0
  74. package/dist/lib/scaffold-cli-floor.js +50 -0
  75. package/dist/main.js +4 -1
  76. package/dist/utils/cli-telemetry.d.ts +7 -1
  77. package/dist/utils/cli-telemetry.js +84 -8
  78. package/dist/utils/contribution-table.d.ts +1 -1
  79. package/dist/utils/scaffold-fast-import-guard.d.ts +3 -0
  80. package/dist/utils/scaffold-fast-import-guard.js +28 -0
  81. package/dist/utils/scaffold-packlist-guard.d.ts +7 -0
  82. package/dist/utils/scaffold-packlist-guard.js +23 -0
  83. package/dist/utils/scaffold-sibling-references.d.ts +21 -0
  84. package/dist/utils/scaffold-sibling-references.js +156 -0
  85. package/package.json +2 -2
  86. package/dist/commands/lanes-import.d.ts +0 -14
  87. package/dist/commands/lanes-import.js +0 -1210
  88. package/dist/commands/lanes-tracker.d.ts +0 -18
  89. package/dist/commands/lanes-tracker.js +0 -452
  90. package/dist/lib/daemon/sync-runner.d.ts +0 -58
  91. package/dist/lib/daemon/sync-runner.js +0 -277
@@ -18,7 +18,7 @@ import * as os from "node:os";
18
18
  import * as path from "node:path";
19
19
  import { major as semverMajor, valid as validSemver } from "semver";
20
20
  import { DEFAULT_SERVICES, SERVICE_DESCRIPTIONS, enabledServices, hqCloudUnits } from "../lib/daemon/catalog.js";
21
- import { applyServiceToggle, effectiveSyncOwner, readDaemonConfig, writeDaemonConfig } from "../lib/daemon/config.js";
21
+ import { applyServiceToggle, readDaemonConfig, writeDaemonConfig } from "../lib/daemon/config.js";
22
22
  import { daemonUnitInstalled } from "../lib/daemon/handoff.js";
23
23
  import { adoptLegacyUnits, readAdoptedUnits, restoreLegacyUnits } from "../lib/daemon/migrate.js";
24
24
  import { ROOT_REFUSAL, SYSTEM_UNIT_DIR, daemonRootRefusal, installDaemonUnit, installScopeFor, uninstallDaemonUnit, } from "../lib/daemon/install.js";
@@ -27,10 +27,7 @@ import { isDaemonProcess, runningDaemonPid } from "../lib/daemon/lock.js";
27
27
  import { bundledHqCloud } from "../lib/daemon/hq-cloud.js";
28
28
  import { MACHINE_TYPE_MARKER_PATH, detectMachineType, homeFromPasswd, } from "../lib/daemon/machine-type.js";
29
29
  import { daemonPaths } from "../lib/daemon/paths.js";
30
- import { processCommandLine } from "../lib/daemon/lock.js";
31
30
  import { writeControlRequest } from "../lib/daemon/requests.js";
32
- import { isSyncRunnerCommandLine, readWatchRunnerOwnerStatus, watchRunnerOwnerStatusPath } from "../lib/daemon/sync-runner.js";
33
- import { getSyncModeForCompany, setSyncMode } from "./sync-mode.js";
34
31
  import { resolveDefaultHqRoot } from "../utils/cognito-session.js";
35
32
  class DaemonCommandError extends Error {
36
33
  }
@@ -99,7 +96,6 @@ async function loadDefaults(over) {
99
96
  windowsUser: over.windowsUser ?? (platform === "win32" ? mesh.resolveWindowsUser(env) : ""),
100
97
  windowsHeadlessLauncher: over.windowsHeadlessLauncher ?? (platform === "win32" ? mesh.resolveWindowsHeadlessLauncher(env) : undefined),
101
98
  systemUnitDir: over.systemUnitDir ?? SYSTEM_UNIT_DIR,
102
- syncModeClient: over.syncModeClient,
103
99
  };
104
100
  }
105
101
  function detect(d, flag, config) {
@@ -376,135 +372,6 @@ function queueRequest(d, request, what) {
376
372
  const running = runningDaemonPid(paths.lockPath, d.daemonAlive) !== null;
377
373
  d.out(running ? `Asked the daemon to ${what}.` : `Queued "${what}"; the daemon is not running, so it applies when it starts.`);
378
374
  }
379
- function daemonSyncModeClient(d) {
380
- if (d.syncModeClient)
381
- return d.syncModeClient();
382
- return (async () => {
383
- const [{ VaultClient }, { buildVaultConfig, ensureCognitoToken }] = await Promise.all([
384
- import("@indigoai-us/hq-cloud"),
385
- import("../utils/cognito-session.js"),
386
- ]);
387
- const token = await ensureCognitoToken();
388
- return new VaultClient(buildVaultConfig(token));
389
- })();
390
- }
391
- function daemonSyncStatus(d) {
392
- const host = currentHost(d);
393
- const { paths } = host;
394
- const config = readDaemonConfig(paths.configPath);
395
- const daemonPid = runningDaemonPid(paths.lockPath, d.daemonAlive);
396
- const daemonState = daemonPid === null ? null : readState(paths);
397
- const syncUnit = daemonState?.units.find((unit) => unit.id === "sync");
398
- const hqRoot = config.hqRoot ?? d.env.HQ_ROOT?.trim();
399
- let ownerStatusCandidate = null;
400
- if (hqRoot) {
401
- try {
402
- ownerStatusCandidate = readWatchRunnerOwnerStatus(watchRunnerOwnerStatusPath(hqRoot, d.home, d.env));
403
- }
404
- catch (error) {
405
- const detail = error instanceof Error ? error.message : String(error);
406
- d.err(`Could not read sync runner owner status; treating it as unavailable: ${detail}`);
407
- }
408
- }
409
- const ownerCommandLine = ownerStatusCandidate
410
- ? processCommandLine(ownerStatusCandidate.pid, { platform: d.platform, run: d.run })
411
- : null;
412
- const ownerStatus = ownerCommandLine && isSyncRunnerCommandLine(ownerCommandLine)
413
- ? ownerStatusCandidate
414
- : null;
415
- return {
416
- running: syncUnit?.status === "running",
417
- paused: config.syncPaused === true,
418
- syncOwner: effectiveSyncOwner(config, host.detected.type),
419
- owner: ownerStatus?.owner ?? (syncUnit?.status === "running" ? "hq-daemon" : null),
420
- runnerPid: ownerStatus?.pid ?? syncUnit?.pid ?? null,
421
- hqCloudVersion: ownerStatus?.version ?? null,
422
- lastHeartbeat: syncUnit?.lastHeartbeatAt ?? ownerStatus?.heartbeatAt ?? null,
423
- lastPassResult: syncUnit?.lastPassResult ?? null,
424
- unitStatus: syncUnit?.status ?? "not-running",
425
- reason: syncUnit?.reason ?? null,
426
- logPath: paths.unitLogPath("sync"),
427
- };
428
- }
429
- async function runDaemonSyncStatus(d, opts) {
430
- const status = daemonSyncStatus(d);
431
- if (opts.json) {
432
- d.out(JSON.stringify(status, null, 2));
433
- return;
434
- }
435
- d.out(`Sync ${status.running ? "running" : status.unitStatus}${status.paused ? " (paused)" : ""}.`);
436
- d.out(`Configured sync owner: ${status.syncOwner}.`);
437
- d.out(`Owner: ${status.owner ?? "none"}; runner pid: ${status.runnerPid ?? "none"}; hq-cloud: ${status.hqCloudVersion ?? "unknown"}.`);
438
- if (status.reason)
439
- d.out(`Reason: ${status.reason}`);
440
- d.out(`Log: ${status.logPath}`);
441
- }
442
- function queueDaemonSyncControl(d, request) {
443
- const host = currentHost(d);
444
- writeControlRequest(host.paths.requestsDir, request);
445
- handOver(d, host);
446
- return runningDaemonPid(host.paths.lockPath, d.daemonAlive) !== null;
447
- }
448
- async function runDaemonSyncNow(d, opts) {
449
- const host = currentHost(d);
450
- const config = readDaemonConfig(host.paths.configPath);
451
- if (config.syncPaused)
452
- throw new DaemonCommandError("sync is paused; run `hq daemon sync resume` first");
453
- if (!enabledServices(host.detected.type, config).includes("sync")) {
454
- throw new DaemonCommandError("sync is disabled for this machine; run `hq daemon enable sync` first");
455
- }
456
- const running = queueDaemonSyncControl(d, { action: "restart", unit: "sync" });
457
- const result = { accepted: true, action: "restart-sync-runner", daemonRunning: running };
458
- d.out(opts.json ? JSON.stringify(result, null, 2) : running ? "Asked the daemon to start a fresh sync pass." : "Queued a fresh sync pass for the next daemon start.");
459
- }
460
- async function runDaemonSyncPause(d, paused, opts) {
461
- const host = currentHost(d);
462
- const config = readDaemonConfig(host.paths.configPath);
463
- if (config.syncPaused !== paused)
464
- writeDaemonConfig(host.paths.configPath, { ...config, syncPaused: paused });
465
- const daemonRunning = queueDaemonSyncControl(d, { action: "reconcile" });
466
- const result = { paused, persisted: true, daemonRunning };
467
- d.out(opts.json ? JSON.stringify(result, null, 2) : `Sync ${paused ? "paused" : "resumed"}${daemonRunning ? "" : "; the setting will apply when the daemon starts"}.`);
468
- }
469
- async function runDaemonSyncTakeOwnership(d, opts) {
470
- const host = currentHost(d);
471
- const config = readDaemonConfig(host.paths.configPath);
472
- if (config.syncOwner !== "daemon")
473
- writeDaemonConfig(host.paths.configPath, { ...config, syncOwner: "daemon" });
474
- const daemonRunning = queueDaemonSyncControl(d, { action: "reconcile" });
475
- const result = { syncOwner: "daemon", persisted: true, daemonRunning };
476
- d.out(opts.json ? JSON.stringify(result, null, 2) : `Daemon sync ownership persisted${daemonRunning ? "; asked the daemon to reconcile" : "; it applies when the daemon starts"}.`);
477
- }
478
- async function runDaemonSyncModeGet(d, companySlug, opts) {
479
- const result = await getSyncModeForCompany({ companySlug, vaultClient: await daemonSyncModeClient(d) });
480
- const value = {
481
- companySlug: result.companySlug,
482
- membershipId: result.membershipId,
483
- mode: result.config.syncMode,
484
- isDefault: result.config.isDefault,
485
- ...(result.config.updatedAt ? { updatedAt: result.config.updatedAt } : {}),
486
- };
487
- d.out(opts.json ? JSON.stringify(value, null, 2) : `${value.companySlug}: ${value.mode}${value.isDefault ? " (default)" : ""}`);
488
- }
489
- async function runDaemonSyncModeSet(d, companySlug, mode, opts) {
490
- if (mode !== "all" && mode !== "shared") {
491
- throw new DaemonCommandError(`Invalid sync mode '${mode}'. Use all or shared.`);
492
- }
493
- const result = await setSyncMode({
494
- mode,
495
- companySlug,
496
- vaultClient: await daemonSyncModeClient(d),
497
- });
498
- const value = {
499
- companySlug: result.companySlug,
500
- membershipId: result.membershipId,
501
- previousMode: result.previousMode,
502
- mode: result.newMode,
503
- isDefault: result.newConfig.isDefault,
504
- ...(result.newConfig.updatedAt ? { updatedAt: result.newConfig.updatedAt } : {}),
505
- };
506
- d.out(opts.json ? JSON.stringify(value, null, 2) : `${value.companySlug}: ${value.previousMode} → ${value.mode}`);
507
- }
508
375
  async function runToggle(d, action, services) {
509
376
  const host = currentHost(d);
510
377
  const { paths } = host;
@@ -736,45 +603,6 @@ export function registerDaemonCommand(program, over = {}) {
736
603
  .description("Show the machine type and each service's state")
737
604
  .option("--json", "Print machine-readable JSON")
738
605
  .action(wrap((d, opts) => runStatus(d, opts)));
739
- const sync = daemon
740
- .command("sync")
741
- .description("Inspect and control the daemon's company sync runner");
742
- sync
743
- .command("status")
744
- .description("Show sync ownership, heartbeat, last pass and log path")
745
- .option("--json", "Print machine-readable JSON")
746
- .action(wrap((d, opts) => runDaemonSyncStatus(d, opts)));
747
- sync
748
- .command("now")
749
- .description("Start a fresh sync pass immediately")
750
- .option("--json", "Print machine-readable JSON")
751
- .action(wrap((d, opts) => runDaemonSyncNow(d, opts)));
752
- sync
753
- .command("pause")
754
- .description("Pause daemon sync until resumed")
755
- .option("--json", "Print machine-readable JSON")
756
- .action(wrap((d, opts) => runDaemonSyncPause(d, true, opts)));
757
- sync
758
- .command("resume")
759
- .description("Resume daemon sync after a pause")
760
- .option("--json", "Print machine-readable JSON")
761
- .action(wrap((d, opts) => runDaemonSyncPause(d, false, opts)));
762
- sync
763
- .command("take-ownership")
764
- .description("Persist daemon ownership of desktop sync and reconcile")
765
- .option("--json", "Print machine-readable JSON")
766
- .action(wrap((d, opts) => runDaemonSyncTakeOwnership(d, opts)));
767
- const mode = sync.command("mode").description("Read or change a company's sync footprint");
768
- mode
769
- .command("get <company>")
770
- .description("Read a company's current all/shared sync mode")
771
- .option("--json", "Print machine-readable JSON")
772
- .action(wrap((d, company, opts) => runDaemonSyncModeGet(d, company, opts)));
773
- mode
774
- .command("set <company> <mode>")
775
- .description("Set a company's sync mode to all or shared")
776
- .option("--json", "Print machine-readable JSON")
777
- .action(wrap((d, company, mode, opts) => runDaemonSyncModeSet(d, company, mode, opts)));
778
606
  daemon
779
607
  .command("logs [unit]")
780
608
  .description("Print the supervisor log, or one service's log")
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerDeployCommentsCommand(deploy: Command): void;
3
+ //# sourceMappingURL=deploy-comments.d.ts.map
@@ -0,0 +1,159 @@
1
+ import { deployApiRequest } from "../lib/deploy-api.js";
2
+ import { DEFAULT_HQ_ROOT, ensureCognitoToken } from "../utils/cognito-session.js";
3
+ import { readActiveCompanySlug } from "./skill.js";
4
+ class DeployCommentsError extends Error {
5
+ }
6
+ function callerOrgHeaders(companyFlag) {
7
+ const slug = companyFlag ?? readActiveCompanySlug(DEFAULT_HQ_ROOT);
8
+ if (slug === undefined)
9
+ return {};
10
+ if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(slug)) {
11
+ throw new DeployCommentsError("The company slug is not valid.");
12
+ }
13
+ return { "X-Org-Slug": slug };
14
+ }
15
+ function errorCode(body, status) {
16
+ const code = body.error?.code;
17
+ return typeof code === "string" && /^[A-Z0-9_]+$/.test(code) ? code : `HTTP_${status}`;
18
+ }
19
+ async function responseError(response) {
20
+ const body = await response.json().catch(() => ({}));
21
+ const code = errorCode(body, response.status);
22
+ if (response.status === 401 || response.status === 403) {
23
+ return new DeployCommentsError(`You need to be the app owner or an org admin to read its comments. (${code})`);
24
+ }
25
+ if (response.status === 404) {
26
+ return new DeployCommentsError(`App was not found in the current org. (${code})`);
27
+ }
28
+ if (response.status === 429) {
29
+ const value = response.headers.get("Retry-After");
30
+ const retryAfter = value && /^\d+$/.test(value) ? value : "unknown";
31
+ return new DeployCommentsError(`Comment reads are rate limited. Retry-After: ${retryAfter} seconds. (${code})`);
32
+ }
33
+ return new DeployCommentsError(`Could not read deploy comments (HTTP ${response.status}, ${code}).`);
34
+ }
35
+ async function deployJson(token, route, orgHeaders) {
36
+ let response;
37
+ try {
38
+ response = await deployApiRequest(token, route, "GET", undefined, orgHeaders);
39
+ }
40
+ catch {
41
+ throw new DeployCommentsError("Could not reach hq-deploy.");
42
+ }
43
+ if (!response.ok)
44
+ throw await responseError(response);
45
+ try {
46
+ return await response.json();
47
+ }
48
+ catch {
49
+ throw new DeployCommentsError("hq-deploy returned an invalid response.");
50
+ }
51
+ }
52
+ async function resolveAppId(token, appNameOrId, orgHeaders) {
53
+ const result = await deployJson(token, "/api/apps", orgHeaders);
54
+ const apps = Array.isArray(result.apps) ? result.apps : [];
55
+ const byId = apps.find((app) => app && typeof app.id === "string" && app.id === appNameOrId);
56
+ const matches = byId
57
+ ? [byId]
58
+ : apps.filter((app) => app && typeof app.id === "string" && app.name === appNameOrId);
59
+ const ids = [...new Set(matches.map((app) => app.id).filter((id) => typeof id === "string"))];
60
+ if (ids.length === 0)
61
+ throw new DeployCommentsError("App was not found in the current org.");
62
+ if (ids.length > 1)
63
+ throw new DeployCommentsError("More than one app has that name in the current org; use its app id.");
64
+ return ids[0];
65
+ }
66
+ async function listComments(token, appId, orgHeaders) {
67
+ const route = `/api/apps/${encodeURIComponent(appId)}/manage/comments`;
68
+ const result = await deployJson(token, route, orgHeaders);
69
+ if (typeof result.commentsEnabled !== "boolean" || !Array.isArray(result.comments)) {
70
+ throw new DeployCommentsError("hq-deploy returned an incomplete comments response.");
71
+ }
72
+ return { commentsEnabled: result.commentsEnabled, comments: result.comments };
73
+ }
74
+ function displayText(value) {
75
+ return [...String(value ?? "")].map((character) => {
76
+ const code = character.codePointAt(0) ?? 0;
77
+ return isUnsafeTerminalCodePoint(code)
78
+ ? `\\u${code.toString(16).padStart(4, "0")}`
79
+ : character;
80
+ }).join("");
81
+ }
82
+ function isUnsafeTerminalCodePoint(code) {
83
+ return code <= 0x1f
84
+ || (code >= 0x7f && code <= 0x9f)
85
+ || (code >= 0x202a && code <= 0x202e)
86
+ || (code >= 0x2066 && code <= 0x2069);
87
+ }
88
+ function safeJson(value) {
89
+ return JSON.stringify(value, null, 2).replace(/[\u007f-\u009f\u202a-\u202e\u2066-\u2069]/gu, (character) => `\\u${(character.codePointAt(0) ?? 0).toString(16).padStart(4, "0")}`);
90
+ }
91
+ function printCommentLine(comment) {
92
+ const firstLine = comment.body.split(/\r\n|\r|\n/, 1)[0] ?? "";
93
+ console.log([
94
+ displayText(comment.id),
95
+ displayText(comment.status),
96
+ displayText(comment.createdAt),
97
+ displayText(comment.author?.name || "Unknown author"),
98
+ displayText(comment.anchor?.mode || "unknown"),
99
+ displayText(firstLine),
100
+ ].join(" | "));
101
+ }
102
+ async function run(action) {
103
+ try {
104
+ await action();
105
+ }
106
+ catch (error) {
107
+ console.error(error instanceof DeployCommentsError ? error.message : "Deploy comments command failed.");
108
+ process.exitCode = 1;
109
+ }
110
+ }
111
+ async function listAction(appNameOrId, options) {
112
+ const status = options.status ?? "all";
113
+ if (status !== "open" && status !== "resolved" && status !== "all") {
114
+ throw new DeployCommentsError("--status must be open, resolved, or all.");
115
+ }
116
+ const token = await ensureCognitoToken();
117
+ const orgHeaders = callerOrgHeaders(options.company);
118
+ const appId = await resolveAppId(token, appNameOrId, orgHeaders);
119
+ const result = await listComments(token, appId, orgHeaders);
120
+ const comments = status === "all"
121
+ ? result.comments
122
+ : result.comments.filter((comment) => comment.status === status);
123
+ if (options.json) {
124
+ console.log(safeJson({ commentsEnabled: result.commentsEnabled, comments }));
125
+ return;
126
+ }
127
+ if (comments.length === 0) {
128
+ console.log("No comments.");
129
+ return;
130
+ }
131
+ for (const comment of comments)
132
+ printCommentLine(comment);
133
+ }
134
+ async function readAction(appNameOrId, commentId, company) {
135
+ const token = await ensureCognitoToken();
136
+ const orgHeaders = callerOrgHeaders(company);
137
+ const appId = await resolveAppId(token, appNameOrId, orgHeaders);
138
+ const result = await listComments(token, appId, orgHeaders);
139
+ const comment = result.comments.find((entry) => entry.id === commentId);
140
+ if (!comment)
141
+ throw new DeployCommentsError(`Comment '${displayText(commentId)}' was not found on that app.`);
142
+ console.log(safeJson(comment));
143
+ }
144
+ export function registerDeployCommentsCommand(deploy) {
145
+ const comments = deploy.command("comments").description("List and read comments on your deployed apps");
146
+ comments
147
+ .command("list <app>")
148
+ .description("List comments on an app in your org")
149
+ .option("--company <slug>", "use this company instead of the active company")
150
+ .option("--json", "print the API comment array as JSON")
151
+ .option("--status <status>", "filter by open, resolved, or all", "all")
152
+ .action((app, options) => run(() => listAction(app, options)));
153
+ comments
154
+ .command("read <app> <commentId>")
155
+ .description("Read a comment on an app in your org")
156
+ .option("--company <slug>", "use this company instead of the active company")
157
+ .action((app, commentId, options) => run(() => readAction(app, commentId, options.company)));
158
+ }
159
+ //# sourceMappingURL=deploy-comments.js.map
@@ -4,7 +4,6 @@ import type { LaneCompanyRef, QuestionRecord } from "../lib/lanes/types.js";
4
4
  export interface LaneChecksDeps {
5
5
  hqRoot: () => string;
6
6
  resolveCompany: (company?: string) => Promise<LaneCompanyRef>;
7
- trackerEnabled: (company: LaneCompanyRef) => boolean | Promise<boolean>;
8
7
  now: () => Date;
9
8
  stopLane?: (laneId: string, reason: string) => Promise<void>;
10
9
  sessionState?: (root: string, sessionId: string, now: Date) => string | null;