@framedash/cli 0.1.1 → 0.1.3

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 (101) hide show
  1. package/README.md +62 -5
  2. package/dist/commands/alerts.d.ts.map +1 -1
  3. package/dist/commands/alerts.js +2 -1
  4. package/dist/commands/alerts.js.map +1 -1
  5. package/dist/commands/auth.d.ts.map +1 -1
  6. package/dist/commands/auth.js +25 -2
  7. package/dist/commands/auth.js.map +1 -1
  8. package/dist/commands/builds.d.ts +2 -0
  9. package/dist/commands/builds.d.ts.map +1 -0
  10. package/dist/commands/builds.js +31 -0
  11. package/dist/commands/builds.js.map +1 -0
  12. package/dist/commands/content.d.ts.map +1 -1
  13. package/dist/commands/content.js +2 -2
  14. package/dist/commands/content.js.map +1 -1
  15. package/dist/commands/dashboard.d.ts.map +1 -1
  16. package/dist/commands/dashboard.js +3 -3
  17. package/dist/commands/dashboard.js.map +1 -1
  18. package/dist/commands/funnel.d.ts.map +1 -1
  19. package/dist/commands/funnel.js +7 -8
  20. package/dist/commands/funnel.js.map +1 -1
  21. package/dist/commands/login.d.ts +2 -0
  22. package/dist/commands/login.d.ts.map +1 -0
  23. package/dist/commands/login.js +142 -0
  24. package/dist/commands/login.js.map +1 -0
  25. package/dist/commands/logout.d.ts +2 -0
  26. package/dist/commands/logout.d.ts.map +1 -0
  27. package/dist/commands/logout.js +79 -0
  28. package/dist/commands/logout.js.map +1 -0
  29. package/dist/commands/map-capture.d.ts +2 -1
  30. package/dist/commands/map-capture.d.ts.map +1 -1
  31. package/dist/commands/map-capture.js +42 -12
  32. package/dist/commands/map-capture.js.map +1 -1
  33. package/dist/commands/perf-diff.d.ts +2 -0
  34. package/dist/commands/perf-diff.d.ts.map +1 -0
  35. package/dist/commands/perf-diff.js +125 -0
  36. package/dist/commands/perf-diff.js.map +1 -0
  37. package/dist/commands/query.d.ts.map +1 -1
  38. package/dist/commands/query.js +12 -0
  39. package/dist/commands/query.js.map +1 -1
  40. package/dist/commands/retention.d.ts.map +1 -1
  41. package/dist/commands/retention.js +3 -3
  42. package/dist/commands/retention.js.map +1 -1
  43. package/dist/commands/run-profile-test.d.ts +7 -0
  44. package/dist/commands/run-profile-test.d.ts.map +1 -0
  45. package/dist/commands/run-profile-test.js +503 -0
  46. package/dist/commands/run-profile-test.js.map +1 -0
  47. package/dist/commands/threshold-profiles.d.ts +2 -0
  48. package/dist/commands/threshold-profiles.d.ts.map +1 -0
  49. package/dist/commands/threshold-profiles.js +24 -0
  50. package/dist/commands/threshold-profiles.js.map +1 -0
  51. package/dist/index.js +16 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/lib/config.d.ts +42 -1
  54. package/dist/lib/config.d.ts.map +1 -1
  55. package/dist/lib/config.js +44 -14
  56. package/dist/lib/config.js.map +1 -1
  57. package/dist/lib/create-client.d.ts +27 -1
  58. package/dist/lib/create-client.d.ts.map +1 -1
  59. package/dist/lib/create-client.js +167 -24
  60. package/dist/lib/create-client.js.map +1 -1
  61. package/dist/lib/formatters.js +93 -1
  62. package/dist/lib/formatters.js.map +1 -1
  63. package/dist/lib/logger.d.ts +2 -0
  64. package/dist/lib/logger.d.ts.map +1 -1
  65. package/dist/lib/logger.js +4 -0
  66. package/dist/lib/logger.js.map +1 -1
  67. package/dist/lib/oauth/loopback-server.d.ts +27 -0
  68. package/dist/lib/oauth/loopback-server.d.ts.map +1 -0
  69. package/dist/lib/oauth/loopback-server.js +164 -0
  70. package/dist/lib/oauth/loopback-server.js.map +1 -0
  71. package/dist/lib/oauth/manager.d.ts +36 -0
  72. package/dist/lib/oauth/manager.d.ts.map +1 -0
  73. package/dist/lib/oauth/manager.js +130 -0
  74. package/dist/lib/oauth/manager.js.map +1 -0
  75. package/dist/lib/oauth/pkce.d.ts +15 -0
  76. package/dist/lib/oauth/pkce.d.ts.map +1 -0
  77. package/dist/lib/oauth/pkce.js +24 -0
  78. package/dist/lib/oauth/pkce.js.map +1 -0
  79. package/dist/lib/oauth/token-endpoint.d.ts +39 -0
  80. package/dist/lib/oauth/token-endpoint.d.ts.map +1 -0
  81. package/dist/lib/oauth/token-endpoint.js +117 -0
  82. package/dist/lib/oauth/token-endpoint.js.map +1 -0
  83. package/dist/lib/oauth/token-store.d.ts +34 -0
  84. package/dist/lib/oauth/token-store.d.ts.map +1 -0
  85. package/dist/lib/oauth/token-store.js +164 -0
  86. package/dist/lib/oauth/token-store.js.map +1 -0
  87. package/dist/lib/perf-diff-eval.d.ts +58 -0
  88. package/dist/lib/perf-diff-eval.d.ts.map +1 -0
  89. package/dist/lib/perf-diff-eval.js +49 -0
  90. package/dist/lib/perf-diff-eval.js.map +1 -0
  91. package/dist/lib/run-command.js +2 -2
  92. package/dist/lib/run-command.js.map +1 -1
  93. package/dist/lib/run-profile-test-lib.d.ts +156 -0
  94. package/dist/lib/run-profile-test-lib.d.ts.map +1 -0
  95. package/dist/lib/run-profile-test-lib.js +171 -0
  96. package/dist/lib/run-profile-test-lib.js.map +1 -0
  97. package/dist/lib/uploader.d.ts +14 -1
  98. package/dist/lib/uploader.d.ts.map +1 -1
  99. package/dist/lib/uploader.js +26 -12
  100. package/dist/lib/uploader.js.map +1 -1
  101. package/package.json +3 -3
@@ -0,0 +1,503 @@
1
+ import { execFileSync, spawn, spawnSync } from "node:child_process";
2
+ import { constants as osConstants } from "node:os";
3
+ import { buildBuildComparePath, buildBuildsPath } from "@framedash/api-client";
4
+ import { createClient } from "../lib/create-client.js";
5
+ import { formatOutput } from "../lib/formatters.js";
6
+ import { error, log, success, warn } from "../lib/logger.js";
7
+ import { evaluateRegression, formatMetricDiff, isRegressionMetric, } from "../lib/perf-diff-eval.js";
8
+ import { runCommand } from "../lib/run-command.js";
9
+ import { buildEventCount, buildSessionEnv, planTreeKill, resolveProfileIdentity, SESSION_ENV_KEYS, validateSeconds, waitForIngest, } from "../lib/run-profile-test-lib.js";
10
+ const DEFAULT_INGEST_TIMEOUT_S = 180;
11
+ const DEFAULT_POLL_INTERVAL_S = 5;
12
+ // Generous finite default so a game that never self-quits cannot hang a CI job
13
+ // forever (the F53 failure mode); 0 disables the bound for interactive/local use.
14
+ const DEFAULT_COMMAND_TIMEOUT_S = 1800;
15
+ // Cap so command-timeout * 1000 stays within setTimeout's signed 32-bit delay
16
+ // (2^31-1 ms); a larger value would be clamped to 1ms and fire almost at once.
17
+ const MAX_COMMAND_TIMEOUT_S = Math.floor(2 ** 31 / 1000);
18
+ /** Best-effort `git` lookup; returns undefined when git is absent or fails. */
19
+ function gitOutput(args) {
20
+ try {
21
+ const out = execFileSync("git", args, {
22
+ encoding: "utf8",
23
+ stdio: ["ignore", "pipe", "ignore"],
24
+ }).trim();
25
+ return out.length > 0 ? out : undefined;
26
+ }
27
+ catch {
28
+ return undefined;
29
+ }
30
+ }
31
+ /** Parse a seconds-valued flag with a descriptive error, or fall back. */
32
+ function parseSeconds(value, flag, fallback, opts = {}) {
33
+ const result = validateSeconds(value, fallback, opts);
34
+ if ("error" in result) {
35
+ error(`--${flag} ${result.error}`);
36
+ process.exit(1);
37
+ }
38
+ return result.value;
39
+ }
40
+ /**
41
+ * Forward the project id and base URL so a CI game build that reads them from the
42
+ * environment targets the same project/endpoint the gate queries (they must match
43
+ * or the gate would read a different project's builds).
44
+ *
45
+ * Deliberately does NOT forward the API key: the gate's key needs analytics:read,
46
+ * while the game's SDK needs a separate events:write ingest key (the presets keep
47
+ * ingest a separate plane). Overwriting the child's FRAMEDASH_API_KEY would make
48
+ * the game send events with a read-only key and 403 at ingest, so the game keeps
49
+ * whatever ingest key the CI environment already set for it.
50
+ */
51
+ function passthroughProjectEnv(config) {
52
+ return {
53
+ FRAMEDASH_PROJECT_ID: config.projectId,
54
+ FRAMEDASH_BASE_URL: config.baseUrl,
55
+ };
56
+ }
57
+ /** Validate and resolve the regression-gate flags, exiting on a misconfig. */
58
+ function resolveGateOptions(values, candidateBuildId) {
59
+ let metric;
60
+ if (values.metric !== undefined) {
61
+ const raw = values.metric.trim();
62
+ if (!isRegressionMetric(raw)) {
63
+ error(`Invalid --metric '${raw}'. Allowed: frame_time, memory, gpu_time`);
64
+ process.exit(1);
65
+ }
66
+ metric = raw;
67
+ }
68
+ let thresholdPct = 0;
69
+ if (values.threshold !== undefined) {
70
+ thresholdPct = Number(values.threshold);
71
+ if (!Number.isFinite(thresholdPct) || thresholdPct < 0) {
72
+ error("--threshold must be a non-negative number (percent)");
73
+ process.exit(1);
74
+ }
75
+ }
76
+ const baseline = values.baseline?.trim();
77
+ // A build compared against itself is a zero diff that would silently pass.
78
+ if (baseline && baseline === candidateBuildId) {
79
+ error("--baseline must differ from the candidate build id (the build under test).");
80
+ process.exit(1);
81
+ }
82
+ if (values["fail-on-regression"] && !baseline) {
83
+ error("--fail-on-regression requires --baseline (the known-good build to compare against).");
84
+ process.exit(1);
85
+ }
86
+ return { metric, thresholdPct, baseline };
87
+ }
88
+ /**
89
+ * Terminate the launched process TREE by pid. A plain child kill leaves
90
+ * grandchildren behind (a game launched via the shell spawns helper processes),
91
+ * so use the platform-specific whole-tree strategy from planTreeKill: taskkill
92
+ * /T /F on win32, or a SIGKILL to the process group on POSIX (falling back to the
93
+ * direct pid if the group is already gone). planTreeKill returns a noop plan for a
94
+ * missing/non-positive pid (which would otherwise signal our own group or every
95
+ * process on POSIX). Best-effort: never throws.
96
+ */
97
+ function killProcessTree(pid) {
98
+ const plan = planTreeKill(process.platform, pid);
99
+ if (plan.platform === "noop")
100
+ return;
101
+ if (plan.platform === "win32") {
102
+ spawnSync(plan.command, plan.args, { stdio: "ignore" });
103
+ return;
104
+ }
105
+ try {
106
+ process.kill(plan.groupPid, "SIGKILL");
107
+ }
108
+ catch {
109
+ try {
110
+ // -groupPid is the validated positive child pid; fall back to it directly.
111
+ process.kill(-plan.groupPid, "SIGKILL");
112
+ }
113
+ catch {
114
+ // The process (group) is already gone; nothing left to kill.
115
+ }
116
+ }
117
+ }
118
+ /**
119
+ * Spawn the profiling command (inheriting stdio) and resolve when it exits.
120
+ * Bounds the run with an overall timeout: on win32 the child is the shell, whose
121
+ * whole tree taskkill terminates; on POSIX the child is spawned detached so we
122
+ * can signal its process group. timeoutMs <= 0 disables the bound. Rejects only
123
+ * on a spawn error (ENOENT etc.).
124
+ */
125
+ function runProfilingCommand(command, env, timeoutMs) {
126
+ return new Promise((resolve, reject) => {
127
+ const child = spawn(command, {
128
+ shell: true,
129
+ stdio: "inherit",
130
+ env,
131
+ // POSIX: run in a new process group so killProcessTree can signal the whole
132
+ // group. win32 has no process groups here; taskkill /T handles the tree.
133
+ detached: process.platform !== "win32",
134
+ });
135
+ let timedOut = false;
136
+ let timer;
137
+ // A detached POSIX child does NOT share the terminal's signals, so a Ctrl-C or
138
+ // CI cancellation that terminates THIS CLI would orphan the game's process
139
+ // group. Kill the tree on those signals first, then exit with the conventional
140
+ // 128 + signal-number code so the exit status still reflects the signal.
141
+ const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"];
142
+ const cleanup = () => {
143
+ if (timer)
144
+ clearTimeout(timer);
145
+ for (const s of forwardedSignals)
146
+ process.off(s, onSignal);
147
+ };
148
+ const onSignal = (signal) => {
149
+ cleanup();
150
+ killProcessTree(child.pid);
151
+ process.exit(128 + (osConstants.signals[signal] ?? 0));
152
+ };
153
+ for (const s of forwardedSignals)
154
+ process.on(s, onSignal);
155
+ if (timeoutMs > 0) {
156
+ timer = setTimeout(() => {
157
+ timedOut = true;
158
+ killProcessTree(child.pid);
159
+ }, timeoutMs);
160
+ }
161
+ child.on("error", (err) => {
162
+ cleanup();
163
+ reject(err);
164
+ });
165
+ child.on("exit", (code, signal) => {
166
+ cleanup();
167
+ resolve({ status: code, signal, timedOut });
168
+ });
169
+ });
170
+ }
171
+ /** Launch the profiling command and exit on any failure (or timeout). */
172
+ async function launchProfilingRun(command, env, timeoutMs) {
173
+ let result;
174
+ try {
175
+ result = await runProfilingCommand(command, env, timeoutMs);
176
+ }
177
+ catch (err) {
178
+ error(`Failed to launch --command: ${err instanceof Error ? err.message : String(err)}`);
179
+ process.exit(1);
180
+ }
181
+ // A timeout is incomplete data: fail closed WITHOUT gating, same philosophy as a
182
+ // non-zero exit. Checked first because the kill also sets a signal on the child.
183
+ if (result.timedOut) {
184
+ error(`Profiling command exceeded the --command-timeout of ${timeoutMs / 1000}s and was killed; ` +
185
+ "not gating on incomplete data. If the game does not exit on its own, make your " +
186
+ "automation quit when the run finishes, or raise --command-timeout (0 disables the bound).");
187
+ process.exit(1);
188
+ }
189
+ if (result.signal) {
190
+ error(`Profiling command was terminated by signal ${result.signal}.`);
191
+ // Conventional 128 + signal number so CI sees a meaningful non-zero code.
192
+ process.exit(128 + (osConstants.signals[result.signal] ?? 0));
193
+ }
194
+ if (typeof result.status === "number" && result.status !== 0) {
195
+ error(`Profiling command exited with code ${result.status}; not gating on incomplete data.`);
196
+ process.exit(result.status);
197
+ }
198
+ }
199
+ /**
200
+ * Fetch the candidate build's list row (the suffix the pre-run snapshot and poll
201
+ * share). Scopes to the candidate buildId so the server's newest-50 cap cannot
202
+ * hide a re-run of an older build, and uses fresh=1 to bypass the aggregation
203
+ * cache so the wait reads live state (a cached, stale-low count could let an old
204
+ * run's data pass the wait).
205
+ */
206
+ function fetchBuilds(client, values, buildId) {
207
+ return client.get(client.projectPath(buildBuildsPath({ days: values.days, buildId, fresh: true })));
208
+ }
209
+ /**
210
+ * The candidate's perf-event count right now. Read BEFORE the run so the ingest
211
+ * wait can require the count to grow -- a CI re-run for the same build_id must
212
+ * not be satisfied by the previous run's data. Fails closed if the snapshot
213
+ * cannot be read: without a reliable baseline the wait could pass on old data
214
+ * (a false-green gate), so abort before launch rather than guess 0.
215
+ */
216
+ async function readPriorEventCount(client, values, buildId) {
217
+ let builds;
218
+ try {
219
+ builds = await fetchBuilds(client, values, buildId);
220
+ }
221
+ catch (err) {
222
+ error(`Could not read the build's pre-run event count (${err instanceof Error ? err.message : String(err)}). Aborting before launch; pass --skip-wait if your SDK flushes synchronously.`);
223
+ process.exit(1);
224
+ }
225
+ // A non-error response that is not an array means we cannot trust the baseline
226
+ // (it could read as 0 and let an old run's data satisfy the wait), so fail closed.
227
+ if (!Array.isArray(builds)) {
228
+ error("The builds API returned an unexpected response (no build list); cannot establish a " +
229
+ "pre-run baseline. Aborting before launch; pass --skip-wait if your SDK flushes synchronously.");
230
+ process.exit(1);
231
+ }
232
+ return buildEventCount(builds, buildId);
233
+ }
234
+ /** Poll the builds list until the candidate gains fresh events; exit on timeout. */
235
+ async function awaitIngest(client, values, buildId, priorEventCount, timeoutMs, intervalMs) {
236
+ success(`Waiting up to ${timeoutMs / 1000}s for build '${buildId}' to ingest...`);
237
+ const landed = await waitForIngest({
238
+ buildId,
239
+ minEventCount: priorEventCount,
240
+ timeoutMs,
241
+ intervalMs,
242
+ now: () => Date.now(),
243
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
244
+ fetchBuilds: () => fetchBuilds(client, values, buildId),
245
+ });
246
+ if (!landed) {
247
+ error(`Build '${buildId}' did not ingest fresh events within ${timeoutMs / 1000}s. ` +
248
+ "The SDK may not have flushed, or ingest/aggregation is lagging. " +
249
+ "Increase --ingest-timeout, or check with 'framedash builds'. " +
250
+ `Also confirm the game picked up the awaited build id: --build-id reaches the game via the ` +
251
+ `exported FRAMEDASH_BUILD_ID env var and the SDK's BeginAutomatedSessionFromEnvironment() ` +
252
+ `auto-session pickup -- a game that hardcodes its buildId will never match '${buildId}'.`);
253
+ process.exit(1);
254
+ }
255
+ success(`Build '${buildId}' ingested.`);
256
+ }
257
+ /** Fetch the build comparison, print it, and (with --fail-on-regression) gate. */
258
+ async function runRegressionGate(client, config, values, gate) {
259
+ const comparison = await client.get(client.projectPath(buildBuildComparePath({
260
+ baseline: gate.baseline,
261
+ candidate: gate.candidate,
262
+ days: values.days,
263
+ mapId: values.map,
264
+ platform: values.platform,
265
+ // Bypass the compare cache so a re-run gates on the just-ingested data.
266
+ fresh: true,
267
+ })));
268
+ // A non-error 2xx with a malformed body must fail closed, not crash the gate.
269
+ if (!comparison || !Array.isArray(comparison.diffs)) {
270
+ error("Unexpected response from the builds/compare API (no comparison data).");
271
+ process.exit(1);
272
+ }
273
+ log(config.format === "json"
274
+ ? formatOutput(comparison, "json")
275
+ : formatOutput(comparison.diffs, config.format));
276
+ const verdict = evaluateRegression(comparison, {
277
+ metric: gate.metric,
278
+ thresholdPct: gate.thresholdPct,
279
+ });
280
+ // Report-only (no --fail-on-regression): never exit non-zero, but still warn --
281
+ // on a regression, AND when nothing was comparable -- so a misconfigured CI job
282
+ // (wrong build IDs, or a metric with no data) does not silently go green.
283
+ if (!values["fail-on-regression"]) {
284
+ if (verdict.evaluated.length === 0) {
285
+ warn(gate.metric
286
+ ? `No comparable '${gate.metric}' data between these builds; the gate evaluated nothing (check the build IDs / metric).`
287
+ : "No comparable performance data between these builds; the gate evaluated nothing (check the build IDs).");
288
+ }
289
+ else if (verdict.failed) {
290
+ warn(`Regression detected (threshold ${gate.thresholdPct}%): ${verdict.offenders
291
+ .map(formatMetricDiff)
292
+ .join("; ")}. Pass --fail-on-regression to fail the build.`);
293
+ }
294
+ return;
295
+ }
296
+ // Fail closed: nothing comparable means the gate is unmeasured, not passed.
297
+ if (verdict.evaluated.length === 0) {
298
+ error(gate.metric
299
+ ? `No comparable '${gate.metric}' data between these builds; cannot evaluate a regression.`
300
+ : "No comparable performance data between these builds; cannot evaluate a regression.");
301
+ process.exit(1);
302
+ }
303
+ if (verdict.failed) {
304
+ error(`Performance regression detected (threshold ${gate.thresholdPct}%): ${verdict.offenders
305
+ .map(formatMetricDiff)
306
+ .join("; ")}`);
307
+ process.exit(1);
308
+ }
309
+ success(`No performance regression beyond ${gate.thresholdPct}% (${verdict.evaluated.length} metric(s) checked).`);
310
+ }
311
+ /**
312
+ * Turnkey CI profiling gate: export the FRAMEDASH_* session contract, launch the
313
+ * configured game/profiling command, wait for its perf data to ingest, then run
314
+ * the perf-diff regression gate against a baseline build.
315
+ */
316
+ export async function runProfileTest(args) {
317
+ await runCommand({
318
+ args,
319
+ help: HELP,
320
+ options: {
321
+ command: { type: "string" },
322
+ "build-id": { type: "string" },
323
+ branch: { type: "string" },
324
+ commit: { type: "string" },
325
+ scenario: { type: "string" },
326
+ "command-timeout": { type: "string" },
327
+ "ingest-timeout": { type: "string" },
328
+ "poll-interval": { type: "string" },
329
+ "skip-wait": { type: "boolean" },
330
+ baseline: { type: "string" },
331
+ metric: { type: "string" },
332
+ threshold: { type: "string" },
333
+ days: { type: "string" },
334
+ map: { type: "string" },
335
+ platform: { type: "string" },
336
+ "fail-on-regression": { type: "boolean" },
337
+ },
338
+ }, async ({ client, config, values }) => {
339
+ const command = values.command?.trim();
340
+ if (!command) {
341
+ error("--command is required (the game/profiling command to launch)");
342
+ process.exit(1);
343
+ }
344
+ // Resolve the build identity: explicit flags win, else fall back to git.
345
+ // Only spawn `git` for a field the caller did not provide.
346
+ const branchFlag = values.branch;
347
+ const commitFlag = values.commit;
348
+ const identity = resolveProfileIdentity({
349
+ buildId: values["build-id"],
350
+ branch: branchFlag,
351
+ commit: commitFlag,
352
+ scenario: values.scenario,
353
+ }, {
354
+ branch: branchFlag ? undefined : gitOutput(["rev-parse", "--abbrev-ref", "HEAD"]),
355
+ commit: commitFlag ? undefined : gitOutput(["rev-parse", "HEAD"]),
356
+ });
357
+ if (!identity) {
358
+ error("Could not determine a build id: pass --build-id or --commit, or run inside a git repo.");
359
+ process.exit(1);
360
+ }
361
+ // Validate everything up front so a misconfig fails before we spend minutes
362
+ // running the game only to reject the result.
363
+ const gate = resolveGateOptions(values, identity.buildId);
364
+ // Bounds only the launched game/profiling process, NOT the ingest wait
365
+ // (--ingest-timeout covers that separately). 0 disables the bound.
366
+ const commandTimeoutMs = parseSeconds(values["command-timeout"], "command-timeout", DEFAULT_COMMAND_TIMEOUT_S, {
367
+ allowZero: true,
368
+ max: MAX_COMMAND_TIMEOUT_S,
369
+ }) * 1000;
370
+ const ingestTimeoutMs = parseSeconds(values["ingest-timeout"], "ingest-timeout", DEFAULT_INGEST_TIMEOUT_S) * 1000;
371
+ const pollIntervalMs = parseSeconds(values["poll-interval"], "poll-interval", DEFAULT_POLL_INTERVAL_S) * 1000;
372
+ // The ingest snapshot/poll run in a retry loop, so they need a client that
373
+ // THROWS on a transient 429/5xx (the default client exits the process,
374
+ // which would kill the wait on a temporary blip). The gate compare keeps
375
+ // the default exit-on-error client (a compare failure should fail the gate).
376
+ const skipWait = Boolean(values["skip-wait"]);
377
+ const pollClient = skipWait
378
+ ? client
379
+ : createClient(config.baseUrl, config.credential, config.projectId, {
380
+ throwOnError: true,
381
+ });
382
+ // Snapshot the candidate's event count BEFORE the run (only when we will
383
+ // wait), so the ingest wait can require fresh events rather than accepting
384
+ // a prior run's data for the same build_id.
385
+ const priorEventCount = skipWait
386
+ ? 0
387
+ : await readPriorEventCount(pollClient, values, identity.buildId);
388
+ // 1) Launch the profiling command with the FRAMEDASH_* contract exported, so
389
+ // the SDK's BeginAutomatedSessionFromEnvironment() stamps build_id + ci.* tags
390
+ // onto every event (including the perf_heartbeat the gate reads).
391
+ success(`Running profiling build '${identity.buildId}'${identity.scenario ? ` (scenario: ${identity.scenario})` : ""}`);
392
+ // Build the child environment. Two safeguards:
393
+ // 1) The runner owns the whole FRAMEDASH_* session contract: clear any
394
+ // contract key it did not set this run, so the game cannot inherit stale
395
+ // branch/commit/scenario left in the environment by a prior CI step.
396
+ // 2) The gate key (analytics:read) must not be handed to the game as its key
397
+ // -- the game needs a separate events:write ingest key. When the gate key
398
+ // came from FRAMEDASH_API_KEY in the environment (no --api-key flag), strip
399
+ // it from the child so the game cannot inherit a read key.
400
+ const sessionEnv = buildSessionEnv(identity);
401
+ const childEnv = {
402
+ ...process.env,
403
+ ...passthroughProjectEnv(config),
404
+ ...sessionEnv,
405
+ };
406
+ for (const key of SESSION_ENV_KEYS) {
407
+ if (!(key in sessionEnv))
408
+ delete childEnv[key];
409
+ }
410
+ if (!values["api-key"] && !values["api-key-file"] && process.env.FRAMEDASH_API_KEY) {
411
+ delete childEnv.FRAMEDASH_API_KEY;
412
+ warn("Removed the gate's FRAMEDASH_API_KEY (an analytics:read key) from the launched " +
413
+ "game's environment so it is not used as the game's ingest key. Set the game's " +
414
+ "events:write key separately, or pass the gate key via --api-key/--api-key-file.");
415
+ }
416
+ await launchProfilingRun(command, childEnv, commandTimeoutMs);
417
+ // 2) Wait for the candidate build's perf events to land (unless skipped).
418
+ if (!skipWait) {
419
+ await awaitIngest(pollClient, values, identity.buildId, priorEventCount, ingestTimeoutMs, pollIntervalMs);
420
+ }
421
+ // 3) Run the perf-diff gate when a baseline is given; otherwise the run is
422
+ // complete (the SDK reported the build; the caller can compare it later).
423
+ if (!gate.baseline) {
424
+ success("Profiling run complete. Pass --baseline to gate on a build-over-build regression.");
425
+ return;
426
+ }
427
+ await runRegressionGate(client, config, values, {
428
+ baseline: gate.baseline,
429
+ candidate: identity.buildId,
430
+ metric: gate.metric,
431
+ thresholdPct: gate.thresholdPct,
432
+ });
433
+ });
434
+ }
435
+ const HELP = `Usage: framedash run-profile-test --command "<cmd>" [options]
436
+
437
+ Run an automated profiling test end-to-end for CI: export the FRAMEDASH_*
438
+ session contract, launch your game/profiling command, wait for its performance
439
+ data to ingest, then (with --baseline) gate on a build-over-build regression.
440
+
441
+ The launched command inherits these environment variables; an SDK that calls
442
+ BeginAutomatedSessionFromEnvironment() picks them up automatically:
443
+ FRAMEDASH_BUILD_ID the candidate build_id (first-class)
444
+ FRAMEDASH_GIT_BRANCH ci.branch attribute
445
+ FRAMEDASH_GIT_COMMIT ci.commit attribute
446
+ FRAMEDASH_TEST_SCENARIO ci.scenario attribute
447
+ The build id from --build-id (or the git default) reaches the game ONLY via the
448
+ exported FRAMEDASH_BUILD_ID env var, which BeginAutomatedSessionFromEnvironment()
449
+ reads. A game that hardcodes its buildId will never match the awaited build, so
450
+ the ingest wait will time out -- do not hardcode the SDK build id in a CI build.
451
+ FRAMEDASH_PROJECT_ID / FRAMEDASH_BASE_URL are forwarded so the build targets the
452
+ same project the gate queries. The gate uses an analytics:read key while your
453
+ game needs a separate events:write ingest key: pass the gate key with
454
+ --api-key/--api-key-file and keep the game's ingest key in the launched command's
455
+ FRAMEDASH_API_KEY, so the gate key does not get inherited as the game's key.
456
+
457
+ Required:
458
+ --command <cmd> Game/profiling command to launch (run via the shell)
459
+
460
+ Run bound (kills the launched process tree on timeout so a game that never
461
+ self-quits cannot hang a CI job forever; a timeout fails the run without gating):
462
+ --command-timeout <s> Max seconds the launched command may run (default: 1800,
463
+ 0 disables the bound)
464
+
465
+ Build identity (each defaults as noted; exported to the child):
466
+ --build-id <id> Candidate build_id (default: --commit, else git HEAD)
467
+ --branch <name> default: git rev-parse --abbrev-ref HEAD
468
+ --commit <sha> default: git rev-parse HEAD
469
+ --scenario <name> Test scenario label (no default)
470
+
471
+ Note: the gate compares ALL of a build_id's samples in the window (the build_id
472
+ is the comparison unit, as in 'perf-diff'). If a CI job may re-run the SAME
473
+ commit, give each run a unique build_id (e.g. --build-id "$GIT_SHA-$RUN_ID") so
474
+ the comparison is not diluted by a previous run's samples.
475
+
476
+ Ingest wait (polls a cache-bypassing live read and waits for the build's
477
+ perf-event count to GROW past its pre-run value, so a re-run for the same build
478
+ id is not satisfied by old data):
479
+ --ingest-timeout <s> Max seconds to wait for fresh events (default: 180)
480
+ --poll-interval <s> Seconds between build-list polls (default: 5)
481
+ --skip-wait Skip the ingest wait (e.g. the SDK flushed synchronously)
482
+
483
+ Regression gate (runs only when --baseline is set):
484
+ --baseline <id> Known-good build_id to compare the candidate against
485
+ --metric <name> Gate on one metric: frame_time, memory, gpu_time (default: all)
486
+ --threshold <pct> Tolerate a regression up to this percent (default: 0)
487
+ --fail-on-regression Exit 1 on a regression beyond the threshold (needs --baseline)
488
+ --days <n> Comparison window in days: 7, 14, 30, 90 (default: 30)
489
+ --map <id> Restrict the comparison to one map
490
+ --platform <name> Restrict the comparison to one platform
491
+
492
+ Global:
493
+ --api-key <key> API key (or FRAMEDASH_API_KEY env)
494
+ --project-id <uuid> Project ID (or FRAMEDASH_PROJECT_ID env)
495
+ --base-url <url> API base URL (default: https://app.framedash.dev)
496
+ --format <fmt> Comparison output format: json, table, csv (default: json)
497
+ -h, --help Show help
498
+
499
+ Example (GitHub Actions gate):
500
+ framedash run-profile-test \\
501
+ --command "./Build/Game.exe -nullrhi -ExecCmds='Automation RunTest Perf'" \\
502
+ --scenario nightly --baseline "$BASE_SHA" --threshold 5 --fail-on-regression`;
503
+ //# sourceMappingURL=run-profile-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-profile-test.js","sourceRoot":"","sources":["../../src/commands/run-profile-test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const thresholdProfiles: (args: string[]) => Promise<void>;
2
+ //# sourceMappingURL=threshold-profiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threshold-profiles.d.ts","sourceRoot":"","sources":["../../src/commands/threshold-profiles.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,iBAAiB,mCAE5B,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { formatOutput } from "../lib/formatters.js";
2
+ import { log } from "../lib/logger.js";
3
+ import { runCommand, withSubcommands } from "../lib/run-command.js";
4
+ const HELP = `Usage: framedash threshold-profiles <subcommand> [options]
5
+
6
+ List threshold profiles (performance budgets used by alert rules).
7
+
8
+ Subcommands:
9
+ list List threshold profiles
10
+
11
+ Run 'framedash threshold-profiles <subcommand> --help' for more info.`;
12
+ export const thresholdProfiles = withSubcommands("threshold-profiles", HELP, {
13
+ list: thresholdProfilesList,
14
+ });
15
+ async function thresholdProfilesList(args) {
16
+ await runCommand({
17
+ args,
18
+ help: "Usage: framedash threshold-profiles list [--format json|table|csv] [global options]",
19
+ }, async ({ client, config }) => {
20
+ const data = await client.get(client.projectPath("threshold-profiles"));
21
+ log(formatOutput(data, config.format));
22
+ });
23
+ }
24
+ //# sourceMappingURL=threshold-profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threshold-profiles.js","sourceRoot":"","sources":["../../src/commands/threshold-profiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,IAAI,GAAG;;;;;;;sEAOyD,CAAC;AAEvE,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAC,oBAAoB,EAAE,IAAI,EAAE;IAC5E,IAAI,EAAE,qBAAqB;CAC3B,CAAC,CAAC;AAEH,KAAK,UAAU,qBAAqB,CAAC,IAAc;IAClD,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,qFAAqF;KAC3F,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QAC5B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACxE,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC"}
package/dist/index.js CHANGED
@@ -6,14 +6,21 @@ const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.u
6
6
  const HELP = `Usage: framedash <command> [options]
7
7
 
8
8
  Commands:
9
- auth Verify API key and list projects
9
+ login Sign in via the browser (interactive OAuth; CI should use
10
+ the FRAMEDASH_API_KEY env var instead)
11
+ logout Revoke and remove a stored browser login
12
+ auth Verify credentials, show their source, and list projects
10
13
  status Show project status and key metrics
11
14
  dashboard Show dashboard metrics
12
15
  retention Show player retention cohorts
13
16
  funnel Analyze event funnels
17
+ builds List builds seen for the project (for perf-diff)
18
+ perf-diff Compare two builds and gate CI on perf regressions
19
+ run-profile-test Run a profiling build, wait for ingest, gate on regression
14
20
  query Execute a read-only ClickHouse query
15
21
  alerts Manage alert rules (list, create, update, delete)
16
22
  maps Manage maps (list, delete)
23
+ threshold-profiles List threshold profiles (perf budgets for alerts)
17
24
  content Manage content registry (list, import, delete)
18
25
  map-capture Upload captured map images
19
26
 
@@ -22,6 +29,8 @@ Global Options:
22
29
  a key passed as --api-key is visible in the process
23
30
  list and shell history.
24
31
  --api-key-file <path> Read the API key from a file ('-' for stdin)
32
+ Without any API key, commands fall back to the OAuth
33
+ tokens stored by 'framedash login' for the base URL.
25
34
  --project-id <uuid> Project ID (or FRAMEDASH_PROJECT_ID env)
26
35
  --base-url <url> API base URL (default: https://app.framedash.dev)
27
36
  --format <fmt> Output format: json, table, csv (default: json)
@@ -31,14 +40,20 @@ Global Options:
31
40
  Run 'framedash <command> --help' for command-specific options.`;
32
41
  /** Type-safe command registry — typos in export names become build-time errors. */
33
42
  const COMMANDS = {
43
+ login: () => import("./commands/login.js").then((m) => m.login),
44
+ logout: () => import("./commands/logout.js").then((m) => m.logout),
34
45
  auth: () => import("./commands/auth.js").then((m) => m.auth),
35
46
  status: () => import("./commands/status.js").then((m) => m.status),
36
47
  dashboard: () => import("./commands/dashboard.js").then((m) => m.dashboard),
37
48
  retention: () => import("./commands/retention.js").then((m) => m.retention),
38
49
  funnel: () => import("./commands/funnel.js").then((m) => m.funnel),
50
+ builds: () => import("./commands/builds.js").then((m) => m.builds),
51
+ "perf-diff": () => import("./commands/perf-diff.js").then((m) => m.perfDiff),
52
+ "run-profile-test": () => import("./commands/run-profile-test.js").then((m) => m.runProfileTest),
39
53
  query: () => import("./commands/query.js").then((m) => m.query),
40
54
  alerts: () => import("./commands/alerts.js").then((m) => m.alerts),
41
55
  maps: () => import("./commands/maps.js").then((m) => m.maps),
56
+ "threshold-profiles": () => import("./commands/threshold-profiles.js").then((m) => m.thresholdProfiles),
42
57
  content: () => import("./commands/content.js").then((m) => m.content),
43
58
  "map-capture": () => import("./commands/map-capture.js").then((m) => m.mapCaptureCommand),
44
59
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;KAC3F,OAAiB,CAAC;AAEpB,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;+DAyBkD,CAAC;AAEhE,mFAAmF;AACnF,MAAM,QAAQ,GAAqE;IAClF,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAClE,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAClE,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/D,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAClE,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IACrE,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC;CACzF,CAAC;AAEF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAChC,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAE1C,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;IACjD,GAAG,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;IAC1D,GAAG,CAAC,IAAI,CAAC,CAAC;IACV,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,IAAI,CAAC;IACJ,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;QACvC,KAAK,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;QACrC,GAAG,CAAC,IAAI,CAAC,CAAC;QACV,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,+EAA+E;IAC/E,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAE,EAAE,CAAC;IACtC,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC;AACvB,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACd,IAAI,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,+BAA+B,EAAE,CAAC;QAC3F,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}