@indigoai-us/hq-cli 5.345.134 → 5.345.136

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 (100) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/assets/scaffold/core/scripts/worktree-gc.sh +98 -8
  3. package/dist/command-catalog.generated.d.ts +62 -6
  4. package/dist/command-catalog.generated.js +79 -7
  5. package/dist/command-registration-plan.d.ts +6 -0
  6. package/dist/command-registration-plan.js +1 -0
  7. package/dist/commands/core-bucket-c-handoff-knowledge-native.d.ts +3 -1
  8. package/dist/commands/core-bucket-c-handoff-knowledge-native.js +8 -4
  9. package/dist/commands/core-checkpoint-native.d.ts +10 -0
  10. package/dist/commands/core-checkpoint-native.js +60 -15
  11. package/dist/commands/core-handoff.d.ts +2 -0
  12. package/dist/commands/core-handoff.js +3 -1
  13. package/dist/commands/core-pipeline-workflow-runner-native.js +7 -5
  14. package/dist/commands/daemon.js +3 -1
  15. package/dist/commands/fingerprint.d.ts +10 -0
  16. package/dist/commands/fingerprint.js +191 -0
  17. package/dist/commands/integrations-core.d.ts +22 -0
  18. package/dist/commands/integrations-core.js +25 -0
  19. package/dist/commands/integrations-manage.d.ts +24 -1
  20. package/dist/commands/integrations-manage.js +92 -17
  21. package/dist/commands/integrations.d.ts +11 -0
  22. package/dist/commands/integrations.js +100 -19
  23. package/dist/commands/lanes-loop.d.ts +9 -0
  24. package/dist/commands/lanes-loop.js +9 -1
  25. package/dist/commands/lanes.js +78 -55
  26. package/dist/commands/meetings.d.ts +2 -0
  27. package/dist/commands/meetings.js +57 -14
  28. package/dist/commands/mesh.d.ts +19 -1
  29. package/dist/commands/mesh.js +158 -70
  30. package/dist/commands/reindex.js +80 -5
  31. package/dist/commands/storage-offload.d.ts +93 -28
  32. package/dist/commands/storage-offload.js +290 -34
  33. package/dist/commands/storage.js +60 -7
  34. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2550 -1748
  35. package/dist/hq-cloud-sync-runner/package.json +1 -1
  36. package/dist/index.js +4 -0
  37. package/dist/lib/capacity/route.js +24 -0
  38. package/dist/lib/core-utils/session-company.d.ts +11 -0
  39. package/dist/lib/core-utils/session-company.js +34 -0
  40. package/dist/lib/core-utils/worktree-resume.js +44 -4
  41. package/dist/lib/core-utils/worktree-session-owner.d.ts +27 -0
  42. package/dist/lib/core-utils/worktree-session-owner.js +157 -0
  43. package/dist/lib/daemon/catalog.d.ts +2 -0
  44. package/dist/lib/daemon/catalog.js +3 -3
  45. package/dist/lib/daemon/hq-child-bootstrap-stage.d.ts +5 -0
  46. package/dist/lib/daemon/hq-child-bootstrap-stage.js +3 -11
  47. package/dist/lib/daemon/server.js +10 -1
  48. package/dist/lib/daemon/supervisor.js +6 -0
  49. package/dist/lib/daemon/sync-runner-bundle.d.ts +5 -0
  50. package/dist/lib/daemon/sync-runner-bundle.js +4 -12
  51. package/dist/lib/doctor/checks/claude-wiring.js +2 -1
  52. package/dist/lib/doctor/fix/apply.d.ts +20 -0
  53. package/dist/lib/doctor/fix/apply.js +63 -3
  54. package/dist/lib/doctor/fix/remediation.d.ts +1 -1
  55. package/dist/lib/doctor/fix/remediation.js +16 -0
  56. package/dist/lib/doctor/shared.d.ts +11 -1
  57. package/dist/lib/doctor/shared.js +16 -2
  58. package/dist/lib/fingerprint/fixtures.d.ts +19 -0
  59. package/dist/lib/fingerprint/fixtures.js +61 -0
  60. package/dist/lib/fingerprint/format.d.ts +50 -0
  61. package/dist/lib/fingerprint/format.js +105 -0
  62. package/dist/lib/fingerprint/offline.d.ts +16 -0
  63. package/dist/lib/fingerprint/offline.js +44 -0
  64. package/dist/lib/fingerprint/registry-client.d.ts +74 -0
  65. package/dist/lib/fingerprint/registry-client.js +141 -0
  66. package/dist/lib/fingerprint/resolve-local.d.ts +14 -0
  67. package/dist/lib/fingerprint/resolve-local.js +67 -0
  68. package/dist/lib/fingerprint/self-core.d.ts +139 -0
  69. package/dist/lib/fingerprint/self-core.js +371 -0
  70. package/dist/lib/fingerprint/self.d.ts +44 -0
  71. package/dist/lib/fingerprint/self.js +191 -0
  72. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  73. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  74. package/dist/lib/lanes/monitor.js +14 -2
  75. package/dist/lib/lanes/spawn.d.ts +19 -1
  76. package/dist/lib/lanes/spawn.js +50 -6
  77. package/dist/lib/lanes/story-ledger.d.ts +1 -0
  78. package/dist/lib/lanes/story-ledger.js +3 -1
  79. package/dist/lib/lanes/types.d.ts +12 -0
  80. package/dist/lib/lanes/types.js +12 -1
  81. package/dist/lib/mesh/live/daemon/install.d.ts +12 -0
  82. package/dist/lib/mesh/live/daemon/install.js +22 -0
  83. package/dist/lib/mesh/live/flush.js +106 -3
  84. package/dist/lib/mesh/live/session-events-client.d.ts +9 -0
  85. package/dist/lib/mesh/live/session-events-client.js +15 -0
  86. package/dist/lib/work-context/state.d.ts +10 -0
  87. package/dist/lib/work-context/state.js +5 -0
  88. package/dist/utils/cli-network-lifecycle.d.ts +2 -0
  89. package/dist/utils/cli-network-lifecycle.js +5 -1
  90. package/dist/utils/cognito-session.d.ts +2 -21
  91. package/dist/utils/cognito-session.js +3 -84
  92. package/dist/utils/fsync-directory.d.ts +17 -0
  93. package/dist/utils/fsync-directory.js +27 -0
  94. package/dist/utils/person-token-cache.d.ts +32 -0
  95. package/dist/utils/person-token-cache.js +95 -0
  96. package/dist/utils/vault-api.d.ts +21 -1
  97. package/dist/utils/vault-api.js +54 -5
  98. package/dist/utils/windows-hide-default.d.ts +42 -0
  99. package/dist/utils/windows-hide-default.js +98 -0
  100. package/package.json +2 -2
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Client for the hq-pro fingerprint registry (session-fingerprints US-006):
3
+ *
4
+ * POST /v1/fingerprints record this session's fingerprint
5
+ * GET /v1/fingerprints/{value} resolve a fingerprint to its record
6
+ *
7
+ * Every call is capped at REGISTRY_TIMEOUT_MS and never throws: failures come
8
+ * back as a typed result. vault-api is imported lazily, and self.ts and the
9
+ * command import this module lazily, so the offline builder (self-core.ts,
10
+ * offline.ts) never reaches it.
11
+ *
12
+ * Failures are written as one line to ~/.hq/fingerprint.log. A line names the
13
+ * operation, the HTTP status or the error class, and the server's error text,
14
+ * never a token or a request header.
15
+ */
16
+ import * as fs from "node:fs";
17
+ import * as os from "node:os";
18
+ import * as path from "node:path";
19
+ export const REGISTRY_TIMEOUT_MS = 3000;
20
+ export const FINGERPRINT_LOG_FILE = "fingerprint.log";
21
+ async function defaultFetch(opts) {
22
+ const { vaultApiFetch } = await import("../../utils/vault-api.js");
23
+ return vaultApiFetch(opts);
24
+ }
25
+ /** Runs the request with an AbortController that fires after the timeout. */
26
+ async function withDeadline(run, ms) {
27
+ const controller = new AbortController();
28
+ let timer;
29
+ const timeout = new Promise((_, reject) => {
30
+ timer = setTimeout(() => {
31
+ const err = new Error(`timed out after ${ms}ms`);
32
+ err.name = "TimeoutError";
33
+ controller.abort(err);
34
+ reject(err);
35
+ }, ms);
36
+ });
37
+ try {
38
+ return await Promise.race([run(controller.signal), timeout]);
39
+ }
40
+ finally {
41
+ clearTimeout(timer);
42
+ }
43
+ }
44
+ async function readJson(response) {
45
+ try {
46
+ return await response.json();
47
+ }
48
+ catch {
49
+ // A body that is not JSON (an HTML gateway page, an empty 502) has no
50
+ // fields to read; the caller reports the status instead.
51
+ return null;
52
+ }
53
+ }
54
+ function serverError(body, status) {
55
+ if (body && typeof body === "object") {
56
+ const record = body;
57
+ if (typeof record.error === "string")
58
+ return record.error;
59
+ if (typeof record.message === "string")
60
+ return record.message;
61
+ }
62
+ return `HTTP ${status}`;
63
+ }
64
+ function thrownError(err) {
65
+ const e = err instanceof Error ? err : new Error(String(err));
66
+ return { ok: false, status: null, error: `${e.name}: ${e.message}` };
67
+ }
68
+ export async function registerFingerprint(body, token, opts = {}) {
69
+ const fetchImpl = opts.fetch ?? defaultFetch;
70
+ try {
71
+ const response = await withDeadline((signal) => fetchImpl({
72
+ baseUrl: opts.baseUrl,
73
+ token,
74
+ path: "/v1/fingerprints",
75
+ method: "POST",
76
+ body: { ...body },
77
+ signal,
78
+ }), opts.timeoutMs ?? REGISTRY_TIMEOUT_MS);
79
+ const json = await readJson(response);
80
+ if (response.status === 200 || response.status === 201) {
81
+ return { ok: true, status: response.status, row: json };
82
+ }
83
+ return { ok: false, status: response.status, error: serverError(json, response.status) };
84
+ }
85
+ catch (err) {
86
+ return thrownError(err);
87
+ }
88
+ }
89
+ export async function lookupFingerprint(value, token, opts = {}) {
90
+ const fetchImpl = opts.fetch ?? defaultFetch;
91
+ try {
92
+ const response = await withDeadline((signal) => fetchImpl({
93
+ baseUrl: opts.baseUrl,
94
+ token,
95
+ path: `/v1/fingerprints/${encodeURIComponent(value)}`,
96
+ signal,
97
+ }), opts.timeoutMs ?? REGISTRY_TIMEOUT_MS);
98
+ const json = await readJson(response);
99
+ if (response.status === 200 && json && typeof json === "object") {
100
+ return { ok: true, found: true, record: json };
101
+ }
102
+ // Unknown values and fingerprints of other companies get the same 404.
103
+ if (response.status === 404)
104
+ return { ok: true, found: false };
105
+ return { ok: false, status: response.status, error: serverError(json, response.status) };
106
+ }
107
+ catch (err) {
108
+ return thrownError(err);
109
+ }
110
+ }
111
+ export function fingerprintLogPath(dir = path.join(os.homedir(), ".hq")) {
112
+ return path.join(dir, FINGERPRINT_LOG_FILE);
113
+ }
114
+ /** Strips newlines and anything shaped like a bearer token or a JWT. */
115
+ function sanitize(text) {
116
+ return text
117
+ .replace(/[\r\n]+/g, " ")
118
+ .replace(/Bearer\s+\S+/gi, "Bearer <redacted>")
119
+ .replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*/g, "<redacted>")
120
+ .replace(/\bhqk_[A-Za-z0-9_-]+/g, "<redacted>")
121
+ .slice(0, 300);
122
+ }
123
+ /**
124
+ * Appends one line to ~/.hq/fingerprint.log. A log that cannot be written is
125
+ * dropped: registration is fire-and-forget and must not print or fail.
126
+ */
127
+ export function appendFingerprintLog(op, detail, opts = {}) {
128
+ const file = fingerprintLogPath(opts.dir);
129
+ const at = (opts.now ?? (() => new Date()))().toISOString();
130
+ try {
131
+ fs.mkdirSync(path.dirname(file), { recursive: true });
132
+ fs.appendFileSync(file, `${at} ${op} ${sanitize(detail)}\n`, { mode: 0o600 });
133
+ }
134
+ catch {
135
+ // Nowhere left to report it without changing the command's output.
136
+ }
137
+ }
138
+ export function describeFailure(result) {
139
+ return result.status === null ? result.error : `status=${result.status} ${result.error}`;
140
+ }
141
+ //# sourceMappingURL=registry-client.js.map
@@ -0,0 +1,14 @@
1
+ import type { HostKind, IdentityKind } from "./format.js";
2
+ export interface LocalFingerprintRecord {
3
+ fingerprint: string;
4
+ identityUid: string | null;
5
+ identityKind: IdentityKind | null;
6
+ machineId: string | null;
7
+ hostKind: HostKind | null;
8
+ sessionId: string | null;
9
+ company: string | null;
10
+ builtAt: string | null;
11
+ source: "local";
12
+ }
13
+ export declare function resolveLocalFingerprint(hqRoot: string, value: string): LocalFingerprintRecord | null;
14
+ //# sourceMappingURL=resolve-local.d.ts.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Resolve a fingerprint against this machine's session records
3
+ * (workspace/sessions/<sid>/fingerprint.yaml). Exact value match only.
4
+ */
5
+ import * as fs from "node:fs";
6
+ import * as path from "node:path";
7
+ import yaml from "js-yaml";
8
+ import { FINGERPRINT_FILE } from "./self-core.js";
9
+ function str(value) {
10
+ return typeof value === "string" && value ? value : null;
11
+ }
12
+ export function resolveLocalFingerprint(hqRoot, value) {
13
+ const sessionsDir = path.join(hqRoot, "workspace", "sessions");
14
+ let entries;
15
+ try {
16
+ entries = fs.readdirSync(sessionsDir, { withFileTypes: true });
17
+ }
18
+ catch (err) {
19
+ if (err.code === "ENOENT")
20
+ return null;
21
+ throw err;
22
+ }
23
+ for (const entry of entries) {
24
+ if (!entry.isDirectory())
25
+ continue;
26
+ const file = path.join(sessionsDir, entry.name, FINGERPRINT_FILE);
27
+ let raw;
28
+ try {
29
+ raw = fs.readFileSync(file, "utf8");
30
+ }
31
+ catch (err) {
32
+ if (err.code !== "ENOENT") {
33
+ process.stderr.write(`hq fingerprint: skipping unreadable ${file}: ${err.message}\n`);
34
+ }
35
+ continue;
36
+ }
37
+ // A cheap substring check skips the YAML parse for the many sessions that do not match.
38
+ if (!raw.includes(value))
39
+ continue;
40
+ let record;
41
+ try {
42
+ record = yaml.load(raw);
43
+ }
44
+ catch (err) {
45
+ process.stderr.write(`hq fingerprint: skipping unreadable ${file}: ${err.message}\n`);
46
+ continue;
47
+ }
48
+ if (!record || typeof record !== "object")
49
+ continue;
50
+ const r = record;
51
+ if (r.fingerprint !== value)
52
+ continue;
53
+ return {
54
+ fingerprint: value,
55
+ identityUid: str(r.identity_uid),
56
+ identityKind: str(r.identity_kind) ?? null,
57
+ machineId: str(r.machine_id),
58
+ hostKind: str(r.host_kind) ?? null,
59
+ sessionId: str(r.session_id),
60
+ company: str(r.company),
61
+ builtAt: str(r.built_at),
62
+ source: "local",
63
+ };
64
+ }
65
+ return null;
66
+ }
67
+ //# sourceMappingURL=resolve-local.js.map
@@ -0,0 +1,139 @@
1
+ import { type HostKind, type IdentityKind } from "./format.js";
2
+ export declare const SESSION_ID_ENV_VARS: readonly ["HQ_SESSION_ID", "CLAUDE_CODE_SESSION_ID", "CLAUDE_SESSION_ID", "CODEX_SESSION_ID", "CODEX_THREAD_ID", "GROK_SESSION_ID"];
3
+ export declare const IDENTITY_LOOKUP_TIMEOUT_MS = 3000;
4
+ export declare const FINGERPRINT_FILE = "fingerprint.yaml";
5
+ export declare const IDENTITY_UID_RE: RegExp;
6
+ export type IdentitySource = "token" | "machine-creds" | "cache" | "lookup" | "none";
7
+ export interface SelfFingerprint {
8
+ fingerprint: string | null;
9
+ company: string | null;
10
+ identityUid: string | null;
11
+ identityKind: IdentityKind | null;
12
+ machineId: string | null;
13
+ hostKind: HostKind;
14
+ sessionId: string | null;
15
+ source: IdentitySource;
16
+ reason?: string;
17
+ }
18
+ export interface MachineCredsSummary {
19
+ entityType?: string;
20
+ entityUid?: string;
21
+ }
22
+ export interface SelfFingerprintDeps {
23
+ hqRoot: string;
24
+ env?: NodeJS.ProcessEnv;
25
+ offline?: boolean;
26
+ /** Returns the cached ID token, or null when signed out. */
27
+ loadIdToken?: () => string | null | Promise<string | null>;
28
+ /** Returns the machine creds, or null when this host has none or they are unreadable. */
29
+ loadMachineCreds?: () => MachineCredsSummary | null | Promise<MachineCredsSummary | null>;
30
+ /** Resolves the caller's uid over the network. The signal aborts on timeout. */
31
+ resolveIdentity?: (idToken: string, signal: AbortSignal) => Promise<{
32
+ kind: IdentityKind;
33
+ uid: string;
34
+ }>;
35
+ readMachineId?: (hqRoot: string) => string | null;
36
+ /**
37
+ * Never create or write a file: read an existing machine id only, return a
38
+ * null fingerprint without one, and skip the session cache write. For the
39
+ * handoff commits, which must leave the tree as the shell script does.
40
+ */
41
+ readOnly?: boolean;
42
+ /**
43
+ * The caller found no HQ root, so `hqRoot` names no HQ tree. The machine is
44
+ * `-`, nothing under `hqRoot` is read or written, and the result carries
45
+ * `reason: "no HQ root"`.
46
+ */
47
+ noRoot?: boolean;
48
+ lookupTimeoutMs?: number;
49
+ now?: () => Date;
50
+ warn?: (message: string) => void;
51
+ }
52
+ interface FingerprintCacheFile {
53
+ fingerprint?: string | null;
54
+ company?: string | null;
55
+ identity_uid?: string | null;
56
+ identity_kind?: IdentityKind | null;
57
+ identity_lookup_done?: boolean;
58
+ /** sha256 of the ID token `sub` the identity was resolved for, or null without one. */
59
+ token_owner?: string | null;
60
+ machine_id?: string | null;
61
+ host_kind?: HostKind;
62
+ session_id?: string;
63
+ built_at?: string;
64
+ /** When hq-pro recorded this fingerprint (US-007). Set by the registry step in self.ts. */
65
+ registered_at?: string;
66
+ /** Why registration will not be tried again for this fingerprint, such as "outpost". */
67
+ registration_skipped?: string;
68
+ }
69
+ /** Registration keys carried across rewrites while the fingerprint stays the same. */
70
+ type RegistrationKeys = Pick<FingerprintCacheFile, "registered_at" | "registration_skipped">;
71
+ export declare function resolveSessionId(env: NodeJS.ProcessEnv): string | null;
72
+ export declare function sessionDir(hqRoot: string, sessionId: string): string;
73
+ /**
74
+ * Read `<hqRoot>/.hq/machine-id`, creating it when missing. Mirrors
75
+ * getOrCreateMachineId in hq-cloud, which the package does not export (its
76
+ * exports map has no ./lib/machine-id entry). Falls back to the menubar id in
77
+ * ~/.hq/menubar.json before generating a new UUID, as hq-cloud does.
78
+ */
79
+ export declare function readOrCreateMachineId(hqRoot: string): string | null;
80
+ /** Read `<hqRoot>/.hq/machine-id` without creating it. Null when it is missing or empty. */
81
+ export declare function readMachineIdIfPresent(hqRoot: string): string | null;
82
+ /** Machine ids are usually UUIDs. A free-form id is hashed so the fingerprint keeps 8 hex chars. */
83
+ export declare function machineIdForFingerprint(machineId: string): string;
84
+ export interface IdentityContext {
85
+ env: NodeJS.ProcessEnv;
86
+ hqRoot: string;
87
+ warn: (message: string) => void;
88
+ sessionId: string | null;
89
+ idToken: string | null;
90
+ owner: string | null;
91
+ hostKind: HostKind;
92
+ cacheFile: string | null;
93
+ identityUid: string | null;
94
+ source: IdentitySource;
95
+ lookupDone: boolean;
96
+ /** True when no local source named the identity and a person lookup may run. */
97
+ needsLookup: boolean;
98
+ /** The cached fingerprint and its registration keys, kept when the value is unchanged. */
99
+ cachedRegistration?: {
100
+ fingerprint: string | null;
101
+ } & RegistrationKeys;
102
+ }
103
+ export declare function identityContext(deps: SelfFingerprintDeps, idToken: string | null, creds: MachineCredsSummary | null): IdentityContext;
104
+ export interface SelfFingerprintSyncDeps extends Omit<SelfFingerprintDeps, "offline" | "loadIdToken" | "loadMachineCreds" | "resolveIdentity" | "lookupTimeoutMs"> {
105
+ /** Returns the cached ID token, or null when signed out. */
106
+ loadIdToken: () => string | null;
107
+ /** Returns the machine creds, or null when this host has none or they are unreadable. */
108
+ loadMachineCreds: () => MachineCredsSummary | null;
109
+ }
110
+ /**
111
+ * The offline build as a synchronous call, for callers that cannot await
112
+ * (lane start). It never does the person lookup, so the loaders are required:
113
+ * the default ones live in self.ts, which loads them lazily and only
114
+ * asynchronously.
115
+ */
116
+ export declare function buildSelfFingerprintOfflineSync(deps: SelfFingerprintSyncDeps): SelfFingerprint;
117
+ export declare function finishFingerprint(deps: SelfFingerprintDeps, ctx: IdentityContext): SelfFingerprint;
118
+ export interface FingerprintRegistrationState {
119
+ registeredAt: string | null;
120
+ skipped: string | null;
121
+ }
122
+ /**
123
+ * The registration keys of this session's fingerprint.yaml, or null when the
124
+ * file is missing or holds another fingerprint (the registry step then has no
125
+ * record to mark).
126
+ */
127
+ export declare function readRegistrationState(hqRoot: string, sessionId: string, fingerprint: string, warn: (message: string) => void): FingerprintRegistrationState | null;
128
+ /**
129
+ * Writes registered_at or registration_skipped into fingerprint.yaml. Re-reads
130
+ * the file first and writes nothing when it now holds another fingerprint.
131
+ * Returns true when the file was rewritten.
132
+ */
133
+ export declare function recordRegistrationState(hqRoot: string, sessionId: string, fingerprint: string, update: {
134
+ registeredAt: string;
135
+ } | {
136
+ skipped: string;
137
+ }, warn: (message: string) => void): boolean;
138
+ export {};
139
+ //# sourceMappingURL=self-core.d.ts.map