tracksiy 0.13.0 → 0.14.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/README.md CHANGED
@@ -150,6 +150,14 @@ Labels: `bug`, `feature`, `question`, `blocker`. `npx tracksiy support` prints t
150
150
  | `build upload <path>` | Measure an app build (APK, AAB, IPA, .xcarchive, .app) on this machine and record its size: per category, the largest files, growth against the previous build, size budgets. The binary is not uploaded. | write | `build_upload` |
151
151
  | `build list` | Recorded builds newest first, with their growth against the build before. | read | `build_list` |
152
152
  | `build view <id>` | One build: size per category, the largest files, what changed against the previous build. | read | `build_view` |
153
+ | `metrics list` | Metrics the app sent (Sentry.metrics.*, sentry-php traceMetrics, the server agent): name, type, unit, series and tag keys. | read | `metrics_list` |
154
+ | `metrics query <name>` | One metric as a time series: sum, count, avg, min, max, last or p50..p99 per interval, grouped by a tag. | read | `metrics_query` |
155
+ | `profiles functions` | Where the CPU time goes: functions by self or total time, with file and line, per transaction or overall. | read | `profiles_functions` |
156
+ | `profiles list` | Stored profiles newest first. | read | `profiles_list` |
157
+ | `profiles flamegraph <id>` | One profile's call tree, root to leaf, in ms (text: the heaviest paths). | read | `profiles_flamegraph` |
158
+ | `replays list` | Session replays newest first (kept only when the project turned replays on). | read | `replays_list` |
159
+ | `replays view <id>` | One replay: what the user did, in order (navigations, clicks / taps, console, network), and its segments. | read | `replays_view` |
160
+ | `issues replays <id>` | Replays that recorded this issue's events. | read | `issues_replays` |
153
161
  | `issues list` | Recent issues of a project, newest activity first, with the frames that matter inlined. | read | `issues_list` |
154
162
  | `issues view <id>` | One issue in enough depth to act on: latest event, stack frames, tags, recent volume. | read | `issues_view` |
155
163
  | `issues resolve <ids...>` | Mark issues resolved, optionally fixed in a release so old clients do not reopen them. Records why. | write | `issues_resolve` |
package/dist/tracksiy.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  import { readFileSync as readFileSync2 } from "node:fs";
5
5
 
6
6
  // src/version.ts
7
- var VERSION = true ? "0.13.0" : "0.0.0-dev";
7
+ var VERSION = true ? "0.14.0" : "0.0.0-dev";
8
8
  var USER_AGENT = `tracksiy-cli/${VERSION} (node ${process.versions.node}; ${process.platform})`;
9
9
 
10
10
  // src/core/errors.ts
@@ -1050,7 +1050,7 @@ var settingsGet = {
1050
1050
  context: "project",
1051
1051
  effect: "read",
1052
1052
  endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/settings" },
1053
- output: "{sourceRoots[], environments[], environmentPolicy, fingerprintRules[], privacyRules[], performanceIssues, storage{maxEventsPerIssue, sourcemapKeepReleases, sourcemapKeepUsedDays}, releaseHealth{crashFreeSessionsMin, minSessions, maxDropPct, velocityPct, velocityMinEvents}, logs{enabled, minLevel, dailyLimit}, attachments{store, maxKb, quotaMb, retentionDays}}",
1053
+ output: "{sourceRoots[], environments[], environmentPolicy, fingerprintRules[], privacyRules[], performanceIssues, storage{maxEventsPerIssue, sourcemapKeepReleases, sourcemapKeepUsedDays}, releaseHealth{crashFreeSessionsMin, minSessions, maxDropPct, velocityPct, velocityMinEvents}, logs{enabled, minLevel, dailyLimit}, attachments{store, maxKb, quotaMb, retentionDays}, profiles{enabled, quotaMb, retentionDays}, replays{enabled, quotaMb, retentionDays}}",
1054
1054
  async run(ctx) {
1055
1055
  return ctx.api.get(await projectPath(ctx, "settings"));
1056
1056
  }
@@ -1097,6 +1097,12 @@ var settingsSet = {
1097
1097
  "attachment-max-kb": { type: "integer", value: "<kb>", description: "Largest attachment kept (default 5120)." },
1098
1098
  "attachments-quota-mb": { type: "integer", value: "<mb>", description: "The project's attachment budget (default 500); past it new files are dropped (attachment:quota)." },
1099
1099
  "attachments-retention-days": { type: "integer", value: "<days>", description: "Keep attachments this long (default 30)." },
1100
+ profiles: { type: "boolean", description: "Keep profiles (default on); --no-profiles drops them (profile:disabled)." },
1101
+ "profiles-quota-mb": { type: "integer", value: "<mb>", description: "Raw profiles' budget (default 500)." },
1102
+ "profiles-retention-days": { type: "integer", value: "<days>", description: "Keep raw profiles this long (default 7)." },
1103
+ replays: { type: "boolean", description: "Keep session replays (default OFF: a replay records the screen). --replays turns them on." },
1104
+ "replays-quota-mb": { type: "integer", value: "<mb>", description: "Replays' budget (default 1000)." },
1105
+ "replays-retention-days": { type: "integer", value: "<days>", description: "Keep replays this long (default 14)." },
1100
1106
  body: { type: "json", value: "<json|@file|->", description: "The whole declaration; overrides the flags." }
1101
1107
  },
1102
1108
  examples: [
@@ -1106,7 +1112,7 @@ var settingsSet = {
1106
1112
  { command: "tracksiy settings set --source-root 'strip=/var/www/shop/,prepend=backend/,surface=backend'", description: "The server checkout is only the repo's backend/ directory." },
1107
1113
  { command: "tracksiy settings set --max-events-per-issue 500 --sourcemap-keep-releases 5", description: "A noisy project keeps less on disk." }
1108
1114
  ],
1109
- output: "{sourceRoots[], environments[], environmentPolicy, fingerprintRules[], privacyRules[], performanceIssues, storage{maxEventsPerIssue, sourcemapKeepReleases, sourcemapKeepUsedDays}, releaseHealth{crashFreeSessionsMin, minSessions, maxDropPct, velocityPct, velocityMinEvents}, logs{enabled, minLevel, dailyLimit}, attachments{store, maxKb, quotaMb, retentionDays}}",
1115
+ output: "{sourceRoots[], environments[], environmentPolicy, fingerprintRules[], privacyRules[], performanceIssues, storage{maxEventsPerIssue, sourcemapKeepReleases, sourcemapKeepUsedDays}, releaseHealth{crashFreeSessionsMin, minSessions, maxDropPct, velocityPct, velocityMinEvents}, logs{enabled, minLevel, dailyLimit}, attachments{store, maxKb, quotaMb, retentionDays}, profiles{enabled, quotaMb, retentionDays}, replays{enabled, quotaMb, retentionDays}}",
1110
1116
  async run(ctx) {
1111
1117
  const path = await projectPath(ctx, "settings");
1112
1118
  const i = ctx.input;
@@ -1138,6 +1144,18 @@ var settingsSet = {
1138
1144
  ...i.attachmentsQuotaMb !== void 0 ? { quotaMb: i.attachmentsQuotaMb } : {},
1139
1145
  ...i.attachmentsRetentionDays !== void 0 ? { retentionDays: i.attachmentsRetentionDays } : {}
1140
1146
  },
1147
+ profiles: {
1148
+ ...current.profiles ?? {},
1149
+ ...i.profiles !== void 0 ? { enabled: i.profiles } : {},
1150
+ ...i.profilesQuotaMb !== void 0 ? { quotaMb: i.profilesQuotaMb } : {},
1151
+ ...i.profilesRetentionDays !== void 0 ? { retentionDays: i.profilesRetentionDays } : {}
1152
+ },
1153
+ replays: {
1154
+ ...current.replays ?? {},
1155
+ ...i.replays !== void 0 ? { enabled: i.replays } : {},
1156
+ ...i.replaysQuotaMb !== void 0 ? { quotaMb: i.replaysQuotaMb } : {},
1157
+ ...i.replaysRetentionDays !== void 0 ? { retentionDays: i.replaysRetentionDays } : {}
1158
+ },
1141
1159
  releaseHealth: {
1142
1160
  ...current.releaseHealth ?? {},
1143
1161
  ...i.crashFreeMin !== void 0 ? { crashFreeSessionsMin: i.crashFreeMin } : {},
@@ -2000,6 +2018,162 @@ var view = {
2000
2018
  };
2001
2019
  var builds_default = [upload, list2, view];
2002
2020
 
2021
+ // src/commands/telemetry.ts
2022
+ var metricsList = {
2023
+ path: ["metrics", "list"],
2024
+ aliases: [["metrics"]],
2025
+ summary: "Metrics the app sent (Sentry.metrics.*, sentry-php traceMetrics, the server agent): name, type, unit, series and tag keys.",
2026
+ context: "project",
2027
+ effect: "read",
2028
+ endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/metrics" },
2029
+ flags: { since: { type: "string", value: TIME, description: "Default 24h." } },
2030
+ output: "{since, metrics:[{name, type, unit, source, series, points, lastSeen, tagKeys[]}]}",
2031
+ text: (d) => table(d.metrics ?? [], ["name", "type", "unit", "series", "points", ["last", (r) => ago(r.lastSeen)], ["tags", (r) => (r.tagKeys ?? []).join(",")]]),
2032
+ async run(ctx) {
2033
+ return ctx.api.get(await projectPath(ctx, "metrics"), { since: since(ctx.input.since) });
2034
+ }
2035
+ };
2036
+ var metricsQuery = {
2037
+ path: ["metrics", "query"],
2038
+ summary: "One metric as a time series: sum, count, avg, min, max, last or p50..p99 per interval, grouped by a tag.",
2039
+ context: "project",
2040
+ effect: "read",
2041
+ endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/metrics/query" },
2042
+ args: [{ name: "name", required: true, description: "The metric name." }],
2043
+ flags: {
2044
+ agg: { type: "string", enum: ["sum", "count", "avg", "min", "max", "last", "p50", "p75", "p90", "p95", "p99"], description: "Default sum." },
2045
+ since: { type: "string", value: TIME, description: "Default 24h." },
2046
+ until: { type: "string", value: TIME, description: "Default now." },
2047
+ "group-by": { type: "string", value: "<tag>", description: "One series per value of this tag (top 20)." },
2048
+ filter: { type: "string", value: "<key:value>", description: "Only points with this tag value." },
2049
+ interval: { type: "integer", value: "<seconds>", description: "Seconds per point (default by window)." }
2050
+ },
2051
+ examples: [{ command: "tracksiy metrics query checkout.duration --agg p95 --group-by route --since 6h", description: "p95 per route." }],
2052
+ output: "{name, type, unit, agg, interval, series:[{group, total, points:[{t, v, count}]}]}",
2053
+ text: (d) => `${kv({ name: d.name, type: d.type, unit: d.unit, agg: d.agg, interval: `${d.interval}s` })}
2054
+
2055
+ ${table(d.series ?? [], [["group", (r) => r.group ?? "(all)"], ["total", (r) => r.total ?? "-"], ["points", (r) => (r.points ?? []).length], ["last", (r) => (r.points ?? []).at(-1)?.v ?? "-"]])}`,
2056
+ async run(ctx) {
2057
+ const i = ctx.input;
2058
+ return ctx.api.get(await projectPath(ctx, "metrics/query"), { name: i.name, agg: i.agg, since: since(i.since), until: since(i.until, "until"), groupBy: i.groupBy, filter: i.filter, interval: i.interval });
2059
+ }
2060
+ };
2061
+ var profileFunctions = {
2062
+ path: ["profiles", "functions"],
2063
+ aliases: [["profiles"]],
2064
+ summary: "Where the CPU time goes: functions by self or total time, with file and line, per transaction or overall.",
2065
+ context: "project",
2066
+ effect: "read",
2067
+ endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/profiles/functions" },
2068
+ flags: {
2069
+ since: { type: "string", value: TIME, description: "Default 7d." },
2070
+ transaction: { type: "string", value: "<name>", description: "Only profiles of this route / screen." },
2071
+ "application-only": { type: "boolean", description: "Only your own code (in_app frames)." },
2072
+ sort: { type: "string", enum: ["self", "total"], description: "Default self." },
2073
+ limit: { type: "integer", value: "<n>", description: "At most 200 (default 50)." }
2074
+ },
2075
+ examples: [{ command: "tracksiy profiles functions --transaction 'GET /cart' --application-only", description: "Our hottest code on one route." }],
2076
+ output: "{since, sampledMs, functions:[{function, module, file, line, isApplication, selfMs, totalMs, selfPct, totalPct, samples, profiles, transactions}], notes[]}",
2077
+ text: (d) => `${table(d.functions ?? [], [["self %", (r) => r.selfPct], ["total %", (r) => r.totalPct], "function", ["where", (r) => `${r.file ?? r.module ?? ""}${r.line ? ":" + r.line : ""}`], ["app", (r) => r.isApplication ? "yes" : ""]])}${(d.notes ?? []).length ? `
2078
+
2079
+ ${d.notes.join("\n")}` : ""}`,
2080
+ async run(ctx) {
2081
+ const i = ctx.input;
2082
+ return ctx.api.get(await projectPath(ctx, "profiles/functions"), { since: since(i.since), transaction: i.transaction, applicationOnly: i.applicationOnly || void 0, sort: i.sort, limit: i.limit });
2083
+ }
2084
+ };
2085
+ var profileList = {
2086
+ path: ["profiles", "list"],
2087
+ summary: "Stored profiles newest first.",
2088
+ context: "project",
2089
+ effect: "read",
2090
+ endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/profiles" },
2091
+ flags: {
2092
+ since: { type: "string", value: TIME, description: "Only since then." },
2093
+ transaction: { type: "string", value: "<name>", description: "Only this transaction." },
2094
+ trace: { type: "string", value: "<trace id>", description: "Only this trace." },
2095
+ limit: { type: "integer", value: "<n>", description: "At most 200." }
2096
+ },
2097
+ output: "[{id, received, kind, platform, transaction, traceId, release, durationMs, samples, analyzed}]",
2098
+ text: (d) => table(d ?? [], ["id", ["when", (r) => ago(r.received)], "kind", "platform", "transaction", "durationMs", "samples", "analyzed"]),
2099
+ async run(ctx) {
2100
+ const i = ctx.input;
2101
+ return ctx.api.get(await projectPath(ctx, "profiles"), { since: since(i.since), transaction: i.transaction, trace: i.trace, limit: i.limit });
2102
+ }
2103
+ };
2104
+ var profileFlamegraph = {
2105
+ path: ["profiles", "flamegraph"],
2106
+ summary: "One profile's call tree, root to leaf, in ms (text: the heaviest paths).",
2107
+ context: "project",
2108
+ effect: "read",
2109
+ endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/profiles/{id}/flamegraph" },
2110
+ args: [{ name: "id", required: true, description: "The profile id (from `profiles list`)." }],
2111
+ output: "{name, value, children[...]}",
2112
+ text: (d) => {
2113
+ const lines = [];
2114
+ const walk3 = (node, depth) => {
2115
+ if (depth > 12 || lines.length > 60) return;
2116
+ lines.push(`${" ".repeat(depth)}${Math.round(node.value)} ms ${node.name}`);
2117
+ for (const child of (node.children ?? []).slice(0, 3)) walk3(child, depth + 1);
2118
+ };
2119
+ walk3(d, 0);
2120
+ return lines.join("\n");
2121
+ },
2122
+ async run(ctx) {
2123
+ return ctx.api.get(await projectPath(ctx, `profiles/${encodeURIComponent(ctx.input.id)}/flamegraph`));
2124
+ }
2125
+ };
2126
+ var replayList = {
2127
+ path: ["replays", "list"],
2128
+ aliases: [["replays"]],
2129
+ summary: "Session replays newest first (kept only when the project turned replays on).",
2130
+ context: "project",
2131
+ effect: "read",
2132
+ endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/replays" },
2133
+ flags: {
2134
+ since: { type: "string", value: TIME, description: "Only since then." },
2135
+ url: { type: "string", value: "<text>", description: "Only replays that visited a URL containing this." },
2136
+ limit: { type: "integer", value: "<n>", description: "At most 200." }
2137
+ },
2138
+ output: "[{replayId, started, durationMs, segments, platform, release, browser, os, device, urls[], errorIds[], hasVideo}]",
2139
+ text: (d) => table(d ?? [], ["replayId", ["when", (r) => ago(r.lastSeen)], ["length", (r) => `${Math.round(r.durationMs / 1e3)}s`], "platform", ["errors", (r) => (r.errorIds ?? []).length], ["first url", (r) => (r.urls ?? [])[0] ?? "-"]]),
2140
+ async run(ctx) {
2141
+ const i = ctx.input;
2142
+ return ctx.api.get(await projectPath(ctx, "replays"), { since: since(i.since), url: i.url, limit: i.limit });
2143
+ }
2144
+ };
2145
+ var replayView = {
2146
+ path: ["replays", "view"],
2147
+ summary: "One replay: what the user did, in order (navigations, clicks / taps, console, network), and its segments.",
2148
+ context: "project",
2149
+ effect: "read",
2150
+ endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/replays/{replay_id}" },
2151
+ args: [{ name: "id", required: true, description: "The replay id (32 hex)." }],
2152
+ output: "{replayId, ..., segmentList[{segmentId, kind, recordingUrl, videoUrl}], timeline[{offsetMs, category, message, status?, durationMs?, level?}]}",
2153
+ text: (d) => `${kv({ replay: d.replayId, platform: d.platform, release: d.release ?? "-", length: `${Math.round((d.durationMs ?? 0) / 1e3)}s`, segments: d.segments })}
2154
+
2155
+ ${(d.timeline ?? []).map((l) => `${String(Math.round((l.offsetMs ?? 0) / 1e3)).padStart(5)}s ${String(l.category).padEnd(18)} ${l.message ?? ""}${l.status ? ` [${l.status}]` : ""}`).join("\n")}`,
2156
+ async run(ctx) {
2157
+ return ctx.api.get(await projectPath(ctx, `replays/${encodeURIComponent(ctx.input.id)}`));
2158
+ }
2159
+ };
2160
+ var issueReplays = {
2161
+ path: ["issues", "replays"],
2162
+ summary: "Replays that recorded this issue's events.",
2163
+ context: "project",
2164
+ effect: "read",
2165
+ endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/issues/{id}/replays" },
2166
+ args: [{ name: "id", required: true, description: "The issue id." }],
2167
+ output: "{issueId, replays[...], notes[]}",
2168
+ text: (d) => `${table(d.replays ?? [], ["replayId", ["length", (r) => `${Math.round(r.durationMs / 1e3)}s`], "platform", ["first url", (r) => (r.urls ?? [])[0] ?? "-"]])}${(d.notes ?? []).length ? `
2169
+
2170
+ ${d.notes.join("\n")}` : ""}`,
2171
+ async run(ctx) {
2172
+ return ctx.api.get(await projectPath(ctx, `issues/${encodeURIComponent(ctx.input.id)}/replays`));
2173
+ }
2174
+ };
2175
+ var telemetry_default = [metricsList, metricsQuery, profileFunctions, profileList, profileFlamegraph, replayList, replayView, issueReplays];
2176
+
2003
2177
  // src/commands/issues.ts
2004
2178
  import { writeFileSync as writeFileSync2 } from "node:fs";
2005
2179
  var FILTERS = {
@@ -4324,6 +4498,7 @@ var COMMANDS = [
4324
4498
  ...logs_default,
4325
4499
  ...evidence_default,
4326
4500
  ...builds_default,
4501
+ ...telemetry_default,
4327
4502
  ...issues_default,
4328
4503
  ...projects_default,
4329
4504
  ...alerts_default,
@@ -4337,7 +4512,7 @@ var COMMANDS = [
4337
4512
  ];
4338
4513
  var GROUPS = [
4339
4514
  ["Start", "init auth onboarding describe"],
4340
- ["Look", "overview changes health storage orgs projects issues monitors performance mobile logs user-feedback attachments queries not-found releases alerts keys artifacts settings privacy-preview"],
4515
+ ["Look", "overview changes health storage orgs projects issues monitors performance mobile logs metrics profiles replays user-feedback attachments queries not-found releases alerts keys artifacts settings privacy-preview"],
4341
4516
  ["Ship", "sourcemaps proguard build builds"],
4342
4517
  ["Manage", "tokens"],
4343
4518
  ["Talk to us", "support"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracksiy",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Agent-first CLI and MCP server for Tracksiy: errors, uptime, alerts, releases and source maps, from one token.",
5
5
  "type": "module",
6
6
  "bin": {