@revfleet/hscli 0.5.3 → 0.7.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/CHANGELOG.md +169 -0
- package/README.md +3 -3
- package/dist/cli.js +8 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/audit/index.d.ts +3 -0
- package/dist/commands/audit/index.js +226 -0
- package/dist/commands/audit/index.js.map +1 -0
- package/dist/commands/auth/index.js +2 -2
- package/dist/commands/auth/index.js.map +1 -1
- package/dist/commands/crm/sync.js +1 -1
- package/dist/commands/crm/sync.js.map +1 -1
- package/dist/commands/policy/index.d.ts +3 -0
- package/dist/commands/policy/index.js +217 -0
- package/dist/commands/policy/index.js.map +1 -0
- package/dist/commands/trace/index.d.ts +3 -0
- package/dist/commands/trace/index.js +455 -0
- package/dist/commands/trace/index.js.map +1 -0
- package/dist/core/auth.js +19 -1
- package/dist/core/auth.js.map +1 -1
- package/dist/core/http.js +37 -2
- package/dist/core/http.js.map +1 -1
- package/dist/core/policy.d.ts +75 -0
- package/dist/core/policy.js +124 -6
- package/dist/core/policy.js.map +1 -1
- package/dist/core/vault.js +1 -1
- package/dist/mcp/server.js +17 -1
- package/dist/mcp/server.js.map +1 -1
- package/docs/policy-templates/business-hours.json +21 -0
- package/docs/policy-templates/change-ticket-required.json +35 -0
- package/docs/policy-templates/compliance-strict.json +36 -0
- package/docs/policy-templates/no-deletes.json +17 -0
- package/docs/policy-templates/read-only.json +17 -0
- package/package.json +6 -5
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { watch, open } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { CliError, printResult } from "../../core/output.js";
|
|
6
|
+
function getHscliHome() {
|
|
7
|
+
return process.env.HSCLI_HOME?.trim() || join(homedir(), ".revfleet");
|
|
8
|
+
}
|
|
9
|
+
function getSessionFile() {
|
|
10
|
+
return join(getHscliHome(), "trace-session.json");
|
|
11
|
+
}
|
|
12
|
+
function readSession() {
|
|
13
|
+
const path = getSessionFile();
|
|
14
|
+
if (!existsSync(path))
|
|
15
|
+
return null;
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function writeSession(session) {
|
|
24
|
+
const path = getSessionFile();
|
|
25
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
26
|
+
writeFileSync(path, JSON.stringify(session, null, 2), { encoding: "utf8", mode: 0o600 });
|
|
27
|
+
}
|
|
28
|
+
function deleteSession() {
|
|
29
|
+
const path = getSessionFile();
|
|
30
|
+
if (existsSync(path)) {
|
|
31
|
+
try {
|
|
32
|
+
unlinkSync(path);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// If unlink fails (permission, race), fall back to emptying the file —
|
|
36
|
+
// readSession() will return null on empty/malformed JSON.
|
|
37
|
+
writeFileSync(path, "", { encoding: "utf8" });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function readTraceFile(path) {
|
|
42
|
+
if (!existsSync(path)) {
|
|
43
|
+
throw new CliError("TRACE_FILE_NOT_FOUND", `Trace file not found: ${path}`);
|
|
44
|
+
}
|
|
45
|
+
const raw = readFileSync(path, "utf8");
|
|
46
|
+
const events = [];
|
|
47
|
+
for (const line of raw.split("\n")) {
|
|
48
|
+
const trimmed = line.trim();
|
|
49
|
+
if (!trimmed)
|
|
50
|
+
continue;
|
|
51
|
+
try {
|
|
52
|
+
events.push(JSON.parse(trimmed));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Skip malformed lines; trace files are append-only and may be partially
|
|
56
|
+
// written if the CLI crashed mid-request.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return events;
|
|
60
|
+
}
|
|
61
|
+
function matchFilter(ev, filter) {
|
|
62
|
+
for (const [key, expected] of Object.entries(filter)) {
|
|
63
|
+
const actual = String(ev[key] ?? "");
|
|
64
|
+
// Support >= / <= / > / < / ! for numeric-ish keys
|
|
65
|
+
if (expected.startsWith(">=")) {
|
|
66
|
+
if (Number(actual) < Number(expected.slice(2)))
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
else if (expected.startsWith("<=")) {
|
|
70
|
+
if (Number(actual) > Number(expected.slice(2)))
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
else if (expected.startsWith(">")) {
|
|
74
|
+
if (Number(actual) <= Number(expected.slice(1)))
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
else if (expected.startsWith("<")) {
|
|
78
|
+
if (Number(actual) >= Number(expected.slice(1)))
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
else if (expected.startsWith("!")) {
|
|
82
|
+
if (actual === expected.slice(1))
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
else if (!actual.includes(expected)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
function parseFilterArg(raw) {
|
|
92
|
+
if (!raw)
|
|
93
|
+
return {};
|
|
94
|
+
const out = {};
|
|
95
|
+
for (const pair of raw.split(",")) {
|
|
96
|
+
const [k, ...vParts] = pair.split("=");
|
|
97
|
+
if (!k || !vParts.length)
|
|
98
|
+
continue;
|
|
99
|
+
out[k.trim()] = vParts.join("=").trim();
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
export function registerTrace(program, getCtx) {
|
|
104
|
+
const trace = program.command("trace").description("Session tracing + replay (observability of every hscli request, human + MCP)");
|
|
105
|
+
trace
|
|
106
|
+
.command("start")
|
|
107
|
+
.description("Start a trace session. Writes a state file at ~/.revfleet/trace-session.json that the http client reads on each request. Every subsequent hscli command (and MCP tool call) appends to the trace file until `hscli trace stop`.")
|
|
108
|
+
.option("--out <file>", "Path to the .jsonl trace file (default: ~/.revfleet/trace-<timestamp>.jsonl)")
|
|
109
|
+
.option("--include-bodies", "Capture request + response bodies (bigger files, redacted for secrets)")
|
|
110
|
+
.option("--scope <mode>", "Filter: read|write|all (default: all)", "all")
|
|
111
|
+
.action(async (opts) => {
|
|
112
|
+
const ctx = getCtx();
|
|
113
|
+
const existing = readSession();
|
|
114
|
+
if (existing) {
|
|
115
|
+
throw new CliError("TRACE_ALREADY_ACTIVE", `A trace session is already active (file: ${existing.file}, started: ${existing.startedAt}). Run \`hscli trace stop\` before starting a new one.`);
|
|
116
|
+
}
|
|
117
|
+
const file = opts.out
|
|
118
|
+
? String(opts.out)
|
|
119
|
+
: join(getHscliHome(), `trace-${new Date().toISOString().replace(/[:.]/g, "-")}.jsonl`);
|
|
120
|
+
const session = {
|
|
121
|
+
file,
|
|
122
|
+
startedAt: new Date().toISOString(),
|
|
123
|
+
includeBodies: Boolean(opts.includeBodies),
|
|
124
|
+
scope: opts.scope === "read" || opts.scope === "write" ? opts.scope : "all",
|
|
125
|
+
};
|
|
126
|
+
writeSession(session);
|
|
127
|
+
printResult(ctx, {
|
|
128
|
+
started: true,
|
|
129
|
+
file,
|
|
130
|
+
session,
|
|
131
|
+
note: "The trace file will grow with every hscli / MCP request until `hscli trace stop`.",
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
trace
|
|
135
|
+
.command("stop")
|
|
136
|
+
.description("Stop the active trace session. Removes the state file; the trace .jsonl is preserved.")
|
|
137
|
+
.action(async () => {
|
|
138
|
+
const ctx = getCtx();
|
|
139
|
+
const existing = readSession();
|
|
140
|
+
if (!existing) {
|
|
141
|
+
throw new CliError("TRACE_NOT_ACTIVE", "No active trace session. Use `hscli trace start` to begin one.");
|
|
142
|
+
}
|
|
143
|
+
deleteSession();
|
|
144
|
+
let fileSize = 0;
|
|
145
|
+
let eventCount = 0;
|
|
146
|
+
try {
|
|
147
|
+
if (existsSync(existing.file)) {
|
|
148
|
+
fileSize = statSync(existing.file).size;
|
|
149
|
+
eventCount = readTraceFile(existing.file).length;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch { /* stats best-effort */ }
|
|
153
|
+
printResult(ctx, {
|
|
154
|
+
stopped: true,
|
|
155
|
+
file: existing.file,
|
|
156
|
+
durationMs: Date.now() - new Date(existing.startedAt).getTime(),
|
|
157
|
+
fileSizeBytes: fileSize,
|
|
158
|
+
eventCount,
|
|
159
|
+
nextSteps: [
|
|
160
|
+
`hscli trace show ${existing.file}`,
|
|
161
|
+
`hscli trace stats ${existing.file}`,
|
|
162
|
+
`hscli trace errors ${existing.file}`,
|
|
163
|
+
],
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
trace
|
|
167
|
+
.command("status")
|
|
168
|
+
.description("Show active trace session state (if any)")
|
|
169
|
+
.action(async () => {
|
|
170
|
+
const ctx = getCtx();
|
|
171
|
+
const session = readSession();
|
|
172
|
+
if (!session) {
|
|
173
|
+
printResult(ctx, { active: false });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
let fileSize = 0;
|
|
177
|
+
let eventCount = 0;
|
|
178
|
+
try {
|
|
179
|
+
if (existsSync(session.file)) {
|
|
180
|
+
fileSize = statSync(session.file).size;
|
|
181
|
+
eventCount = readTraceFile(session.file).length;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch { /* best-effort */ }
|
|
185
|
+
printResult(ctx, {
|
|
186
|
+
active: true,
|
|
187
|
+
session,
|
|
188
|
+
fileSizeBytes: fileSize,
|
|
189
|
+
eventCount,
|
|
190
|
+
durationMs: Date.now() - new Date(session.startedAt).getTime(),
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
trace
|
|
194
|
+
.command("tail")
|
|
195
|
+
.description("Live-stream a trace file as new events are appended (like tail -f)")
|
|
196
|
+
.argument("<file>", "Path to the .jsonl trace file (or leave blank to tail the active session)")
|
|
197
|
+
.option("--format <mode>", "Output format: compact|json|pretty", "compact")
|
|
198
|
+
.action(async (fileArg, opts) => {
|
|
199
|
+
const session = readSession();
|
|
200
|
+
const file = fileArg || session?.file;
|
|
201
|
+
if (!file) {
|
|
202
|
+
throw new CliError("TRACE_TAIL_NO_FILE", "No active session and no file argument. Either `hscli trace start` or pass a file path.");
|
|
203
|
+
}
|
|
204
|
+
if (!existsSync(file)) {
|
|
205
|
+
throw new CliError("TRACE_FILE_NOT_FOUND", `Trace file not found: ${file}`);
|
|
206
|
+
}
|
|
207
|
+
const formatLine = (ev) => {
|
|
208
|
+
if (opts.format === "json")
|
|
209
|
+
return JSON.stringify(ev);
|
|
210
|
+
if (opts.format === "pretty")
|
|
211
|
+
return JSON.stringify(ev, null, 2);
|
|
212
|
+
// compact: status ts method path latency
|
|
213
|
+
const status = ev.status ? String(ev.status) : (ev.error ? "ERR" : "---");
|
|
214
|
+
const ts = ev.ts ? ev.ts.slice(11, 19) : "--:--:--";
|
|
215
|
+
const latency = ev.durationMs !== undefined ? ` ${ev.durationMs}ms` : "";
|
|
216
|
+
const tool = ev.toolName ? ` [${ev.toolName}]` : "";
|
|
217
|
+
return `${status.padStart(3)} ${ts} ${(ev.method || "???").padEnd(6)} ${ev.path || ""}${latency}${tool}`;
|
|
218
|
+
};
|
|
219
|
+
// Print existing content first
|
|
220
|
+
const initial = readTraceFile(file);
|
|
221
|
+
for (const ev of initial)
|
|
222
|
+
process.stdout.write(formatLine(ev) + "\n");
|
|
223
|
+
process.stderr.write(`--- tailing ${file} (Ctrl-C to stop) ---\n`);
|
|
224
|
+
// Watch file for growth
|
|
225
|
+
let lastSize = statSync(file).size;
|
|
226
|
+
const fd = await open(file, "r");
|
|
227
|
+
try {
|
|
228
|
+
const watcher = watch(file);
|
|
229
|
+
for await (const event of watcher) {
|
|
230
|
+
if (event.eventType !== "change")
|
|
231
|
+
continue;
|
|
232
|
+
const newSize = statSync(file).size;
|
|
233
|
+
if (newSize <= lastSize)
|
|
234
|
+
continue;
|
|
235
|
+
const buf = Buffer.alloc(newSize - lastSize);
|
|
236
|
+
await fd.read(buf, 0, buf.length, lastSize);
|
|
237
|
+
lastSize = newSize;
|
|
238
|
+
const chunk = buf.toString("utf8");
|
|
239
|
+
for (const line of chunk.split("\n")) {
|
|
240
|
+
const trimmed = line.trim();
|
|
241
|
+
if (!trimmed)
|
|
242
|
+
continue;
|
|
243
|
+
try {
|
|
244
|
+
const ev = JSON.parse(trimmed);
|
|
245
|
+
process.stdout.write(formatLine(ev) + "\n");
|
|
246
|
+
}
|
|
247
|
+
catch { /* skip malformed */ }
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
await fd.close();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
trace
|
|
256
|
+
.command("show")
|
|
257
|
+
.description("Pretty-print a trace JSONL file with optional filters")
|
|
258
|
+
.argument("<file>", "Path to the .jsonl trace file")
|
|
259
|
+
.option("--filter <pairs>", "Comma-separated key=value filters. Supports >=, <=, >, <, ! operators. Example: --filter status=>=400,method=POST")
|
|
260
|
+
.option("--limit <n>", "Max events to show (default 50)", "50")
|
|
261
|
+
.option("--tail", "Show last N events instead of first N")
|
|
262
|
+
.action(async (file, opts) => {
|
|
263
|
+
const ctx = getCtx();
|
|
264
|
+
const events = readTraceFile(file);
|
|
265
|
+
const filter = parseFilterArg(opts.filter);
|
|
266
|
+
const matching = events.filter(ev => matchFilter(ev, filter));
|
|
267
|
+
const limit = Number(opts.limit) || 50;
|
|
268
|
+
const slice = opts.tail ? matching.slice(-limit) : matching.slice(0, limit);
|
|
269
|
+
const summary = {
|
|
270
|
+
file,
|
|
271
|
+
totalEvents: events.length,
|
|
272
|
+
matchingEvents: matching.length,
|
|
273
|
+
showing: slice.length,
|
|
274
|
+
filter: opts.filter || "(none)",
|
|
275
|
+
statusBreakdown: countBy(matching, e => String(e.status ?? "unknown")),
|
|
276
|
+
methodBreakdown: countBy(matching, e => String(e.method ?? "unknown")),
|
|
277
|
+
p50LatencyMs: percentile(matching.map(e => e.durationMs ?? 0), 50),
|
|
278
|
+
p95LatencyMs: percentile(matching.map(e => e.durationMs ?? 0), 95),
|
|
279
|
+
errorCount: matching.filter(e => e.error || (e.status !== undefined && e.status >= 400)).length,
|
|
280
|
+
};
|
|
281
|
+
printResult(ctx, { summary, events: slice });
|
|
282
|
+
});
|
|
283
|
+
trace
|
|
284
|
+
.command("stats")
|
|
285
|
+
.description("Summary stats of a trace file (no per-event output)")
|
|
286
|
+
.argument("<file>", "Path to the .jsonl trace file")
|
|
287
|
+
.action(async (file) => {
|
|
288
|
+
const ctx = getCtx();
|
|
289
|
+
const events = readTraceFile(file);
|
|
290
|
+
const stats = {
|
|
291
|
+
totalEvents: events.length,
|
|
292
|
+
byStatus: countBy(events, e => String(e.status ?? "unknown")),
|
|
293
|
+
byMethod: countBy(events, e => String(e.method ?? "unknown")),
|
|
294
|
+
byProfile: countBy(events, e => String(e.profile ?? "unknown")),
|
|
295
|
+
byToolName: countBy(events.filter(e => e.toolName), e => String(e.toolName)),
|
|
296
|
+
latency: {
|
|
297
|
+
p50Ms: percentile(events.map(e => e.durationMs ?? 0), 50),
|
|
298
|
+
p95Ms: percentile(events.map(e => e.durationMs ?? 0), 95),
|
|
299
|
+
p99Ms: percentile(events.map(e => e.durationMs ?? 0), 99),
|
|
300
|
+
maxMs: Math.max(0, ...events.map(e => e.durationMs ?? 0)),
|
|
301
|
+
},
|
|
302
|
+
errors: events.filter(e => e.error || (e.status !== undefined && e.status >= 400)).length,
|
|
303
|
+
writeOps: events.filter(e => ["POST", "PUT", "PATCH", "DELETE"].includes(e.method ?? "")).length,
|
|
304
|
+
readOps: events.filter(e => e.method === "GET").length,
|
|
305
|
+
timeSpan: events.length
|
|
306
|
+
? { from: events[0].ts, to: events[events.length - 1].ts }
|
|
307
|
+
: null,
|
|
308
|
+
};
|
|
309
|
+
printResult(ctx, stats);
|
|
310
|
+
});
|
|
311
|
+
trace
|
|
312
|
+
.command("errors")
|
|
313
|
+
.description("Show only error events (status >= 400 or explicit error field)")
|
|
314
|
+
.argument("<file>", "Path to the .jsonl trace file")
|
|
315
|
+
.option("--limit <n>", "Max errors to show", "50")
|
|
316
|
+
.action(async (file, opts) => {
|
|
317
|
+
const ctx = getCtx();
|
|
318
|
+
const events = readTraceFile(file);
|
|
319
|
+
const errors = events.filter(e => e.error || (e.status !== undefined && e.status >= 400));
|
|
320
|
+
const limit = Number(opts.limit) || 50;
|
|
321
|
+
printResult(ctx, {
|
|
322
|
+
totalErrors: errors.length,
|
|
323
|
+
showing: Math.min(errors.length, limit),
|
|
324
|
+
errors: errors.slice(0, limit),
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
trace
|
|
328
|
+
.command("diff")
|
|
329
|
+
.description("Diff two trace JSONL sessions by method+path key. Helps verify that a script/agent made the same HubSpot calls across two runs (reproducibility, migration from stage→prod, regression detection).")
|
|
330
|
+
.argument("<file-a>", "First trace file")
|
|
331
|
+
.argument("<file-b>", "Second trace file")
|
|
332
|
+
.action(async (fileA, fileB) => {
|
|
333
|
+
const ctx = getCtx();
|
|
334
|
+
const a = readTraceFile(fileA);
|
|
335
|
+
const b = readTraceFile(fileB);
|
|
336
|
+
// Key by method + path (ignore query params + ids) so diffs show
|
|
337
|
+
// structural divergence, not trivial id differences.
|
|
338
|
+
const keyOf = (ev) => `${ev.method || "?"} ${(ev.path || "").split("?")[0].replace(/\/\d+/g, "/{id}")}`;
|
|
339
|
+
const bucketize = (events) => {
|
|
340
|
+
const buckets = new Map();
|
|
341
|
+
for (const ev of events) {
|
|
342
|
+
const k = keyOf(ev);
|
|
343
|
+
if (!buckets.has(k))
|
|
344
|
+
buckets.set(k, []);
|
|
345
|
+
buckets.get(k).push(ev);
|
|
346
|
+
}
|
|
347
|
+
return buckets;
|
|
348
|
+
};
|
|
349
|
+
const aBuckets = bucketize(a);
|
|
350
|
+
const bBuckets = bucketize(b);
|
|
351
|
+
const allKeys = new Set([...aBuckets.keys(), ...bBuckets.keys()]);
|
|
352
|
+
const onlyInA = [];
|
|
353
|
+
const onlyInB = [];
|
|
354
|
+
const countChanges = [];
|
|
355
|
+
const statusChanges = [];
|
|
356
|
+
for (const k of allKeys) {
|
|
357
|
+
const aRecs = aBuckets.get(k) ?? [];
|
|
358
|
+
const bRecs = bBuckets.get(k) ?? [];
|
|
359
|
+
if (aRecs.length === 0) {
|
|
360
|
+
onlyInB.push(k);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (bRecs.length === 0) {
|
|
364
|
+
onlyInA.push(k);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (aRecs.length !== bRecs.length) {
|
|
368
|
+
countChanges.push({ key: k, countA: aRecs.length, countB: bRecs.length, delta: bRecs.length - aRecs.length });
|
|
369
|
+
}
|
|
370
|
+
const sa = [...new Set(aRecs.map(r => String(r.status ?? "err")))].sort();
|
|
371
|
+
const sb = [...new Set(bRecs.map(r => String(r.status ?? "err")))].sort();
|
|
372
|
+
if (sa.join(",") !== sb.join(",")) {
|
|
373
|
+
statusChanges.push({ key: k, statusesA: sa, statusesB: sb });
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const summary = {
|
|
377
|
+
fileA,
|
|
378
|
+
fileB,
|
|
379
|
+
totalEventsA: a.length,
|
|
380
|
+
totalEventsB: b.length,
|
|
381
|
+
uniqueKeysA: aBuckets.size,
|
|
382
|
+
uniqueKeysB: bBuckets.size,
|
|
383
|
+
onlyInA: onlyInA.sort(),
|
|
384
|
+
onlyInB: onlyInB.sort(),
|
|
385
|
+
countChanges: countChanges.sort((x, y) => Math.abs(y.delta) - Math.abs(x.delta)),
|
|
386
|
+
statusChanges,
|
|
387
|
+
divergent: onlyInA.length > 0 || onlyInB.length > 0 || statusChanges.length > 0,
|
|
388
|
+
};
|
|
389
|
+
printResult(ctx, summary);
|
|
390
|
+
});
|
|
391
|
+
trace
|
|
392
|
+
.command("replay")
|
|
393
|
+
.description("Re-run the GET requests from a trace session against the current profile. Useful for: reproducing a bug, verifying a portal migration, warming caches. Write operations are NEVER replayed (too risky).")
|
|
394
|
+
.argument("<file>", "Path to the .jsonl trace file")
|
|
395
|
+
.option("--profile <name>", "Override the profile (default: inherit from CLI)")
|
|
396
|
+
.option("--limit <n>", "Max requests to replay", "100")
|
|
397
|
+
.action(async (file, opts) => {
|
|
398
|
+
const ctx = getCtx();
|
|
399
|
+
const events = readTraceFile(file);
|
|
400
|
+
// Only replay GETs — writes (POST/PUT/PATCH/DELETE) are intentionally
|
|
401
|
+
// not supported to avoid accidental re-mutation. If a user wants to
|
|
402
|
+
// replay writes, they must do so via their own script with --force.
|
|
403
|
+
const replayable = events.filter(e => e.method === "GET" && e.path);
|
|
404
|
+
const limit = Number(opts.limit) || 100;
|
|
405
|
+
const slice = replayable.slice(0, limit);
|
|
406
|
+
// Dry-run is the SAFE default (respects CLI global --dry-run).
|
|
407
|
+
// Users must explicitly pass --force to re-issue the GETs live.
|
|
408
|
+
if (ctx.dryRun || !ctx.force) {
|
|
409
|
+
printResult(ctx, {
|
|
410
|
+
dryRun: true,
|
|
411
|
+
wouldReplay: slice.length,
|
|
412
|
+
skippedWrites: events.filter(e => e.method && ["POST", "PUT", "PATCH", "DELETE"].includes(e.method)).length,
|
|
413
|
+
firstTen: slice.slice(0, 10).map(e => `${e.method} ${e.path}`),
|
|
414
|
+
});
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const { createClient } = await import("../../core/http.js");
|
|
418
|
+
const client = createClient(opts.profile ?? ctx.profile);
|
|
419
|
+
const results = [];
|
|
420
|
+
for (const ev of slice) {
|
|
421
|
+
const started = Date.now();
|
|
422
|
+
try {
|
|
423
|
+
await client.request(ev.path);
|
|
424
|
+
results.push({ path: ev.path, status: "ok", durationMs: Date.now() - started });
|
|
425
|
+
}
|
|
426
|
+
catch (err) {
|
|
427
|
+
const statusMatch = (err instanceof Error ? err.message : String(err)).match(/\((\d+)\)/);
|
|
428
|
+
results.push({ path: ev.path, status: statusMatch ? statusMatch[1] : "err", durationMs: Date.now() - started });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
printResult(ctx, {
|
|
432
|
+
replayed: results.length,
|
|
433
|
+
ok: results.filter(r => r.status === "ok").length,
|
|
434
|
+
errors: results.filter(r => r.status !== "ok").length,
|
|
435
|
+
skippedWrites: events.filter(e => e.method && ["POST", "PUT", "PATCH", "DELETE"].includes(e.method)).length,
|
|
436
|
+
results,
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
function countBy(xs, keyFn) {
|
|
441
|
+
const out = {};
|
|
442
|
+
for (const x of xs) {
|
|
443
|
+
const k = keyFn(x);
|
|
444
|
+
out[k] = (out[k] ?? 0) + 1;
|
|
445
|
+
}
|
|
446
|
+
return out;
|
|
447
|
+
}
|
|
448
|
+
function percentile(sortedOrUnsorted, p) {
|
|
449
|
+
if (sortedOrUnsorted.length === 0)
|
|
450
|
+
return 0;
|
|
451
|
+
const sorted = [...sortedOrUnsorted].sort((a, b) => a - b);
|
|
452
|
+
const idx = Math.min(sorted.length - 1, Math.max(0, Math.floor((p / 100) * sorted.length)));
|
|
453
|
+
return Math.round(sorted[idx]);
|
|
454
|
+
}
|
|
455
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/trace/index.ts"],"names":[],"mappings":"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"}
|
package/dist/core/auth.js
CHANGED
|
@@ -49,7 +49,25 @@ export function getApiBaseUrl(profile) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
export function getHubcliHomeDir() {
|
|
52
|
-
|
|
52
|
+
// New primary location: ~/.revfleet/ (avoids colliding with @hubspot/cli's
|
|
53
|
+
// own ~/.hscli/ config dir). If a legacy ~/.hubcli/ install exists and
|
|
54
|
+
// the new location is empty, fall back to the legacy so users who
|
|
55
|
+
// upgrade from pre-0.5.4 hubcli/hscli don't silently lose their auth.
|
|
56
|
+
// We don't auto-migrate (write to new location) on read — that'd be a
|
|
57
|
+
// destructive side-effect from a read call. `hscli auth login` will
|
|
58
|
+
// write to the new location going forward.
|
|
59
|
+
const explicit = process.env.HSCLI_HOME?.trim();
|
|
60
|
+
if (explicit)
|
|
61
|
+
return explicit;
|
|
62
|
+
const primary = join(homedir(), ".revfleet");
|
|
63
|
+
// If the new primary has an auth.json, always use it.
|
|
64
|
+
if (existsSync(join(primary, "auth.json")))
|
|
65
|
+
return primary;
|
|
66
|
+
// Fallback to legacy ~/.hubcli if it still has the only auth.json.
|
|
67
|
+
const legacy = join(homedir(), ".hubcli");
|
|
68
|
+
if (existsSync(join(legacy, "auth.json")))
|
|
69
|
+
return legacy;
|
|
70
|
+
return primary;
|
|
53
71
|
}
|
|
54
72
|
function authPaths() {
|
|
55
73
|
const root = getHubcliHomeDir();
|
package/dist/core/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAcjG;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,OAA6B;IACxD,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC;IAE1C,4DAA4D;IAC5D,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAC3D,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IAED,wDAAwD;IACxD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAC5D,IAAI,UAAU,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,KAAK;YAAE,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAe;IAC9C,IAAI,CAAC,MAAM;QAAE,OAAO,gBAAgB,CAAC;IACrC,OAAO,OAAO,MAAM,aAAa,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,WAAW,IAAI,CAAC,SAAS,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAClC,OAAO,WAAW,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,wBAAwB,CAAC;IAClC,CAAC;AACH,CAAC;AAMD,MAAM,UAAU,gBAAgB;IAC9B,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAcjG;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,OAA6B;IACxD,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC;IAE1C,4DAA4D;IAC5D,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAC3D,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IAED,wDAAwD;IACxD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAC5D,IAAI,UAAU,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,KAAK;YAAE,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAe;IAC9C,IAAI,CAAC,MAAM;QAAE,OAAO,gBAAgB,CAAC;IACrC,OAAO,OAAO,MAAM,aAAa,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,WAAW,IAAI,CAAC,SAAS,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAClC,OAAO,WAAW,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,wBAAwB,CAAC;IAClC,CAAC;AACH,CAAC;AAMD,MAAM,UAAU,gBAAgB;IAC9B,2EAA2E;IAC3E,uEAAuE;IACvE,kEAAkE;IAClE,sEAAsE;IACtE,sEAAsE;IACtE,oEAAoE;IACpE,2CAA2C;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;IAChD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;IAC7C,sDAAsD;IACtD,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3D,mEAAmE;IACnE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC1C,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACzD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAC;IAChC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;AACtD,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,EAAE,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC;IAC5B,oFAAoF;IACpF,IAAI,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,UAAU,GAAG,kBAAkB,EAAE,CAAC;QACxC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,QAAQ,CAChB,2BAA2B,EAC3B,iIAAiI,CAClI,CAAC;QACJ,CAAC;QACD,OAAO,aAAa,CAAC,GAAG,EAAE,UAAU,CAAa,CAAC;IACpD,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,EAAE,CAAC;IAC7B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAa,CAAC;AAC5D,CAAC;AAED,SAAS,aAAa,CAAC,IAAc;IACnC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,SAAS,EAAE,CAAC;IAClC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;IACD,wFAAwF;IACxF,IAAI,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,UAAU,GAAG,kBAAkB,EAAE,CAAC;QACxC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,QAAQ,CAChB,2BAA2B,EAC3B,iIAAiI,CAClI,CAAC;QACJ,CAAC;QACD,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IACD,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,KAAa;IACtD,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE;QAAE,MAAM,IAAI,QAAQ,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,CAAC;IACtF,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;IACtE,aAAa,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAiB;IAC5D,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE;QAAE,MAAM,IAAI,QAAQ,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,CAAC;IAC3F,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;IAC1F,aAAa,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,QAAQ,CAAC,wBAAwB,EAAE,+BAA+B,OAAO,6CAA6C,CAAC,CAAC;IACpI,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9B,aAAa,CAAC,IAAI,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,OAAO,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,QAAQ,CAAC,wBAAwB,EAAE,yBAAyB,OAAO,IAAI,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/core/http.js
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import { CliError } from "./output.js";
|
|
2
|
-
import { appendFileSync } from "node:fs";
|
|
2
|
+
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
3
5
|
import { randomUUID } from "node:crypto";
|
|
6
|
+
/**
|
|
7
|
+
* Read the active trace session file (written by `hscli trace start`).
|
|
8
|
+
* Returns the trace output path if a session is active, or undefined.
|
|
9
|
+
* Keeps session awareness decoupled from the CLI command layer: the
|
|
10
|
+
* http client reads the state file on construction, so ANY code path
|
|
11
|
+
* that opens a HubSpotClient automatically participates in the trace.
|
|
12
|
+
*/
|
|
13
|
+
function readActiveTraceSessionFile() {
|
|
14
|
+
const home = process.env.HSCLI_HOME?.trim() || join(homedir(), ".revfleet");
|
|
15
|
+
const sessionPath = join(home, "trace-session.json");
|
|
16
|
+
if (!existsSync(sessionPath))
|
|
17
|
+
return undefined;
|
|
18
|
+
try {
|
|
19
|
+
const session = JSON.parse(readFileSync(sessionPath, "utf8"));
|
|
20
|
+
return session.file;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
4
26
|
import { mapEndpointAvailabilityError, preflightEndpointCapability, recordEndpointSuccess } from "./capabilities.js";
|
|
5
27
|
import { getToken, getApiBaseUrl } from "./auth.js";
|
|
6
28
|
import { enforcePermissionProfile } from "./permissions.js";
|
|
@@ -84,7 +106,15 @@ export class HubSpotClient {
|
|
|
84
106
|
this.token = token;
|
|
85
107
|
this.baseUrl = new URL(options.apiBaseUrl?.trim() || "https://api.hubapi.com");
|
|
86
108
|
this.requestId = options.requestId?.trim() || process.env.HSCLI_REQUEST_ID?.trim() || randomUUID();
|
|
87
|
-
|
|
109
|
+
// Telemetry priority:
|
|
110
|
+
// 1. explicit --telemetry-file flag / options.telemetryFile
|
|
111
|
+
// 2. HSCLI_TELEMETRY_FILE env var
|
|
112
|
+
// 3. active trace session file (set by `hscli trace start`)
|
|
113
|
+
// Users never need to re-pass --telemetry-file after `trace start`.
|
|
114
|
+
this.telemetryFile = options.telemetryFile?.trim()
|
|
115
|
+
|| process.env.HSCLI_TELEMETRY_FILE?.trim()
|
|
116
|
+
|| readActiveTraceSessionFile()
|
|
117
|
+
|| undefined;
|
|
88
118
|
this.profile = options.profile?.trim() || process.env.HSCLI_PROFILE?.trim() || "default";
|
|
89
119
|
this.strictCapabilities = options.strictCapabilities ?? isEnvTrue(process.env.HSCLI_STRICT_CAPABILITIES);
|
|
90
120
|
}
|
|
@@ -182,9 +212,14 @@ export class HubSpotClient {
|
|
|
182
212
|
if (!this.telemetryFile)
|
|
183
213
|
return;
|
|
184
214
|
try {
|
|
215
|
+
// Baseline event: always includes ts + requestId + profile. toolName
|
|
216
|
+
// is set from HUBCLI_MCP_TOOL_NAME so MCP tool invocations show up
|
|
217
|
+
// distinctly in `hubcli trace stats` (byToolName breakdown).
|
|
185
218
|
appendFileSync(this.telemetryFile, JSON.stringify({
|
|
186
219
|
ts: new Date().toISOString(),
|
|
187
220
|
requestId: this.requestId,
|
|
221
|
+
profile: this.profile,
|
|
222
|
+
toolName: process.env.HUBCLI_MCP_TOOL_NAME?.trim() || undefined,
|
|
188
223
|
...event,
|
|
189
224
|
}) + "\n", "utf8");
|
|
190
225
|
}
|