@indigoai-us/hq-cloud 5.19.1 → 5.21.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 (128) hide show
  1. package/.github/workflows/ci.yml +8 -4
  2. package/.github/workflows/publish.yml +9 -3
  3. package/dist/bin/sync-runner.d.ts +9 -0
  4. package/dist/bin/sync-runner.d.ts.map +1 -1
  5. package/dist/bin/sync-runner.js +58 -0
  6. package/dist/bin/sync-runner.js.map +1 -1
  7. package/dist/entity-resolver.d.ts +53 -0
  8. package/dist/entity-resolver.d.ts.map +1 -0
  9. package/dist/entity-resolver.js +127 -0
  10. package/dist/entity-resolver.js.map +1 -0
  11. package/dist/entity-resolver.test.d.ts +10 -0
  12. package/dist/entity-resolver.test.d.ts.map +1 -0
  13. package/dist/entity-resolver.test.js +244 -0
  14. package/dist/entity-resolver.test.js.map +1 -0
  15. package/dist/index.d.ts +17 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +21 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/schemas/signal-types.d.ts +16 -0
  20. package/dist/schemas/signal-types.d.ts.map +1 -0
  21. package/dist/schemas/signal-types.js +30 -0
  22. package/dist/schemas/signal-types.js.map +1 -0
  23. package/dist/schemas/signal-types.test.d.ts +2 -0
  24. package/dist/schemas/signal-types.test.d.ts.map +1 -0
  25. package/dist/schemas/signal-types.test.js +65 -0
  26. package/dist/schemas/signal-types.test.js.map +1 -0
  27. package/dist/schemas/source-channels.d.ts +15 -0
  28. package/dist/schemas/source-channels.d.ts.map +1 -0
  29. package/dist/schemas/source-channels.js +28 -0
  30. package/dist/schemas/source-channels.js.map +1 -0
  31. package/dist/schemas/source-channels.test.d.ts +2 -0
  32. package/dist/schemas/source-channels.test.d.ts.map +1 -0
  33. package/dist/schemas/source-channels.test.js +65 -0
  34. package/dist/schemas/source-channels.test.js.map +1 -0
  35. package/dist/signals/get.d.ts +13 -0
  36. package/dist/signals/get.d.ts.map +1 -0
  37. package/dist/signals/get.js +74 -0
  38. package/dist/signals/get.js.map +1 -0
  39. package/dist/signals/get.test.d.ts +5 -0
  40. package/dist/signals/get.test.d.ts.map +1 -0
  41. package/dist/signals/get.test.js +170 -0
  42. package/dist/signals/get.test.js.map +1 -0
  43. package/dist/signals/internals.d.ts +16 -0
  44. package/dist/signals/internals.d.ts.map +1 -0
  45. package/dist/signals/internals.js +39 -0
  46. package/dist/signals/internals.js.map +1 -0
  47. package/dist/signals/list.d.ts +10 -0
  48. package/dist/signals/list.d.ts.map +1 -0
  49. package/dist/signals/list.js +76 -0
  50. package/dist/signals/list.js.map +1 -0
  51. package/dist/signals/list.test.d.ts +9 -0
  52. package/dist/signals/list.test.d.ts.map +1 -0
  53. package/dist/signals/list.test.js +227 -0
  54. package/dist/signals/list.test.js.map +1 -0
  55. package/dist/signals/parse.d.ts +8 -0
  56. package/dist/signals/parse.d.ts.map +1 -0
  57. package/dist/signals/parse.js +8 -0
  58. package/dist/signals/parse.js.map +1 -0
  59. package/dist/signals/types.d.ts +69 -0
  60. package/dist/signals/types.d.ts.map +1 -0
  61. package/dist/signals/types.js +10 -0
  62. package/dist/signals/types.js.map +1 -0
  63. package/dist/sources/get.d.ts +11 -0
  64. package/dist/sources/get.d.ts.map +1 -0
  65. package/dist/sources/get.js +67 -0
  66. package/dist/sources/get.js.map +1 -0
  67. package/dist/sources/get.test.d.ts +5 -0
  68. package/dist/sources/get.test.d.ts.map +1 -0
  69. package/dist/sources/get.test.js +132 -0
  70. package/dist/sources/get.test.js.map +1 -0
  71. package/dist/sources/internals.d.ts +16 -0
  72. package/dist/sources/internals.d.ts.map +1 -0
  73. package/dist/sources/internals.js +39 -0
  74. package/dist/sources/internals.js.map +1 -0
  75. package/dist/sources/list.d.ts +10 -0
  76. package/dist/sources/list.d.ts.map +1 -0
  77. package/dist/sources/list.js +76 -0
  78. package/dist/sources/list.js.map +1 -0
  79. package/dist/sources/list.test.d.ts +8 -0
  80. package/dist/sources/list.test.d.ts.map +1 -0
  81. package/dist/sources/list.test.js +198 -0
  82. package/dist/sources/list.test.js.map +1 -0
  83. package/dist/sources/parse.d.ts +18 -0
  84. package/dist/sources/parse.d.ts.map +1 -0
  85. package/dist/sources/parse.js +35 -0
  86. package/dist/sources/parse.js.map +1 -0
  87. package/dist/sources/types.d.ts +62 -0
  88. package/dist/sources/types.d.ts.map +1 -0
  89. package/dist/sources/types.js +8 -0
  90. package/dist/sources/types.js.map +1 -0
  91. package/dist/telemetry.d.ts +87 -0
  92. package/dist/telemetry.d.ts.map +1 -0
  93. package/dist/telemetry.js +349 -0
  94. package/dist/telemetry.js.map +1 -0
  95. package/dist/telemetry.test.d.ts +11 -0
  96. package/dist/telemetry.test.d.ts.map +1 -0
  97. package/dist/telemetry.test.js +309 -0
  98. package/dist/telemetry.test.js.map +1 -0
  99. package/dist/vault-client.d.ts +43 -0
  100. package/dist/vault-client.d.ts.map +1 -1
  101. package/dist/vault-client.js +28 -0
  102. package/dist/vault-client.js.map +1 -1
  103. package/package.json +5 -3
  104. package/src/bin/sync-runner.ts +73 -0
  105. package/src/entity-resolver.test.ts +315 -0
  106. package/src/entity-resolver.ts +180 -0
  107. package/src/index.ts +76 -0
  108. package/src/schemas/signal-types.test.ts +82 -0
  109. package/src/schemas/signal-types.ts +38 -0
  110. package/src/schemas/source-channels.test.ts +82 -0
  111. package/src/schemas/source-channels.ts +36 -0
  112. package/src/signals/get.test.ts +204 -0
  113. package/src/signals/get.ts +79 -0
  114. package/src/signals/internals.ts +46 -0
  115. package/src/signals/list.test.ts +283 -0
  116. package/src/signals/list.ts +92 -0
  117. package/src/signals/parse.ts +8 -0
  118. package/src/signals/types.ts +74 -0
  119. package/src/sources/get.test.ts +166 -0
  120. package/src/sources/get.ts +75 -0
  121. package/src/sources/internals.ts +46 -0
  122. package/src/sources/list.test.ts +247 -0
  123. package/src/sources/list.ts +95 -0
  124. package/src/sources/parse.ts +43 -0
  125. package/src/sources/types.ts +67 -0
  126. package/src/telemetry.test.ts +394 -0
  127. package/src/telemetry.ts +436 -0
  128. package/src/vault-client.ts +60 -0
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Usage telemetry collector — TypeScript port of the Tauri Rust collector that
3
+ * used to live at `hq-workspace/apps/hq-sync/src-tauri/src/commands/telemetry.rs`.
4
+ *
5
+ * Why it moved: the Rust copy only ran inside the macOS menubar app. By moving
6
+ * the logic into `@indigoai-us/hq-cloud`, every consumer of the package
7
+ * (`hq-sync-runner`, `hq-cli`, mobile wrappers) emits telemetry uniformly.
8
+ *
9
+ * What it does: after each successful sync (`all-complete` arm of
10
+ * `bin/sync-runner.ts`), walks `~/.claude/projects/**\/*.jsonl`, diffs each
11
+ * file against a persisted byte-offset cursor at `~/.hq/telemetry-cursor.json`,
12
+ * sanitizes new rows through a tight allowlist that matches the server's
13
+ * KEEP_FIELDS set in `apps/hq-pro/src/vault-service/handlers/usage.ts`,
14
+ * batches into ≤1 MiB POST bodies, and ships them to `/v1/usage`.
15
+ *
16
+ * Trust model: the caller's `personUid` is resolved on the server from the
17
+ * Cognito JWT — never from the body. `sanitizeRow` strips prompt bodies,
18
+ * thinking content, tool inputs/outputs, and any nested `message` object so
19
+ * the wire payload contains only token-accounting fields.
20
+ *
21
+ * Errors are swallowed by design — telemetry must never abort or delay a
22
+ * sync. The cursor is only advanced for batches the server 2xx'd, so a
23
+ * transient outage retries automatically on the next sync.
24
+ */
25
+ import { promises as fs } from "node:fs";
26
+ import * as os from "node:os";
27
+ import * as path from "node:path";
28
+ function emptyCursor() {
29
+ return { version: "1", files: {} };
30
+ }
31
+ async function loadCursor(cursorPath) {
32
+ try {
33
+ const raw = await fs.readFile(cursorPath, "utf-8");
34
+ const parsed = JSON.parse(raw);
35
+ if (parsed && typeof parsed === "object" && parsed.files && typeof parsed.files === "object") {
36
+ return { version: parsed.version ?? "1", files: parsed.files };
37
+ }
38
+ }
39
+ catch {
40
+ // Missing / unparseable — start fresh.
41
+ }
42
+ return emptyCursor();
43
+ }
44
+ async function saveCursor(cursorPath, cursor) {
45
+ // Atomic write: tmp + rename. The Rust impl uses the same .tmp suffix; we
46
+ // keep it for cross-implementation grep-ability.
47
+ await fs.mkdir(path.dirname(cursorPath), { recursive: true });
48
+ const tmp = `${cursorPath}.tmp`;
49
+ await fs.writeFile(tmp, JSON.stringify(cursor, null, 2), "utf-8");
50
+ await fs.rename(tmp, cursorPath);
51
+ }
52
+ // ── Local opt-in fallback ─────────────────────────────────────────────────────
53
+ async function readLocalTelemetryEnabled(menubarPath) {
54
+ try {
55
+ const raw = await fs.readFile(menubarPath, "utf-8");
56
+ const parsed = JSON.parse(raw);
57
+ return parsed.telemetryEnabled === true;
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ // ── Sanitizer ─────────────────────────────────────────────────────────────────
64
+ /** Top-level fields the server accepts. Keep aligned with `KEEP_FIELDS` in
65
+ * `apps/hq-pro/src/vault-service/handlers/usage.ts` — any drift will surface
66
+ * as an `unexpected-event-field` rejection in `UsageIngestResult.skipped`. */
67
+ const KEEP_TOP_LEVEL = [
68
+ "sessionId",
69
+ "timestamp",
70
+ "uuid",
71
+ "cwd",
72
+ "gitBranch",
73
+ "userType",
74
+ ];
75
+ /**
76
+ * Build an outgoing event row matching the server's KEEP allowlist.
77
+ *
78
+ * Two transforms:
79
+ * 1. Top-level fields are copied straight through (string identity).
80
+ * 2. `message.model` and `message.usage.{input_tokens, output_tokens,
81
+ * cache_creation_input_tokens, cache_read_input_tokens}` are promoted to
82
+ * camelCase top-level fields. The original `message` object — which
83
+ * carries prompt/response text, thinking, and tool data — is dropped.
84
+ *
85
+ * Returns `null` when the input isn't an object. Empty results (e.g. a row
86
+ * with no recognised fields) are still returned as `{}` and emitted; the
87
+ * server accepts empty rows and they're useful as a "Claude Code was run at
88
+ * this time" heartbeat.
89
+ */
90
+ export function sanitizeRow(row) {
91
+ if (!row || typeof row !== "object" || Array.isArray(row))
92
+ return null;
93
+ const obj = row;
94
+ const out = {};
95
+ for (const key of KEEP_TOP_LEVEL) {
96
+ if (key in obj) {
97
+ out[key] = obj[key];
98
+ }
99
+ }
100
+ const message = obj.message;
101
+ if (message && typeof message === "object" && !Array.isArray(message)) {
102
+ const m = message;
103
+ if ("model" in m)
104
+ out.model = m.model;
105
+ const usage = m.usage;
106
+ if (usage && typeof usage === "object" && !Array.isArray(usage)) {
107
+ const u = usage;
108
+ if ("input_tokens" in u)
109
+ out.inputTokens = u.input_tokens;
110
+ if ("output_tokens" in u)
111
+ out.outputTokens = u.output_tokens;
112
+ if ("cache_creation_input_tokens" in u) {
113
+ out.cacheCreationInputTokens = u.cache_creation_input_tokens;
114
+ }
115
+ if ("cache_read_input_tokens" in u) {
116
+ out.cacheReadInputTokens = u.cache_read_input_tokens;
117
+ }
118
+ }
119
+ }
120
+ return out;
121
+ }
122
+ // ── File walker ───────────────────────────────────────────────────────────────
123
+ /** Recursively collect every `.jsonl` file under `root`. Skips errors silently
124
+ * (missing dir, EACCES on a stray subdir) — anything we can't enter is
125
+ * treated as absent rather than fatal, matching the Rust glob behavior. */
126
+ async function listJsonlFiles(root) {
127
+ const out = [];
128
+ async function walk(dir) {
129
+ let entries;
130
+ try {
131
+ entries = await fs.readdir(dir, { withFileTypes: true });
132
+ }
133
+ catch {
134
+ return;
135
+ }
136
+ for (const ent of entries) {
137
+ const full = path.join(dir, ent.name);
138
+ if (ent.isDirectory()) {
139
+ await walk(full);
140
+ }
141
+ else if (ent.isFile() && ent.name.endsWith(".jsonl")) {
142
+ out.push(full);
143
+ }
144
+ }
145
+ }
146
+ await walk(root);
147
+ return out;
148
+ }
149
+ // ── Batching primitives ───────────────────────────────────────────────────────
150
+ const MAX_BATCH_BYTES = 1_000_000;
151
+ /**
152
+ * Byte cost of the fixed wire-payload skeleton:
153
+ * {"machineId":"…","installerVersion":"…","events":[]}
154
+ *
155
+ * The Rust implementation re-serializes the entire growing batch on every
156
+ * row to check size — O(n²) bytes of JSON.stringify work per batch, which
157
+ * for 60K events takes ~4 minutes wall-clock in V8. This computes the same
158
+ * payload size incrementally instead: skeleton + Σ(per-row JSON length) +
159
+ * commas between rows. Same threshold semantics, O(n) total cost.
160
+ */
161
+ function envelopeBytes(machineId, installerVersion) {
162
+ // Serialize the empty-events envelope once and measure it. Captures the
163
+ // exact JSON whitespace / escaping V8 produces so we match what would
164
+ // actually go over the wire.
165
+ return Buffer.byteLength(JSON.stringify({ machineId, installerVersion, events: [] }), "utf-8");
166
+ }
167
+ // ── Main entry point ──────────────────────────────────────────────────────────
168
+ /**
169
+ * Scan, sanitize, and POST any new Claude Code session rows.
170
+ *
171
+ * Fire-and-forget from the caller's perspective: errors are caught internally
172
+ * and surfaced only via `log`. The returned summary lets observers (e.g.
173
+ * sync-runner) decide whether to record a "telemetry attempted" breadcrumb,
174
+ * but no consumer is expected to react to it.
175
+ */
176
+ export async function collectAndSendTelemetry(opts) {
177
+ const home = os.homedir();
178
+ const claudeProjectsRoot = opts.claudeProjectsRoot ?? path.join(home, ".claude", "projects");
179
+ const cursorPath = opts.cursorPath ?? path.join(home, ".hq", "telemetry-cursor.json");
180
+ const menubarPath = opts.menubarPath ?? path.join(home, ".hq", "menubar.json");
181
+ const log = opts.log ?? (() => { });
182
+ // 1. Opt-in check (server-authoritative, with local fallback).
183
+ let enabled;
184
+ let optInSource;
185
+ try {
186
+ const resp = await opts.client.getTelemetryOptIn();
187
+ enabled = resp.enabled === true;
188
+ optInSource = "server";
189
+ }
190
+ catch (err) {
191
+ log(`[telemetry] opt-in check failed (${err.message ?? err}) — falling back to local menubar.json`);
192
+ enabled = await readLocalTelemetryEnabled(menubarPath);
193
+ optInSource = "menubar-fallback";
194
+ }
195
+ if (!enabled) {
196
+ return { enabled: false, optInSource, filesScanned: 0, eventsSent: 0, batchesSent: 0 };
197
+ }
198
+ // 2. Cursor + file enumeration.
199
+ const cursor = await loadCursor(cursorPath);
200
+ const loadedFiles = { ...cursor.files };
201
+ const rotationResets = {};
202
+ const newlyCommitted = {};
203
+ const files = await listJsonlFiles(claudeProjectsRoot);
204
+ // 3. Walk each file, sanitize new rows, batch, flush at 1 MiB.
205
+ //
206
+ // Byte accounting is incremental: we track `batchBytes` as the projected
207
+ // serialized size of the current batch (envelope + per-row JSON + commas).
208
+ // Each row contributes its own JSON.stringify length once; we never
209
+ // re-serialize the growing batch. This is the O(n) replacement for the
210
+ // O(n²) projected-payload check the Rust impl uses (which spent ~4 min
211
+ // on a 60K-event first-run in the E2E smoke against hq-prod).
212
+ const ENVELOPE_BYTES = envelopeBytes(opts.machineId, opts.installerVersion);
213
+ let batchEvents = [];
214
+ let batchSources = [];
215
+ let batchBytes = ENVELOPE_BYTES;
216
+ let eventsSent = 0;
217
+ let batchesSent = 0;
218
+ const flush = async () => {
219
+ if (batchEvents.length === 0)
220
+ return;
221
+ const events = batchEvents;
222
+ const sources = batchSources;
223
+ batchEvents = [];
224
+ batchSources = [];
225
+ batchBytes = ENVELOPE_BYTES;
226
+ try {
227
+ await opts.client.postUsage({
228
+ machineId: opts.machineId,
229
+ installerVersion: opts.installerVersion,
230
+ events,
231
+ });
232
+ batchesSent++;
233
+ eventsSent += events.length;
234
+ // Advance cursor to max(endOffset) per file in this batch.
235
+ const maxPerFile = new Map();
236
+ for (const src of sources) {
237
+ const cur = maxPerFile.get(src.filePath);
238
+ if (!cur || src.endOffset > cur.offset) {
239
+ maxPerFile.set(src.filePath, { mtime: src.mtime, offset: src.endOffset });
240
+ }
241
+ }
242
+ for (const [fp, entry] of maxPerFile) {
243
+ newlyCommitted[fp] = { offset: entry.offset, mtime: entry.mtime };
244
+ }
245
+ }
246
+ catch (err) {
247
+ log(`[telemetry] postUsage failed (${err.message ?? err}) — cursor not advanced for ${sources.length} rows`);
248
+ // Cursor intentionally left un-advanced — next sync retries.
249
+ }
250
+ };
251
+ for (const filePath of files) {
252
+ let stat;
253
+ try {
254
+ stat = await fs.stat(filePath);
255
+ }
256
+ catch {
257
+ continue;
258
+ }
259
+ const currentSize = stat.size;
260
+ const currentMtime = Math.floor(stat.mtimeMs / 1000);
261
+ const stored = cursor.files[filePath] ?? { offset: 0, mtime: 0 };
262
+ let offset = stored.offset;
263
+ const rotated = currentSize < offset || (stored.mtime > 0 && currentMtime < stored.mtime);
264
+ if (rotated) {
265
+ offset = 0;
266
+ rotationResets[filePath] = { offset: 0, mtime: currentMtime };
267
+ }
268
+ if (offset >= currentSize && !rotated)
269
+ continue;
270
+ let content;
271
+ try {
272
+ const fh = await fs.open(filePath, "r");
273
+ try {
274
+ const length = Math.max(0, currentSize - offset);
275
+ const buf = Buffer.alloc(length);
276
+ await fh.read(buf, 0, length, offset);
277
+ content = buf.toString("utf-8");
278
+ }
279
+ finally {
280
+ await fh.close();
281
+ }
282
+ }
283
+ catch {
284
+ continue;
285
+ }
286
+ if (content.length === 0)
287
+ continue;
288
+ const segments = content.split("\n");
289
+ const lineEndOffsets = [];
290
+ let cumulative = 0;
291
+ for (let i = 0; i < segments.length; i++) {
292
+ cumulative += Buffer.byteLength(segments[i], "utf-8");
293
+ if (i < segments.length - 1)
294
+ cumulative += 1; // newline byte
295
+ lineEndOffsets.push(offset + cumulative);
296
+ }
297
+ for (let i = 0; i < segments.length; i++) {
298
+ const trimmed = segments[i].trim();
299
+ if (trimmed.length === 0)
300
+ continue;
301
+ let parsed;
302
+ try {
303
+ parsed = JSON.parse(trimmed);
304
+ }
305
+ catch {
306
+ continue;
307
+ }
308
+ const sanitized = sanitizeRow(parsed);
309
+ if (!sanitized)
310
+ continue;
311
+ // Cost of appending this row to the current batch: the row's JSON
312
+ // length plus 1 byte for the leading comma when there's already at
313
+ // least one row. (No comma when the batch is empty — the row sits
314
+ // alone inside the events array.)
315
+ const rowJsonBytes = Buffer.byteLength(JSON.stringify(sanitized), "utf-8");
316
+ const addCost = rowJsonBytes + (batchEvents.length > 0 ? 1 : 0);
317
+ if (batchEvents.length > 0 && batchBytes + addCost > MAX_BATCH_BYTES) {
318
+ await flush();
319
+ // After flush, batchEvents is empty → no comma needed for the first row.
320
+ batchBytes = ENVELOPE_BYTES + rowJsonBytes;
321
+ }
322
+ else {
323
+ batchBytes += addCost;
324
+ }
325
+ batchEvents.push(sanitized);
326
+ batchSources.push({
327
+ filePath,
328
+ endOffset: lineEndOffsets[i],
329
+ mtime: currentMtime,
330
+ });
331
+ }
332
+ }
333
+ await flush();
334
+ // 4. Persist cursor: loaded < rotation_resets < newly_committed.
335
+ const finalFiles = { ...loadedFiles };
336
+ for (const [fp, entry] of Object.entries(rotationResets))
337
+ finalFiles[fp] = entry;
338
+ for (const [fp, entry] of Object.entries(newlyCommitted))
339
+ finalFiles[fp] = entry;
340
+ await saveCursor(cursorPath, { version: "1", files: finalFiles });
341
+ return {
342
+ enabled: true,
343
+ optInSource,
344
+ filesScanned: files.length,
345
+ eventsSent,
346
+ batchesSent,
347
+ };
348
+ }
349
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tests for the usage telemetry collector. Mirrors the Rust test matrix at
3
+ * `hq-workspace/apps/hq-sync/src-tauri/src/commands/telemetry.rs::tests` so a
4
+ * behavioral regression in either implementation surfaces in both suites.
5
+ *
6
+ * The vault client is stubbed structurally rather than via fetch-mock so the
7
+ * tests stay deterministic without spinning up wiremock — the production
8
+ * boundary is `TelemetryClientSurface`, not the HTTP layer.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=telemetry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.test.d.ts","sourceRoot":"","sources":["../src/telemetry.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}