tracksiy 0.3.0 → 0.4.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
@@ -126,6 +126,7 @@ Labels: `bug`, `feature`, `question`, `blocker`. `npx tracksiy support` prints t
126
126
  | `monitors declare` | Declare monitors for a project up front, idempotently: heartbeats for jobs, HTTP checks, TLS, ingest silence. | write | `monitors_declare` |
127
127
  | `monitors delete <slug>` | Remove one of a project's monitors by the slug it was declared with. | **destructive** | `monitors_delete` |
128
128
  | `performance` | Is this project slow, and did it get slower? Transactions compared with the previous window, plus uptime. | read | `performance` |
129
+ | `queries` | Which database queries and outbound HTTP calls make this project slow, with N+1 detection and the routes that run them. | read | `queries` |
129
130
  | `issues list` | Recent issues of a project, newest activity first, with the frames that matter inlined. | read | `issues_list` |
130
131
  | `issues view <id>` | One issue in enough depth to act on: latest event, stack frames, tags, recent volume. | read | `issues_view` |
131
132
  | `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.3.0" : "0.0.0-dev";
7
+ var VERSION = true ? "0.4.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
@@ -851,10 +851,16 @@ var monitorsDeclare = {
851
851
  ssl: { type: "list", value: "<slug>=<url>", description: "TLS certificate check (expiry is reported on every check)." },
852
852
  ingest: { type: "list", value: "<slug>[:<surface>]", description: "Alert when this project (or surface) stops sending events." },
853
853
  interval: { type: "integer", value: "<seconds>", description: "Interval for --get/--ping/--ssl/--ingest monitors." },
854
+ "expect-text": { type: "string", value: "<text>", description: "--get: down unless the body contains this (a 200 with an empty shell is down)." },
855
+ "expect-no-text": { type: "string", value: "<text>", description: "--get: down if the body contains this, e.g. Whoops or Exception." },
856
+ "user-agent": { type: "string", value: "<ua>", description: "--get/--ping: ask as this User-Agent, e.g. GPTBot/1.0, to see what crawlers get." },
857
+ "dry-run": { type: "boolean", description: "Show what would be created, adopted or changed; write nothing." },
854
858
  body: { type: "json", value: "<json|@file|->", description: "Raw request body; overrides the shorthand flags." }
855
859
  },
856
860
  examples: [
857
- { command: "tracksiy monitors declare --heartbeat nightly-backup:93600 --get site=https://example.com", description: "A backup heartbeat and a homepage check." }
861
+ { command: "tracksiy monitors declare --heartbeat nightly-backup:93600 --get site=https://example.com", description: "A backup heartbeat and a homepage check." },
862
+ { command: "tracksiy monitors declare --get tr-landing=https://example.com/tr --expect-text '<h1' --expect-no-text Whoops --user-agent GPTBot/1.0", description: "The page as an answer engine gets it: real content, no error template." },
863
+ { command: "tracksiy monitors declare --heartbeat tariff-sync:608400 --dry-run", description: "See the change before making it." }
858
864
  ],
859
865
  output: "Server result per monitor, including check-in URLs for heartbeats.",
860
866
  async run(ctx) {
@@ -870,7 +876,17 @@ var monitorsDeclare = {
870
876
  for (const item of i[type] ?? []) {
871
877
  const eq = String(item).indexOf("=");
872
878
  if (eq < 1) throw usageError("bad_value", `--${type} expects <slug>=<url>, got ${JSON.stringify(item)}`);
873
- monitors.push(compact({ slug: item.slice(0, eq), type, url: item.slice(eq + 1), intervalSeconds: i.interval }));
879
+ monitors.push(
880
+ compact({
881
+ slug: item.slice(0, eq),
882
+ type,
883
+ url: item.slice(eq + 1),
884
+ intervalSeconds: i.interval,
885
+ expectText: type === "get" ? i.expectText : void 0,
886
+ expectNoText: type === "get" ? i.expectNoText : void 0,
887
+ userAgent: type !== "ssl" ? i.userAgent : void 0
888
+ })
889
+ );
874
890
  }
875
891
  }
876
892
  for (const item of i.ingest ?? []) {
@@ -880,6 +896,7 @@ var monitorsDeclare = {
880
896
  if (!monitors.length) throw usageError("missing_flag", "Nothing to declare.", "Add --heartbeat, --get, --ping, --ssl, --ingest, or --body.");
881
897
  body = { monitors };
882
898
  }
899
+ if (i.dryRun) body = { ...body, dryRun: true };
883
900
  return ctx.api.send("POST", await projectPath(ctx, "monitors"), body);
884
901
  }
885
902
  };
@@ -904,16 +921,26 @@ var performance = {
904
921
  effect: "read",
905
922
  endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/performance" },
906
923
  flags: {
907
- since: { type: "string", value: TIME, description: "Window start (default: server's, 24h)." },
908
- op: { type: "string", value: "<op>", description: "Only this operation, e.g. http.server." },
909
- sort: { type: "string", value: "<key>", description: "Order (server default: slower first)." },
910
- limit: { type: "integer", value: "<n>", description: "How many transactions." }
924
+ since: { type: "string", value: TIME, description: "Window start (default: server's, 24h; 7d with --release)." },
925
+ op: { type: "string", value: "<op>", description: "Only this operation, e.g. http.server or pageload." },
926
+ sort: { type: "string", enum: ["slower", "duration", "p95", "failures"], description: "Order (default: slower first)." },
927
+ limit: { type: "integer", value: "<n>", description: "How many transactions." },
928
+ release: { type: "string", value: "<version>", description: "Compare this release's transactions with --against instead of two time windows." },
929
+ against: { type: "string", value: "<version>", description: "The release to compare with (default: the one before --release)." }
911
930
  },
912
- output: "{since, until, transactions:[{transaction, op, current{...}, previous{...}, changeRatio, slower}], uptime[...], blindSpots[...]}",
931
+ examples: [
932
+ { command: "tracksiy performance --sort p95", description: "The slowest routes by p95." },
933
+ { command: "tracksiy performance --release web@$(git rev-parse HEAD)", description: "Did this deploy make anything slower or failing?" },
934
+ { command: "tracksiy performance --op pageload --select transactions.transaction,transactions.current.webVitals", description: "Core Web Vitals (p75) per page." }
935
+ ],
936
+ output: "{since, until, release?, againstRelease?, transactions:[{transaction, op, current{count, avgDurationMs, p50Ms, p95Ms, p99Ms, failureRate, failed, status, webVitals}, previous{...}, changeRatio, slower}], uptime[...], blindSpots[...]}",
913
937
  text: (d) => `${table(d.transactions ?? [], [
914
938
  "transaction",
915
939
  "op",
916
- ["p95 ms", (r) => r.current?.p95Ms ?? r.current?.p95 ?? "-"],
940
+ ["p50", (r) => r.current?.p50Ms ?? "-"],
941
+ ["p95", (r) => r.current?.p95Ms ?? "-"],
942
+ ["p99", (r) => r.current?.p99Ms ?? "-"],
943
+ ["fail", (r) => r.current?.failureRate == null ? "-" : `${(r.current.failureRate * 100).toFixed(1)}%`],
917
944
  ["count", (r) => r.current?.count ?? "-"],
918
945
  ["change", (r) => r.changeRatio == null ? "-" : `${r.changeRatio > 0 ? "+" : ""}${Math.round(r.changeRatio * 100)}%`],
919
946
  ["slower", (r) => r.slower]
@@ -926,11 +953,52 @@ ${table(d.uptime, ["name", "url", "upNow", "avgResponseMs", "downChecks"])}` : "
926
953
  since: since(ctx.input.since),
927
954
  op: ctx.input.op,
928
955
  sort: ctx.input.sort,
956
+ limit: ctx.input.limit,
957
+ release: ctx.input.release,
958
+ against: ctx.input.against
959
+ });
960
+ }
961
+ };
962
+ var queries = {
963
+ path: ["queries"],
964
+ aliases: [["performance", "queries"]],
965
+ summary: "Which database queries and outbound HTTP calls make this project slow, with N+1 detection and the routes that run them.",
966
+ description: "Aggregated from the spans of sampled transactions (op db* and http.client), normalized so no literal is stored. Ranked by total time by default: the query that costs the most overall. nPlusOne marks a query one request ran 10+ times.",
967
+ context: "project",
968
+ effect: "read",
969
+ endpoint: { method: "GET", path: "/api/agent/v1/projects/{org}/{project}/queries" },
970
+ flags: {
971
+ since: { type: "string", value: TIME, description: "Window start (default 24h)." },
972
+ op: { type: "string", value: "<db|http.client|op>", description: "db (every db* op), http.client, or an exact op." },
973
+ transaction: { type: "string", value: "<route>", description: "Only queries run by this transaction, e.g. /orders." },
974
+ sort: { type: "string", enum: ["total", "avg", "max", "count", "perTransaction"], description: "Order (default total)." },
975
+ limit: { type: "integer", value: "<n>", description: "How many (max 100)." }
976
+ },
977
+ examples: [
978
+ { command: "tracksiy queries --since 24h", description: "The queries that cost the most time today." },
979
+ { command: "tracksiy queries --sort perTransaction --select queries.query,queries.maxPerTransaction,queries.routes", description: "N+1 candidates." }
980
+ ],
981
+ output: "{since, until, queries:[{fingerprint, op, query, count, transactions, perTransaction, maxPerTransaction, totalMs, avgMs, maxMs, previous{count, avgMs}, changeRatio, nPlusOne, routes[{transaction, count, avgMs}]}], note}",
982
+ text: (d) => table(d.queries ?? [], [
983
+ ["total ms", (r) => r.totalMs],
984
+ ["avg", (r) => r.avgMs ?? "-"],
985
+ ["max", (r) => r.maxMs],
986
+ ["count", (r) => r.count],
987
+ ["per req", (r) => r.perTransaction],
988
+ ["n+1", (r) => r.nPlusOne ? "yes" : ""],
989
+ ["query", (r) => String(r.query).slice(0, 90)]
990
+ ]),
991
+ async run(ctx) {
992
+ return ctx.api.get(await projectPath(ctx, "queries"), {
993
+ since: since(ctx.input.since),
994
+ op: ctx.input.op,
995
+ transaction: ctx.input.transaction,
996
+ sort: ctx.input.sort,
929
997
  limit: ctx.input.limit
930
998
  });
931
999
  }
932
1000
  };
933
- var fleet_default = [orgs, projects, overview, changes, health, monitorsList, monitorsDeclare, monitorsDelete, performance];
1001
+ var fleet_default = [orgs, projects, overview, changes, health, monitorsList, monitorsDeclare, monitorsDelete, performance, queries];
934
1002
 
935
1003
  // src/commands/issues.ts
936
1004
  var FILTERS = {
@@ -3101,7 +3169,7 @@ var COMMANDS = [
3101
3169
  ];
3102
3170
  var GROUPS = [
3103
3171
  ["Start", "init auth onboarding describe"],
3104
- ["Look", "overview changes health orgs projects issues monitors performance releases alerts keys artifacts"],
3172
+ ["Look", "overview changes health orgs projects issues monitors performance queries releases alerts keys artifacts"],
3105
3173
  ["Ship", "sourcemaps proguard"],
3106
3174
  ["Manage", "tokens"],
3107
3175
  ["Talk to us", "support"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracksiy",
3
- "version": "0.3.0",
3
+ "version": "0.4.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": {