@sleds/cli 0.1.64 → 0.1.66

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 (3) hide show
  1. package/README.md +20 -0
  2. package/dist/sleds.mjs +1182 -58
  3. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -6061,7 +6061,6 @@ var init_adf = __esm(() => {
6061
6061
  "decisionList"
6062
6062
  ]);
6063
6063
  });
6064
-
6065
6064
  // ../../packages/shared/src/help/whatsNew.ts
6066
6065
  var SEPT_30 = "/whats-new/2026-09-30", WHATS_NEW;
6067
6066
  var init_whatsNew = __esm(() => {
@@ -6836,6 +6835,30 @@ var init_stability = __esm(() => {
6836
6835
  init_series();
6837
6836
  });
6838
6837
 
6838
+ // ../../packages/shared/src/deploys/shipping.ts
6839
+ var DAY_MS5;
6840
+ var init_shipping = __esm(() => {
6841
+ DAY_MS5 = 24 * 60 * 60 * 1000;
6842
+ });
6843
+
6844
+ // ../../packages/shared/src/deploys/doraBands.ts
6845
+ var HOUR_MS2, DAY_MS6, WEEK_MS3, MONTH_DAYS = 30, MONTH_MS;
6846
+ var init_doraBands = __esm(() => {
6847
+ HOUR_MS2 = 60 * 60 * 1000;
6848
+ DAY_MS6 = 24 * HOUR_MS2;
6849
+ WEEK_MS3 = 7 * DAY_MS6;
6850
+ MONTH_MS = MONTH_DAYS * DAY_MS6;
6851
+ });
6852
+
6853
+ // ../../packages/shared/src/deploys/trends.ts
6854
+ var DAY_MS7;
6855
+ var init_trends = __esm(() => {
6856
+ init_batchSize();
6857
+ init_metrics();
6858
+ init_series();
6859
+ DAY_MS7 = 24 * 60 * 60 * 1000;
6860
+ });
6861
+
6839
6862
  // ../../packages/shared/src/newrelic/newrelic.ts
6840
6863
  var init_newrelic = () => {};
6841
6864
 
@@ -8353,6 +8376,9 @@ var init_src = __esm(() => {
8353
8376
  init_batchSize();
8354
8377
  init_report();
8355
8378
  init_series();
8379
+ init_shipping();
8380
+ init_doraBands();
8381
+ init_trends();
8356
8382
  init_sentry();
8357
8383
  init_newrelic();
8358
8384
  init_musher();
@@ -9071,8 +9097,8 @@ var init_seat = __esm(() => {
9071
9097
  });
9072
9098
 
9073
9099
  // src/index.ts
9074
- import { existsSync as existsSync36, realpathSync as realpathSync3 } from "fs";
9075
- import { homedir as homedir10, platform as platform3 } from "os";
9100
+ import { existsSync as existsSync37, realpathSync as realpathSync3 } from "fs";
9101
+ import { homedir as homedir11, platform as platform3 } from "os";
9076
9102
  import { fileURLToPath } from "url";
9077
9103
 
9078
9104
  // ../../packages/shared/src/index.ts
@@ -9124,6 +9150,9 @@ init_stability();
9124
9150
  init_batchSize();
9125
9151
  init_report();
9126
9152
  init_series();
9153
+ init_shipping();
9154
+ init_doraBands();
9155
+ init_trends();
9127
9156
  init_sentry();
9128
9157
  init_newrelic();
9129
9158
  init_musher();
@@ -11793,7 +11822,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
11793
11822
  name: flags.name,
11794
11823
  os: host.os ?? process.platform,
11795
11824
  runner: flags.runner,
11796
- version: host.version ?? "0.1.64" ?? "dev",
11825
+ version: host.version ?? "0.1.66" ?? "dev",
11797
11826
  running: live.length,
11798
11827
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
11799
11828
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -18611,7 +18640,7 @@ async function fetchVersion(o) {
18611
18640
  const platform = o.platform ?? process.platform;
18612
18641
  const arch = o.arch ?? process.arch;
18613
18642
  const publicKey = o.publicKey ?? RELEASE_PUBLIC_KEY;
18614
- const running = o.running ?? "0.1.64";
18643
+ const running = o.running ?? "0.1.66";
18615
18644
  if (!publicKey)
18616
18645
  return { ok: false, why: "no release key yet" };
18617
18646
  if (!VERSION_RE2.test(o.version))
@@ -19837,7 +19866,7 @@ function engineUpdater(o) {
19837
19866
 
19838
19867
  // src/version.ts
19839
19868
  function cliVersion(env) {
19840
- return "0.1.64";
19869
+ return "0.1.66";
19841
19870
  }
19842
19871
 
19843
19872
  // src/commands/update.ts
@@ -20223,7 +20252,7 @@ function engineClient(o, org, breaker) {
20223
20252
  ...src,
20224
20253
  ...breaker ? { fetchFn: breaker.fetch } : {},
20225
20254
  timeoutMs: ENGINE_TIMEOUT_MS,
20226
- version: "0.1.64"
20255
+ version: "0.1.66"
20227
20256
  });
20228
20257
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
20229
20258
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -20529,7 +20558,7 @@ async function driveEngine(o, flags, deps, until = {}) {
20529
20558
  runner: deps.runner.name,
20530
20559
  atOnce: flags.atOnce,
20531
20560
  interval: flags.interval,
20532
- cliVersion: "0.1.64"
20561
+ cliVersion: "0.1.66"
20533
20562
  };
20534
20563
  const beat = (checkedAt) => {
20535
20564
  const m = deps.machine;
@@ -22398,7 +22427,7 @@ function engineClient2(o, org, breaker) {
22398
22427
  ...src,
22399
22428
  ...breaker ? { fetchFn: breaker.fetch } : {},
22400
22429
  timeoutMs: ENGINE_TIMEOUT_MS2,
22401
- version: "0.1.64"
22430
+ version: "0.1.66"
22402
22431
  });
22403
22432
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
22404
22433
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -22739,7 +22768,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
22739
22768
  runner: deps.runner.name,
22740
22769
  atOnce: flags.atOnce,
22741
22770
  interval: flags.interval,
22742
- cliVersion: "0.1.64"
22771
+ cliVersion: "0.1.66"
22743
22772
  };
22744
22773
  const beat = (checkedAt) => {
22745
22774
  const m = deps.machine;
@@ -24850,7 +24879,7 @@ function contextFor(deps) {
24850
24879
  ...src,
24851
24880
  fetchFn: deps.fetchFn,
24852
24881
  timeoutMs: deps.timeoutMs,
24853
- version: "0.1.64"
24882
+ version: "0.1.66"
24854
24883
  }), deps.home, now) : null;
24855
24884
  return {
24856
24885
  channel: "code",
@@ -28367,9 +28396,1098 @@ var sessionEvent = async (o) => {
28367
28396
  return 0;
28368
28397
  };
28369
28398
 
28399
+ // src/commands/sleuth.ts
28400
+ import { homedir as homedir8 } from "node:os";
28401
+ import { join as join35, resolve as resolve12 } from "node:path";
28402
+
28403
+ // src/sleuth/export.ts
28404
+ import { existsSync as existsSync31, mkdirSync as mkdirSync28, readFileSync as readFileSync32, renameSync as renameSync13, writeFileSync as writeFileSync28 } from "node:fs";
28405
+ import { dirname as dirname25, join as join34 } from "node:path";
28406
+
28407
+ // src/sleuth/graphql.ts
28408
+ var SLEUTH_URL = "https://app.sleuth.io/graphql";
28409
+
28410
+ class SleuthError extends Error {
28411
+ adaptable;
28412
+ constructor(message, adaptable) {
28413
+ super(message);
28414
+ this.adaptable = adaptable;
28415
+ }
28416
+ }
28417
+ var RATE = /rate.?limit|throttl|too many requests/i;
28418
+ var backoffMs = (attempt, retryAfter) => {
28419
+ const s = Number(retryAfter);
28420
+ if (retryAfter && Number.isFinite(s) && s >= 0)
28421
+ return Math.min(s, 300) * 1000;
28422
+ return Math.min(60000, 1000 * 2 ** attempt);
28423
+ };
28424
+ var sleuthClient = (o) => {
28425
+ const url = o.url ?? SLEUTH_URL;
28426
+ const fetchFn = o.fetch ?? ((u, init) => fetch(u, init));
28427
+ const sleep = o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
28428
+ const retries = o.retries ?? 6;
28429
+ const timeoutMs = o.timeoutMs ?? 120000;
28430
+ let scheme = "apikey";
28431
+ let settled = false;
28432
+ let count = 0;
28433
+ const request = async (query) => {
28434
+ let lastWhy = "";
28435
+ for (let attempt = 0;attempt <= retries; attempt++) {
28436
+ let res;
28437
+ count++;
28438
+ try {
28439
+ res = await fetchFn(url, {
28440
+ method: "POST",
28441
+ headers: { "content-type": "application/json", authorization: `${scheme} ${o.key}` },
28442
+ body: JSON.stringify({ query }),
28443
+ signal: AbortSignal.timeout(timeoutMs)
28444
+ });
28445
+ } catch (e) {
28446
+ lastWhy = e instanceof Error ? e.message : String(e);
28447
+ o.log?.(`sleuth: ${lastWhy} — retrying`);
28448
+ await sleep(backoffMs(attempt, null));
28449
+ continue;
28450
+ }
28451
+ if ((res.status === 401 || res.status === 403) && !settled) {
28452
+ settled = true;
28453
+ scheme = "Bearer";
28454
+ attempt--;
28455
+ continue;
28456
+ }
28457
+ if (res.status === 401 || res.status === 403)
28458
+ throw new SleuthError(`Sleuth refused the key (HTTP ${res.status}) — check SLEUTH_API_KEY`, false);
28459
+ if (res.status === 429 || res.status >= 500) {
28460
+ lastWhy = `HTTP ${res.status}`;
28461
+ const wait = backoffMs(attempt, res.headers.get("retry-after"));
28462
+ o.log?.(`sleuth: ${lastWhy} — waiting ${Math.round(wait / 1000)}s`);
28463
+ await sleep(wait);
28464
+ continue;
28465
+ }
28466
+ const text = await res.text();
28467
+ let body;
28468
+ try {
28469
+ body = JSON.parse(text);
28470
+ } catch {
28471
+ throw new SleuthError(`Sleuth answered HTTP ${res.status} with no JSON`, res.status < 500);
28472
+ }
28473
+ const errors = Array.isArray(body.errors) ? body.errors : [];
28474
+ if (res.status >= 400 && !errors.length)
28475
+ throw new SleuthError(`Sleuth answered HTTP ${res.status}`, false);
28476
+ settled = true;
28477
+ if (!body.data && errors.some((e) => RATE.test(e.message))) {
28478
+ lastWhy = errors[0]?.message ?? "rate limited";
28479
+ await sleep(backoffMs(attempt, null));
28480
+ continue;
28481
+ }
28482
+ return { data: body.data ?? null, errors };
28483
+ }
28484
+ throw new SleuthError(`Sleuth did not answer after ${retries + 1} tries (${lastWhy})`, true);
28485
+ };
28486
+ return { request, calls: () => count };
28487
+ };
28488
+
28489
+ // src/sleuth/schema.ts
28490
+ var MAX_QUERY_DEPTH = 10;
28491
+ var TYPE_REF = "kind name ofType { kind name ofType { kind name ofType { kind name } } }";
28492
+ var INTROSPECTION_QUERY = `query SledsSleuthIntrospection { __schema { queryType { name } types { kind name fields(includeDeprecated: false) { name args { name defaultValue type { ${TYPE_REF} } } type { ${TYPE_REF} } } inputFields { name defaultValue type { ${TYPE_REF} } } } } }`;
28493
+ var queryDepth = (query) => {
28494
+ let braces = 0;
28495
+ let parens = 0;
28496
+ let inString = false;
28497
+ let max = 0;
28498
+ for (let i = 0;i < query.length; i++) {
28499
+ const c = query[i];
28500
+ if (inString) {
28501
+ if (c === "\\")
28502
+ i++;
28503
+ else if (c === '"')
28504
+ inString = false;
28505
+ } else if (c === '"')
28506
+ inString = true;
28507
+ else if (c === "(")
28508
+ parens++;
28509
+ else if (c === ")")
28510
+ parens--;
28511
+ else if (parens > 0)
28512
+ continue;
28513
+ else if (c === "{")
28514
+ braces++;
28515
+ else if (c === "}")
28516
+ braces--;
28517
+ else if (braces > max && /[A-Za-z_]/.test(c))
28518
+ max = braces;
28519
+ }
28520
+ return max;
28521
+ };
28522
+ var indexSchema = (intro) => ({
28523
+ query: intro.__schema.queryType.name,
28524
+ types: new Map(intro.__schema.types.map((t) => [t.name, t]))
28525
+ });
28526
+ var unwrap = (t) => {
28527
+ let cur = t;
28528
+ let list = false;
28529
+ const required = t.kind === "NON_NULL";
28530
+ while (cur && (cur.kind === "NON_NULL" || cur.kind === "LIST")) {
28531
+ if (cur.kind === "LIST")
28532
+ list = true;
28533
+ cur = cur.ofType;
28534
+ }
28535
+ return { name: cur?.name ?? "", list, required };
28536
+ };
28537
+ var kindOf = (s, name) => s.types.get(name)?.kind ?? "";
28538
+ var isLeaf = (s, name) => ["SCALAR", "ENUM"].includes(kindOf(s, name));
28539
+ var isComposite = (s, name) => ["OBJECT", "INTERFACE"].includes(kindOf(s, name));
28540
+ var isRequired = (a) => a.type.kind === "NON_NULL" && (a.defaultValue === null || a.defaultValue === undefined);
28541
+ var fieldOf2 = (s, type, name) => s.types.get(type)?.fields?.find((f) => f.name === name);
28542
+ var ORG = /^(org|orgSlug|organization|organizationSlug|orgId)$/i;
28543
+ var START = /^(start|startDate|startTime|startAt|from|fromDate|dateFrom|since|begin|after)$/i;
28544
+ var END = /^(end|endDate|endTime|endAt|to|toDate|dateTo|until|before)$/i;
28545
+ var CURSOR = /^(after|cursor|nextCursor|pageCursor|pageToken)$/i;
28546
+ var PAGE = /^(page|pageNumber|pageNum)$/i;
28547
+ var SIZE = /^(first|pageSize|limit|perPage|per_page|size|count|pageLimit)$/i;
28548
+ var OFFSET = /^(offset|skip)$/i;
28549
+ var DATEISH = /date|time/i;
28550
+ var dateArg = (path, typeName) => ({
28551
+ path,
28552
+ dateOnly: /^date$/i.test(typeName)
28553
+ });
28554
+ var planArgs = (s, args) => {
28555
+ const plan = { org: [], missing: [] };
28556
+ const visit = (vals, prefix, depth) => {
28557
+ for (const a of vals) {
28558
+ const u = unwrap(a.type);
28559
+ const path = [...prefix, a.name];
28560
+ const leaf = isLeaf(s, u.name);
28561
+ const dated = leaf && (DATEISH.test(u.name) || u.name === "String" && DATEISH.test(a.name) && (START.test(a.name) || END.test(a.name)));
28562
+ if (kindOf(s, u.name) === "INPUT_OBJECT") {
28563
+ const before = JSON.stringify(plan);
28564
+ const inputs = s.types.get(u.name)?.inputFields ?? [];
28565
+ if (depth < 3)
28566
+ visit(inputs, path, depth + 1);
28567
+ if (isRequired(a) && JSON.stringify(plan) === before && inputs.some(isRequired))
28568
+ plan.missing.push(path.join("."));
28569
+ continue;
28570
+ }
28571
+ if (leaf && !u.list && ORG.test(a.name))
28572
+ plan.org.push(path);
28573
+ else if (dated && START.test(a.name) && !plan.start)
28574
+ plan.start = dateArg(path, u.name);
28575
+ else if (dated && END.test(a.name) && !plan.end)
28576
+ plan.end = dateArg(path, u.name);
28577
+ else if (leaf && !dated && CURSOR.test(a.name) && !plan.cursor)
28578
+ plan.cursor = path;
28579
+ else if (leaf && PAGE.test(a.name) && !plan.page)
28580
+ plan.page = { path, first: Number(a.defaultValue ?? 1) || 0 };
28581
+ else if (leaf && SIZE.test(a.name) && !plan.size)
28582
+ plan.size = path;
28583
+ else if (leaf && OFFSET.test(a.name) && !plan.offset)
28584
+ plan.offset = path;
28585
+ else if (isRequired(a))
28586
+ plan.missing.push(path.join("."));
28587
+ }
28588
+ };
28589
+ visit(args, [], 0);
28590
+ return plan;
28591
+ };
28592
+ var windowed = (p) => !!(p.start && p.end);
28593
+ var pageable = (p) => !!(p.cursor || p.page || p.offset);
28594
+ var iso3 = (d, dateOnly) => dateOnly ? d.toISOString().slice(0, 10) : d.toISOString().replace(/\.\d{3}Z$/, "Z");
28595
+ var gqlValue = (v) => {
28596
+ if (typeof v === "string")
28597
+ return JSON.stringify(v);
28598
+ if (typeof v === "number" || typeof v === "boolean")
28599
+ return String(v);
28600
+ if (Array.isArray(v))
28601
+ return `[${v.map(gqlValue).join(", ")}]`;
28602
+ if (v && typeof v === "object")
28603
+ return `{${Object.entries(v).map(([k, x]) => `${k}: ${gqlValue(x)}`).join(", ")}}`;
28604
+ return "null";
28605
+ };
28606
+ var renderArgs = (p, v) => {
28607
+ const root = {};
28608
+ const put = (path, value) => {
28609
+ if (!path || value === undefined)
28610
+ return;
28611
+ let o = root;
28612
+ for (const k of path.slice(0, -1)) {
28613
+ if (!o[k])
28614
+ o[k] = {};
28615
+ o = o[k];
28616
+ }
28617
+ o[path[path.length - 1]] = value;
28618
+ };
28619
+ for (const path of p.org)
28620
+ put(path, v.org);
28621
+ if (p.start && v.start)
28622
+ put(p.start.path, iso3(v.start, p.start.dateOnly));
28623
+ if (p.end && v.end)
28624
+ put(p.end.path, iso3(v.end, p.end.dateOnly));
28625
+ put(p.cursor, v.cursor);
28626
+ put(p.page?.path, v.page);
28627
+ put(p.size, v.size);
28628
+ put(p.offset, v.offset);
28629
+ const body = gqlValue(root);
28630
+ return body === "{}" ? "" : `(${body.slice(1, -1)})`;
28631
+ };
28632
+ var ITEMS = ["objects", "nodes", "items", "results", "edges", "data", "entries", "records"];
28633
+ var HAS_NEXT = /^(hasNextPage|hasNext|hasMore|more)$/;
28634
+ var NEXT = /^(endCursor|nextCursor|cursor|next|nextPageCursor|nextPage)$/;
28635
+ var TOTAL = /^(totalCount|total|count|totalObjects|totalItems)$/;
28636
+ var shapeOf = (s, f) => {
28637
+ const u = unwrap(f.type);
28638
+ if (!isComposite(s, u.name))
28639
+ return;
28640
+ if (u.list)
28641
+ return { kind: "list", item: u.name };
28642
+ const t = s.types.get(u.name);
28643
+ const lists = (t?.fields ?? []).filter((x) => {
28644
+ const xu = unwrap(x.type);
28645
+ return xu.list && isComposite(s, xu.name) && !x.args.some(isRequired);
28646
+ });
28647
+ const rank = (n) => ITEMS.includes(n) ? ITEMS.indexOf(n) : ITEMS.length;
28648
+ const itemsField = [...lists].sort((a, b) => rank(a.name) - rank(b.name))[0];
28649
+ const scalars = (t?.fields ?? []).filter((x) => isLeaf(s, unwrap(x.type).name));
28650
+ const info = (t?.fields ?? []).find((x) => /^pageInfo$/i.test(x.name) && isComposite(s, unwrap(x.type).name));
28651
+ const infoScalars = info ? (s.types.get(unwrap(info.type).name)?.fields ?? []).filter((x) => isLeaf(s, unwrap(x.type).name)) : [];
28652
+ const find = (re) => {
28653
+ const own = scalars.find((x) => re.test(x.name));
28654
+ if (own)
28655
+ return [own.name];
28656
+ const inInfo = infoScalars.find((x) => re.test(x.name));
28657
+ return inInfo && info ? [info.name, inInfo.name] : undefined;
28658
+ };
28659
+ const hasNext = find(HAS_NEXT);
28660
+ const next = find(NEXT);
28661
+ const total = find(TOTAL);
28662
+ const looksLikePage = itemsField && (ITEMS.includes(itemsField.name) || hasNext || next || total || info);
28663
+ if (!itemsField || !looksLikePage)
28664
+ return { kind: "object", item: u.name };
28665
+ let item = unwrap(itemsField.type).name;
28666
+ let node;
28667
+ if (itemsField.name === "edges") {
28668
+ const n = fieldOf2(s, item, "node");
28669
+ if (n && isComposite(s, unwrap(n.type).name)) {
28670
+ node = "node";
28671
+ item = unwrap(n.type).name;
28672
+ }
28673
+ }
28674
+ return {
28675
+ kind: "page",
28676
+ item,
28677
+ items: itemsField.name,
28678
+ ...node ? { node } : {},
28679
+ ...hasNext ? { hasNext } : {},
28680
+ ...next ? { next } : {},
28681
+ ...total ? { total } : {}
28682
+ };
28683
+ };
28684
+ var wrapShape = (shape, inner) => {
28685
+ if (shape.kind !== "page")
28686
+ return inner;
28687
+ const paths = [shape.hasNext, shape.next, shape.total].filter((p) => !!p);
28688
+ const own = paths.filter((p) => p.length === 1).map((p) => p[0]);
28689
+ const info = paths.filter((p) => p.length === 2);
28690
+ const infoName = info[0]?.[0];
28691
+ const infoSel = infoName ? ` ${infoName} { ${[...new Set(info.map((p) => p[1]))].join(" ")} }` : "";
28692
+ const items = shape.node ? `${shape.items} { ${shape.node} ${inner} }` : `${shape.items} ${inner}`;
28693
+ return `{ ${items}${own.length ? ` ${[...new Set(own)].join(" ")}` : ""}${infoSel} }`;
28694
+ };
28695
+ var at = (v, path) => {
28696
+ if (!path)
28697
+ return;
28698
+ let cur = v;
28699
+ for (const k of path)
28700
+ cur = cur && typeof cur === "object" ? cur[k] : undefined;
28701
+ return cur;
28702
+ };
28703
+ var readShape = (shape, v) => {
28704
+ const objs = (xs) => Array.isArray(xs) ? xs.filter((x) => !!x && typeof x === "object") : [];
28705
+ if (v === null || v === undefined)
28706
+ return { items: [], info: {} };
28707
+ if (shape.kind === "object")
28708
+ return { items: objs([v]), info: {} };
28709
+ if (shape.kind === "list")
28710
+ return { items: objs(v), info: {} };
28711
+ const raw = objs(at(v, [shape.items]));
28712
+ const items = shape.node ? objs(raw.map((e) => e[shape.node])) : raw;
28713
+ const hasNext = at(v, shape.hasNext);
28714
+ const next = at(v, shape.next);
28715
+ const total = at(v, shape.total);
28716
+ return {
28717
+ items,
28718
+ info: {
28719
+ ...typeof hasNext === "boolean" ? { hasNext } : {},
28720
+ ...typeof next === "string" && next ? { next } : {},
28721
+ ...typeof next === "number" ? { next: String(next) } : {},
28722
+ ...typeof total === "number" ? { total } : {}
28723
+ }
28724
+ };
28725
+ };
28726
+ var IDENTITY = ["id", "uid", "slug", "key", "name"];
28727
+ var buildSelection = (s, type, depth, exclude = new Set, above = []) => {
28728
+ const t = s.types.get(type);
28729
+ if (!t?.fields)
28730
+ return null;
28731
+ const parts = [];
28732
+ for (const f of t.fields) {
28733
+ if (f.name.startsWith("__") || exclude.has(`${type}.${f.name}`))
28734
+ continue;
28735
+ if (f.args.some(isRequired))
28736
+ continue;
28737
+ const u = unwrap(f.type);
28738
+ if (isLeaf(s, u.name)) {
28739
+ parts.push(f.name);
28740
+ continue;
28741
+ }
28742
+ if (!isComposite(s, u.name))
28743
+ continue;
28744
+ const shape = shapeOf(s, f);
28745
+ if (pageable(planArgs(s, f.args)) || shape?.kind === "page")
28746
+ continue;
28747
+ const sub = depth > 0 && ![...above, type].includes(u.name) ? buildSelection(s, u.name, depth - 1, exclude, [...above, type]) : identitySelection(s, u.name, exclude);
28748
+ if (sub)
28749
+ parts.push(`${f.name} ${sub}`);
28750
+ }
28751
+ return parts.length ? `{ ${parts.join(" ")} }` : null;
28752
+ };
28753
+ var identitySelection = (s, type, exclude = new Set) => {
28754
+ const fields = (s.types.get(type)?.fields ?? []).filter((f) => IDENTITY.includes(f.name) && !exclude.has(`${type}.${f.name}`) && !f.args.some(isRequired) && isLeaf(s, unwrap(f.type).name));
28755
+ return fields.length ? `{ ${fields.map((f) => f.name).join(" ")} }` : null;
28756
+ };
28757
+ var typeFieldAt = (s, path) => {
28758
+ let type = s.query;
28759
+ let last;
28760
+ for (const seg of path) {
28761
+ if (typeof seg === "number")
28762
+ continue;
28763
+ const f = fieldOf2(s, type, seg);
28764
+ if (!f)
28765
+ return;
28766
+ last = `${type}.${seg}`;
28767
+ type = unwrap(f.type).name;
28768
+ }
28769
+ return last;
28770
+ };
28771
+ var findSection = (s, patterns, maxSteps = 4) => {
28772
+ for (const re of patterns) {
28773
+ let frontier = [{ type: s.query, steps: [] }];
28774
+ for (let d = 0;d < maxSteps && frontier.length; d++) {
28775
+ const next = [];
28776
+ for (const { type, steps } of frontier) {
28777
+ for (const f of s.types.get(type)?.fields ?? []) {
28778
+ const shape = shapeOf(s, f);
28779
+ if (!shape)
28780
+ continue;
28781
+ const args = planArgs(s, f.args);
28782
+ if (args.missing.length)
28783
+ continue;
28784
+ if (d === 0 && !args.org.length)
28785
+ continue;
28786
+ const step = { field: f, parent: type, args, shape };
28787
+ if (re.test(f.name) && shape.kind !== "object")
28788
+ return [...steps, step];
28789
+ if (shape.kind === "page" || pageable(args) || args.start || args.end)
28790
+ continue;
28791
+ if (steps.some((x) => x.shape.item === shape.item) || shape.item === s.query)
28792
+ continue;
28793
+ next.push({ type: shape.item, steps: [...steps, step] });
28794
+ }
28795
+ }
28796
+ frontier = next;
28797
+ }
28798
+ }
28799
+ return;
28800
+ };
28801
+ var structural = (steps) => {
28802
+ const out = new Set;
28803
+ for (const st of steps) {
28804
+ out.add(`${st.parent}.${st.field.name}`);
28805
+ if (st.shape.kind === "page") {
28806
+ const pageType = unwrap(st.field.type).name;
28807
+ out.add(`${pageType}.${st.shape.items}`);
28808
+ }
28809
+ }
28810
+ return out;
28811
+ };
28812
+
28813
+ // src/sleuth/export.ts
28814
+ var SECTIONS = [
28815
+ { name: "deploys", patterns: [/^orgChanges$/, /^changes$/, /^deploys$/], byMonth: true },
28816
+ { name: "projects", patterns: [/^projects$/] },
28817
+ { name: "deployments", patterns: [/^deployments$/, /^codeDeployments$/] },
28818
+ { name: "environments", patterns: [/^environments$/] },
28819
+ { name: "teams", patterns: [/^teams$/] },
28820
+ { name: "labels", patterns: [/^labels$/] },
28821
+ { name: "impactSources", patterns: [/^impactSources$/] },
28822
+ { name: "incidents", patterns: [/^incidents$/, /^orgIncidents$/, /incidents$/i], byMonth: true },
28823
+ {
28824
+ name: "flagEvents",
28825
+ patterns: [
28826
+ /^(flagEvents|featureFlagEvents|flagChanges|featureFlagChanges)$/,
28827
+ /flag\w*(event|change)s$/i
28828
+ ],
28829
+ byMonth: true
28830
+ }
28831
+ ];
28832
+ var DAY = 86400000;
28833
+ var MIN_PAGE = 10;
28834
+ var MAX_PAGES = 5000;
28835
+ var MONTH_BUDGET = 8;
28836
+ var MAX_FAILED_MONTHS = 3;
28837
+ var NESTED_SIZE = 500;
28838
+ var MAX_WARNINGS = 200;
28839
+ var DATE_KEYS = [
28840
+ "date",
28841
+ "deployedAt",
28842
+ "deployDate",
28843
+ "createdAt",
28844
+ "startedAt",
28845
+ "occurredAt",
28846
+ "timestamp",
28847
+ "time",
28848
+ "created"
28849
+ ];
28850
+ var MONTH = /^(\d{4})-(0[1-9]|1[0-2])$/;
28851
+ var monthsBetween = (since, until) => {
28852
+ const a = MONTH.exec(since);
28853
+ const b = MONTH.exec(until);
28854
+ if (!a || !b)
28855
+ return [];
28856
+ const out = [];
28857
+ let y = Number(a[1]);
28858
+ let m = Number(a[2]);
28859
+ const endKey = Number(b[1]) * 12 + Number(b[2]);
28860
+ while (y * 12 + m <= endKey) {
28861
+ out.push(`${y}-${String(m).padStart(2, "0")}`);
28862
+ m++;
28863
+ if (m > 12) {
28864
+ m = 1;
28865
+ y++;
28866
+ }
28867
+ }
28868
+ return out;
28869
+ };
28870
+ var monthWindow = (month) => {
28871
+ const [y, m] = month.split("-").map(Number);
28872
+ return { start: new Date(Date.UTC(y, m - 1, 1)), end: new Date(Date.UTC(y, m, 1)) };
28873
+ };
28874
+ var monthOf = (d) => d.toISOString().slice(0, 7);
28875
+ var keyOf2 = (item) => {
28876
+ for (const k of ["id", "uid"]) {
28877
+ const v = item[k];
28878
+ if (typeof v === "string" || typeof v === "number")
28879
+ return `${k}:${v}`;
28880
+ }
28881
+ if (typeof item.slug === "string")
28882
+ return `slug:${JSON.stringify(item._parents ?? {})}:${item.slug}`;
28883
+ return `json:${JSON.stringify(item)}`;
28884
+ };
28885
+ var itemMonth = (item) => {
28886
+ for (const k of DATE_KEYS) {
28887
+ const v = item[k];
28888
+ if (typeof v !== "string" && typeof v !== "number")
28889
+ continue;
28890
+ const d = new Date(v);
28891
+ if (!Number.isNaN(d.getTime()))
28892
+ return monthOf(d);
28893
+ }
28894
+ return;
28895
+ };
28896
+ var IDENTITY2 = ["id", "uid", "slug", "key", "name"];
28897
+ var identity = (item) => Object.fromEntries(IDENTITY2.filter((k) => item[k] !== undefined).map((k) => [k, item[k]]));
28898
+ var writeAtomic = (path, body) => {
28899
+ mkdirSync28(dirname25(path), { recursive: true });
28900
+ const tmp = `${path}.partial`;
28901
+ writeFileSync28(tmp, body);
28902
+ renameSync13(tmp, path);
28903
+ };
28904
+ var jsonl = (items) => items.length ? `${items.map((i) => JSON.stringify(i)).join(`
28905
+ `)}
28906
+ ` : "";
28907
+ var readJsonl = (path) => readFileSync32(path, "utf8").split(`
28908
+ `).filter((l) => l.trim()).map((l) => JSON.parse(l));
28909
+ var CANNOT_QUERY = /Cannot query field "(\w+)" on type "(\w+)"/;
28910
+ var TOO_DEEP = /\bmax(imum)?\b.{0,24}\bdepth\b|\btoo deep\b/i;
28911
+ var exportSleuth = async (o) => {
28912
+ const log = o.log ?? (() => {});
28913
+ const now = (o.now ?? (() => new Date))();
28914
+ const currentMonth = monthOf(now);
28915
+ const exclude = new Set;
28916
+ const warnings = new Set;
28917
+ const warn = (line) => {
28918
+ if (warnings.size < MAX_WARNINGS)
28919
+ warnings.add(line);
28920
+ };
28921
+ const manifest = {
28922
+ v: 1,
28923
+ tool: "sleds sleuth export",
28924
+ ...o.cliVersion ? { cliVersion: o.cliVersion } : {},
28925
+ org: o.org,
28926
+ endpoint: o.endpoint,
28927
+ startedAt: now.toISOString(),
28928
+ since: o.since,
28929
+ until: o.until,
28930
+ depth: o.depth,
28931
+ pageSize: o.pageSize,
28932
+ schema: { file: "schema.json", types: 0 },
28933
+ sections: {},
28934
+ pruned: [],
28935
+ warnings: [],
28936
+ mismatches: [],
28937
+ requests: 0,
28938
+ complete: false
28939
+ };
28940
+ const save = () => {
28941
+ manifest.pruned = [...exclude].sort();
28942
+ manifest.warnings = [...warnings];
28943
+ manifest.requests = o.client.calls();
28944
+ writeAtomic(join34(o.out, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
28945
+ `);
28946
+ };
28947
+ mkdirSync28(o.out, { recursive: true });
28948
+ log("reading Sleuth's schema");
28949
+ const intro = await o.client.request(INTROSPECTION_QUERY);
28950
+ if (!intro.data?.__schema)
28951
+ throw new SleuthError(`Sleuth did not answer the schema query${intro.errors[0] ? ` — ${intro.errors[0].message}` : ""}`, false);
28952
+ writeAtomic(join34(o.out, "schema.json"), `${JSON.stringify(intro.data, null, 2)}
28953
+ `);
28954
+ const schema = indexSchema(intro.data);
28955
+ manifest.schema.types = schema.types.size;
28956
+ const fitDepth = new Map;
28957
+ const ask = async (build, steps) => {
28958
+ const keep = structural(steps);
28959
+ const root = steps[0]?.field.name ?? "";
28960
+ const path = steps.map((s) => s.field.name).join(".");
28961
+ let depth = fitDepth.get(path) ?? o.depth;
28962
+ for (let i = 0;i < 25; i++) {
28963
+ const r = await o.client.request(build(depth));
28964
+ if (r.data && !r.errors.length)
28965
+ return r.data;
28966
+ const tooDeep = r.errors.some((e) => TOO_DEEP.test(e.message));
28967
+ if (!r.data && tooDeep && depth > 0) {
28968
+ depth--;
28969
+ fitDepth.set(path, depth);
28970
+ log(`sleuth said the query was too deep — reading nested objects ${depth} deep`);
28971
+ continue;
28972
+ }
28973
+ const prune = new Set;
28974
+ for (const e of r.errors) {
28975
+ const m = CANNOT_QUERY.exec(e.message);
28976
+ const tf = e.path && typeFieldAt(schema, e.path) || (m ? `${m[2]}.${m[1]}` : undefined);
28977
+ if (tf && !keep.has(tf) && !exclude.has(tf))
28978
+ prune.add(tf);
28979
+ }
28980
+ const empty = !r.data || r.data[root] === null || r.data[root] === undefined;
28981
+ if (empty && prune.size) {
28982
+ for (const tf of prune) {
28983
+ exclude.add(tf);
28984
+ log(`sleuth refused ${tf} — leaving it out`);
28985
+ }
28986
+ continue;
28987
+ }
28988
+ const why = r.errors.slice(0, 3).map((e) => e.message).join("; ");
28989
+ if (empty)
28990
+ throw new SleuthError(why || "Sleuth answered with no data", !tooDeep);
28991
+ for (const e of r.errors) {
28992
+ const tf = e.path ? typeFieldAt(schema, e.path) : undefined;
28993
+ warn(`${tf ? `${tf}: ` : ""}${e.message}`);
28994
+ }
28995
+ return r.data;
28996
+ }
28997
+ throw new SleuthError("Sleuth kept refusing fields after 25 tries", false);
28998
+ };
28999
+ const buildQuery = (steps, leafValues, depth) => {
29000
+ const last = steps.length - 1;
29001
+ const argsAt = (i) => {
29002
+ const st = steps[i];
29003
+ if (i === last)
29004
+ return renderArgs(st.args, leafValues);
29005
+ return renderArgs(st.args, { org: o.org, ...st.args.size ? { size: NESTED_SIZE } : {} });
29006
+ };
29007
+ const leaf = steps[last];
29008
+ const render = (d) => {
29009
+ const sel = buildSelection(schema, leaf.shape.item, d, exclude) ?? "{ __typename }";
29010
+ let inner = `${leaf.field.name}${argsAt(last)} ${wrapShape(leaf.shape, sel)}`;
29011
+ for (let i = last - 1;i >= 0; i--) {
29012
+ const st = steps[i];
29013
+ const ident = identitySelection(schema, st.shape.item, exclude);
29014
+ const body = `{ ${ident ? `${ident.slice(2, -2)} ` : ""}${inner} }`;
29015
+ inner = `${st.field.name}${argsAt(i)} ${wrapShape(st.shape, body)}`;
29016
+ }
29017
+ return `query { ${inner} }`;
29018
+ };
29019
+ let d = depth;
29020
+ let q = render(d);
29021
+ while (d > 0 && queryDepth(q) > MAX_QUERY_DEPTH)
29022
+ q = render(--d);
29023
+ if (d < o.depth)
29024
+ warn(`${leaf.field.name}: nested objects read ${d} deep, not ${o.depth} — Sleuth's max depth`);
29025
+ return q;
29026
+ };
29027
+ const extract = (steps, data) => {
29028
+ let level = [
29029
+ { value: data[steps[0]?.field.name ?? ""], parents: {} }
29030
+ ];
29031
+ const items = [];
29032
+ const infos = [];
29033
+ steps.forEach((st, i) => {
29034
+ const next = [];
29035
+ for (const { value, parents } of level) {
29036
+ const r = readShape(st.shape, value);
29037
+ if (i === steps.length - 1) {
29038
+ infos.push(r.info);
29039
+ for (const it of r.items)
29040
+ items.push(Object.keys(parents).length ? { ...it, _parents: parents } : it);
29041
+ continue;
29042
+ }
29043
+ const child = steps[i + 1]?.field.name ?? "";
29044
+ for (const it of r.items)
29045
+ next.push({ value: it[child], parents: { ...parents, [st.shape.item]: identity(it) } });
29046
+ }
29047
+ level = next;
29048
+ });
29049
+ return { items, infos };
29050
+ };
29051
+ const fetchItems = async (steps, win, size) => {
29052
+ const leaf = steps[steps.length - 1];
29053
+ const a = leaf.args;
29054
+ const oneParent = steps.slice(0, -1).every((s) => s.shape.kind === "object");
29055
+ const canPage = oneParent && pageable(a);
29056
+ const out = [];
29057
+ let cursor;
29058
+ let page = a.page?.first ?? 1;
29059
+ let offset = 0;
29060
+ let prevFirst;
29061
+ let total;
29062
+ for (let n = 0;n < MAX_PAGES; n++) {
29063
+ const values = {
29064
+ org: o.org,
29065
+ ...win ? { start: win.start, end: win.end } : {},
29066
+ ...a.size ? { size: canPage ? size : Math.max(size, NESTED_SIZE) } : {},
29067
+ ...canPage && a.cursor && cursor ? { cursor } : {},
29068
+ ...canPage && a.page ? { page } : {},
29069
+ ...canPage && !a.cursor && !a.page && a.offset ? { offset } : {}
29070
+ };
29071
+ const data = await ask((depth) => buildQuery(steps, values, depth), steps);
29072
+ const { items, infos } = extract(steps, data);
29073
+ if (!canPage) {
29074
+ const full = oneParent && a.size !== undefined && items.length >= (values.size ?? 0);
29075
+ const truncated = infos.filter((i) => i.hasNext === true).length || (full ? 1 : 0);
29076
+ return {
29077
+ items,
29078
+ truncated,
29079
+ ...oneParent && infos[0]?.total !== undefined ? { total: infos[0].total } : {}
29080
+ };
29081
+ }
29082
+ const info = infos[0] ?? {};
29083
+ if (n === 0 && info.total !== undefined)
29084
+ total = info.total;
29085
+ if (!items.length)
29086
+ break;
29087
+ const first = keyOf2(items[0]);
29088
+ if (first === prevFirst) {
29089
+ warn(`${leaf.field.name}: the next page repeated the last one — paging stopped there`);
29090
+ break;
29091
+ }
29092
+ prevFirst = first;
29093
+ out.push(...items);
29094
+ if (info.hasNext === false)
29095
+ break;
29096
+ if (a.cursor) {
29097
+ if (!info.next || info.next === cursor)
29098
+ break;
29099
+ cursor = info.next;
29100
+ continue;
29101
+ }
29102
+ if (info.hasNext === undefined && a.size && items.length < size)
29103
+ break;
29104
+ if (a.page)
29105
+ page = info.next && /^\d+$/.test(info.next) ? Number(info.next) : page + 1;
29106
+ else
29107
+ offset += items.length;
29108
+ }
29109
+ return { items: out, truncated: 0, ...total !== undefined ? { total } : {} };
29110
+ };
29111
+ const adaptive = async (steps, win, size, budget) => {
29112
+ try {
29113
+ return await fetchItems(steps, win, size);
29114
+ } catch (e) {
29115
+ if (!(e instanceof SleuthError) || !e.adaptable || budget.left <= 0)
29116
+ throw e;
29117
+ budget.left--;
29118
+ const leaf = steps[steps.length - 1];
29119
+ if (leaf.args.size && size > MIN_PAGE) {
29120
+ const half = Math.max(MIN_PAGE, Math.floor(size / 2));
29121
+ log(` ${e.message} — trying pages of ${half}`);
29122
+ return adaptive(steps, win, half, budget);
29123
+ }
29124
+ const days = win ? Math.floor((win.end.getTime() - win.start.getTime()) / DAY) : 0;
29125
+ if (!win || days < 2)
29126
+ throw e;
29127
+ const mid = new Date(win.start.getTime() + Math.floor(days / 2) * DAY);
29128
+ log(` ${e.message} — splitting ${win.start.toISOString().slice(0, 10)}…${win.end.toISOString().slice(0, 10)} in two`);
29129
+ const x = await adaptive(steps, { start: win.start, end: mid }, size, budget);
29130
+ const y = await adaptive(steps, { start: mid, end: win.end }, size, budget);
29131
+ return {
29132
+ items: [...x.items, ...y.items],
29133
+ truncated: x.truncated + y.truncated,
29134
+ ...x.total !== undefined && y.total !== undefined ? { total: x.total + y.total } : {}
29135
+ };
29136
+ }
29137
+ };
29138
+ const months = monthsBetween(o.since, o.until);
29139
+ const runSection = async (sec) => {
29140
+ const steps = findSection(schema, sec.patterns);
29141
+ if (!steps)
29142
+ return { found: false, why: "Sleuth's schema has no field by that name" };
29143
+ const leaf = steps[steps.length - 1];
29144
+ const res = {
29145
+ found: true,
29146
+ path: steps.map((s) => s.field.name),
29147
+ windowed: windowed(leaf.args),
29148
+ paged: pageable(leaf.args),
29149
+ count: 0,
29150
+ duplicates: 0
29151
+ };
29152
+ const seen = new Set;
29153
+ const fresh = (items) => {
29154
+ const kept = [];
29155
+ for (const it of items) {
29156
+ const k = keyOf2(it);
29157
+ if (seen.has(k))
29158
+ res.duplicates++;
29159
+ else {
29160
+ seen.add(k);
29161
+ kept.push(it);
29162
+ }
29163
+ }
29164
+ return kept;
29165
+ };
29166
+ if (sec.byMonth && res.windowed) {
29167
+ res.months = [];
29168
+ let failedInARow = 0;
29169
+ for (const month of months) {
29170
+ const file = `${sec.name}/${month}.jsonl`;
29171
+ const path = join34(o.out, file);
29172
+ if (!o.fresh && month < currentMonth && existsSync31(path)) {
29173
+ const kept = fresh(readJsonl(path));
29174
+ res.months.push({ month, file, count: kept.length, resumed: true });
29175
+ res.count += kept.length;
29176
+ continue;
29177
+ }
29178
+ try {
29179
+ const got = await adaptive(steps, monthWindow(month), o.pageSize, { left: MONTH_BUDGET });
29180
+ const kept = fresh(got.items);
29181
+ writeAtomic(path, jsonl(kept));
29182
+ res.months.push({
29183
+ month,
29184
+ file,
29185
+ count: kept.length,
29186
+ ...got.items.length !== kept.length ? { fetched: got.items.length } : {},
29187
+ ...got.total !== undefined ? { total: got.total } : {}
29188
+ });
29189
+ res.count += kept.length;
29190
+ if (got.truncated)
29191
+ res.truncated = (res.truncated ?? 0) + got.truncated;
29192
+ log(`${sec.name} ${month}: ${kept.length}`);
29193
+ failedInARow = 0;
29194
+ } catch (e) {
29195
+ const why = e instanceof Error ? e.message : String(e);
29196
+ res.months.push({ month, file, count: 0, error: why });
29197
+ log(`${sec.name} ${month}: failed — ${why}`);
29198
+ if (++failedInARow >= MAX_FAILED_MONTHS) {
29199
+ res.error = `${MAX_FAILED_MONTHS} months in a row failed; stopped at ${month} — ${why}`;
29200
+ break;
29201
+ }
29202
+ }
29203
+ }
29204
+ return res;
29205
+ }
29206
+ const got = await adaptive(steps, undefined, o.pageSize, { left: MONTH_BUDGET });
29207
+ const kept = fresh(got.items);
29208
+ res.count = kept.length;
29209
+ if (got.truncated)
29210
+ res.truncated = got.truncated;
29211
+ if (!sec.byMonth) {
29212
+ res.file = `${sec.name}.jsonl`;
29213
+ writeAtomic(join34(o.out, res.file), jsonl(kept));
29214
+ log(`${sec.name}: ${kept.length}`);
29215
+ return res;
29216
+ }
29217
+ const byMonth = new Map;
29218
+ for (const it of kept) {
29219
+ const m = itemMonth(it) ?? "undated";
29220
+ byMonth.set(m, [...byMonth.get(m) ?? [], it]);
29221
+ }
29222
+ res.months = [...byMonth.keys()].sort().map((month) => {
29223
+ const file = `${sec.name}/${month}.jsonl`;
29224
+ const list = byMonth.get(month) ?? [];
29225
+ writeAtomic(join34(o.out, file), jsonl(list));
29226
+ return { month, file, count: list.length };
29227
+ });
29228
+ log(`${sec.name}: ${kept.length} (not windowed by Sleuth; filed by each item's date)`);
29229
+ return res;
29230
+ };
29231
+ const orgRoot = (schema.types.get(schema.query)?.fields ?? []).find((f) => {
29232
+ const sh = shapeOf(schema, f);
29233
+ const args = planArgs(schema, f.args);
29234
+ return /^(organization|org)$/i.test(f.name) && sh?.kind === "object" && args.org.length && !args.missing.length;
29235
+ });
29236
+ if (orgRoot) {
29237
+ const steps = [
29238
+ {
29239
+ field: orgRoot,
29240
+ parent: schema.query,
29241
+ args: planArgs(schema, orgRoot.args),
29242
+ shape: { kind: "object", item: unwrap(orgRoot.type).name }
29243
+ }
29244
+ ];
29245
+ try {
29246
+ const sel = buildSelection(schema, unwrap(orgRoot.type).name, 0, exclude) ?? "{ __typename }";
29247
+ const data = await ask(() => `query { ${orgRoot.name}${renderArgs(steps[0]?.args ?? { org: [], missing: [] }, { org: o.org })} ${sel} }`, steps);
29248
+ writeAtomic(join34(o.out, "organization.json"), `${JSON.stringify(data[orgRoot.name], null, 2)}
29249
+ `);
29250
+ manifest.organization = { file: "organization.json" };
29251
+ } catch (e) {
29252
+ manifest.organization = { error: e instanceof Error ? e.message : String(e) };
29253
+ }
29254
+ } else
29255
+ manifest.organization = { error: "Sleuth's schema has no organization(orgSlug) field" };
29256
+ save();
29257
+ for (const sec of SECTIONS) {
29258
+ if (o.only?.length && !o.only.includes(sec.name))
29259
+ continue;
29260
+ log(`— ${sec.name}`);
29261
+ try {
29262
+ manifest.sections[sec.name] = await runSection(sec);
29263
+ } catch (e) {
29264
+ manifest.sections[sec.name] = {
29265
+ found: true,
29266
+ path: [],
29267
+ windowed: false,
29268
+ paged: false,
29269
+ count: 0,
29270
+ duplicates: 0,
29271
+ error: e instanceof Error ? e.message : String(e)
29272
+ };
29273
+ log(`${sec.name}: failed — ${e instanceof Error ? e.message : String(e)}`);
29274
+ }
29275
+ save();
29276
+ }
29277
+ const deploys = manifest.sections.deploys;
29278
+ const recap = orgRoot ? fieldOf2(schema, unwrap(orgRoot.type).name, "metricsRecap") : undefined;
29279
+ const recapArgs = recap ? planArgs(schema, recap.args) : undefined;
29280
+ if (orgRoot && recap && recapArgs && windowed(recapArgs) && !recapArgs.missing.length && fieldOf2(schema, unwrap(recap.type).name, "numOfDeploys") && deploys?.found && deploys.months) {
29281
+ const orgArgs = renderArgs(planArgs(schema, orgRoot.args), { org: o.org });
29282
+ for (const row of deploys.months) {
29283
+ if (row.error || row.month === "undated")
29284
+ continue;
29285
+ const w = monthWindow(row.month);
29286
+ try {
29287
+ const r = await o.client.request(`query { ${orgRoot.name}${orgArgs} { ${recap.name}${renderArgs(recapArgs, { start: w.start, end: w.end })} { numOfDeploys } } }`);
29288
+ const orgData = r.data?.[orgRoot.name];
29289
+ const n = orgData?.[recap.name]?.numOfDeploys;
29290
+ if (typeof n === "number")
29291
+ row.sleuthReports = n;
29292
+ } catch (e) {
29293
+ warn(`metricsRecap ${row.month}: ${e instanceof Error ? e.message : String(e)}`);
29294
+ }
29295
+ }
29296
+ }
29297
+ for (const [name, sec] of Object.entries(manifest.sections)) {
29298
+ if (!sec.found || !sec.months)
29299
+ continue;
29300
+ for (const row of sec.months) {
29301
+ const fetched = row.fetched ?? row.count;
29302
+ if (row.total !== undefined && row.total !== fetched)
29303
+ manifest.mismatches.push({
29304
+ section: name,
29305
+ month: row.month,
29306
+ exported: fetched,
29307
+ reported: row.total,
29308
+ source: "totalCount"
29309
+ });
29310
+ if (row.sleuthReports !== undefined && row.sleuthReports !== row.count)
29311
+ manifest.mismatches.push({
29312
+ section: name,
29313
+ month: row.month,
29314
+ exported: row.count,
29315
+ reported: row.sleuthReports,
29316
+ source: "metricsRecap"
29317
+ });
29318
+ }
29319
+ }
29320
+ manifest.finishedAt = new Date().toISOString();
29321
+ manifest.complete = !!orgRoot && Object.values(manifest.sections).every((s) => !s.found || !s.error && !(s.months ?? []).some((m) => m.error));
29322
+ save();
29323
+ return manifest;
29324
+ };
29325
+
29326
+ // src/commands/sleuth.ts
29327
+ var SLEUTH_USAGE = `usage: sleds sleuth export --org <slug> --out <dir> [options]
29328
+
29329
+ Archive a Sleuth.io org through its GraphQL API: the schema, the organization, every deploy
29330
+ (orgChanges) as JSONL by month, and its projects, deployments, environments, teams, labels,
29331
+ impact sources, incidents and flag events, with manifest.json counting each beside what
29332
+ Sleuth reports. Re-run to resume: finished past months on disk are not fetched again.
29333
+
29334
+ SLEUTH_API_KEY the org's API key or access token (Sleuth › Settings › API)
29335
+ --org <slug> the Sleuth org slug (required)
29336
+ --out <dir> where to write (required; created if missing)
29337
+ --since <YYYY-MM> first month (default 2022-01)
29338
+ --until <YYYY-MM> last month (default this month)
29339
+ --only <a,b> just these: ${SECTIONS.map((s) => s.name).join(", ")}
29340
+ --page-size <n> items a page (default 100; halved on its own when Sleuth struggles)
29341
+ --depth <n> how deep nested objects are read (default 2)
29342
+ --fresh fetch every month again, even the ones on disk
29343
+ --url <endpoint> the GraphQL endpoint (default ${SLEUTH_URL}; else SLEUTH_API_URL)
29344
+ --json print the manifest instead of the summary`;
29345
+ var parse3 = (argv) => {
29346
+ const words = [];
29347
+ const flags = {};
29348
+ for (let i = 0;i < argv.length; i++) {
29349
+ const a = argv[i];
29350
+ if (!a.startsWith("--")) {
29351
+ words.push(a);
29352
+ continue;
29353
+ }
29354
+ const eq = a.indexOf("=");
29355
+ if (eq !== -1) {
29356
+ flags[a.slice(2, eq)] = a.slice(eq + 1);
29357
+ continue;
29358
+ }
29359
+ const next = argv[i + 1];
29360
+ if (next !== undefined && !next.startsWith("--")) {
29361
+ flags[a.slice(2)] = next;
29362
+ i++;
29363
+ } else
29364
+ flags[a.slice(2)] = true;
29365
+ }
29366
+ return { words, flags };
29367
+ };
29368
+ var outDir = (raw, home) => resolve12(raw.startsWith("~/") ? join35(home, raw.slice(2)) : raw);
29369
+ var str15 = (f, k) => typeof f[k] === "string" ? f[k] : undefined;
29370
+ var MONTH2 = /^\d{4}-(0[1-9]|1[0-2])$/;
29371
+ var KNOWN = [
29372
+ "org",
29373
+ "out",
29374
+ "since",
29375
+ "until",
29376
+ "only",
29377
+ "page-size",
29378
+ "depth",
29379
+ "fresh",
29380
+ "url",
29381
+ "json"
29382
+ ];
29383
+ var sleuthCmd = async (o) => {
29384
+ const log = o.log ?? console.log;
29385
+ const error = o.error ?? console.error;
29386
+ const env = o.env ?? process.env;
29387
+ const { words, flags } = parse3(o.argv);
29388
+ const usage = (why) => {
29389
+ if (why)
29390
+ error(why);
29391
+ error(SLEUTH_USAGE);
29392
+ return 2;
29393
+ };
29394
+ if (words[0] !== "export" || words.length !== 1)
29395
+ return usage();
29396
+ const unknown = Object.keys(flags).filter((k) => !KNOWN.includes(k));
29397
+ if (unknown.length)
29398
+ return usage(`unknown option: --${unknown[0]}`);
29399
+ const org = str15(flags, "org");
29400
+ const outRaw = str15(flags, "out");
29401
+ if (!org || !outRaw)
29402
+ return usage("--org and --out are required");
29403
+ const key = env.SLEUTH_API_KEY?.trim();
29404
+ if (!key) {
29405
+ error("SLEUTH_API_KEY is not set — the org's key from Sleuth › Settings › API");
29406
+ return 2;
29407
+ }
29408
+ const now = (o.now ?? (() => new Date))();
29409
+ const since = str15(flags, "since") ?? "2022-01";
29410
+ const until = str15(flags, "until") ?? monthOf(now);
29411
+ if (!MONTH2.test(since) || !MONTH2.test(until) || !monthsBetween(since, until).length)
29412
+ return usage("--since and --until are months, YYYY-MM, since before until");
29413
+ const pageSize = Number(str15(flags, "page-size") ?? 100);
29414
+ const depth = Number(str15(flags, "depth") ?? 2);
29415
+ if (!Number.isInteger(pageSize) || pageSize < 1)
29416
+ return usage("--page-size is a whole number");
29417
+ if (!Number.isInteger(depth) || depth < 0 || depth > 5)
29418
+ return usage("--depth is 0 to 5");
29419
+ const only = str15(flags, "only")?.split(",").map((s) => s.trim()).filter(Boolean);
29420
+ const names = SECTIONS.map((s) => s.name);
29421
+ const bad = only?.find((n) => !names.includes(n));
29422
+ if (bad)
29423
+ return usage(`--only: no section "${bad}" (${names.join(", ")})`);
29424
+ const out = outDir(outRaw, o.home ?? homedir8());
29425
+ const endpoint = str15(flags, "url") ?? env.SLEUTH_API_URL ?? SLEUTH_URL;
29426
+ const version = cliVersion(env);
29427
+ const progress = error;
29428
+ const client = sleuthClient({
29429
+ key,
29430
+ url: endpoint,
29431
+ ...o.fetch ? { fetch: o.fetch } : {},
29432
+ ...o.sleep ? { sleep: o.sleep } : {},
29433
+ log: progress
29434
+ });
29435
+ let manifest;
29436
+ try {
29437
+ manifest = await exportSleuth({
29438
+ org,
29439
+ out,
29440
+ since,
29441
+ until,
29442
+ pageSize,
29443
+ depth,
29444
+ ...only?.length ? { only } : {},
29445
+ fresh: flags.fresh === true,
29446
+ endpoint,
29447
+ ...version ? { cliVersion: version } : {},
29448
+ client,
29449
+ now: () => now,
29450
+ log: progress
29451
+ });
29452
+ } catch (e) {
29453
+ error(`sleuth export stopped — ${e instanceof Error ? e.message : String(e)}`);
29454
+ return e instanceof SleuthError && !e.adaptable ? 2 : 1;
29455
+ }
29456
+ if (flags.json === true) {
29457
+ log(JSON.stringify(manifest));
29458
+ return manifest.complete ? 0 : 1;
29459
+ }
29460
+ log(`Sleuth org ${org} → ${out}`);
29461
+ for (const [name, s] of Object.entries(manifest.sections)) {
29462
+ if (!s.found)
29463
+ log(` ${name.padEnd(14)} not in Sleuth's schema — skipped`);
29464
+ else {
29465
+ const months = s.months ? ` across ${s.months.length} months` : "";
29466
+ const dupes = s.duplicates ? `, ${s.duplicates} duplicates dropped` : "";
29467
+ const more = s.truncated ? `, ${s.truncated} nested lists cut at one page` : "";
29468
+ log(` ${name.padEnd(14)} ${String(s.count).padStart(6)}${months}${dupes}${more}${s.error ? ` — FAILED: ${s.error}` : ""}`);
29469
+ }
29470
+ }
29471
+ if (manifest.pruned.length)
29472
+ log(` Sleuth refused ${manifest.pruned.length} fields: ${manifest.pruned.join(", ")}`);
29473
+ for (const m of manifest.mismatches)
29474
+ log(` ${m.section} ${m.month}: exported ${m.exported}, Sleuth's ${m.source} says ${m.reported}`);
29475
+ const deploys = manifest.sections.deploys;
29476
+ if (deploys?.found) {
29477
+ const checked = (deploys.months ?? []).filter((r) => r.sleuthReports !== undefined || r.total !== undefined).length;
29478
+ const off = manifest.mismatches.filter((m) => m.section === "deploys").length;
29479
+ if (!checked)
29480
+ log(" Sleuth gave no deploy count to check against");
29481
+ else if (!off)
29482
+ log(` deploy counts match what Sleuth reports in all ${checked} months checked`);
29483
+ }
29484
+ log(` ${manifest.requests} requests · manifest.json${manifest.complete ? "" : " — INCOMPLETE: re-run to resume"}`);
29485
+ return manifest.complete ? 0 : 1;
29486
+ };
29487
+
28370
29488
  // src/commands/specEvent.ts
28371
29489
  init_meter();
28372
- import { readFileSync as readFileSync32, statSync as statSync8 } from "node:fs";
29490
+ import { readFileSync as readFileSync33, statSync as statSync8 } from "node:fs";
28373
29491
  import { basename as basename9, isAbsolute as isAbsolute7, relative as relative2 } from "node:path";
28374
29492
  var HOOK_STDIN_TIMEOUT_MS3 = 1000;
28375
29493
  var ESTIMATE_TIMEOUT_MS = 4000;
@@ -28425,7 +29543,7 @@ function readFileOrNull(path) {
28425
29543
  try {
28426
29544
  if (statSync8(path).size > 2000000)
28427
29545
  return null;
28428
- return readFileSync32(path, "utf8");
29546
+ return readFileSync33(path, "utf8");
28429
29547
  } catch {
28430
29548
  return null;
28431
29549
  }
@@ -28489,7 +29607,7 @@ var specEvent = async (o) => {
28489
29607
  };
28490
29608
 
28491
29609
  // src/commands/status.ts
28492
- import { existsSync as existsSync31 } from "node:fs";
29610
+ import { existsSync as existsSync32 } from "node:fs";
28493
29611
  init_config();
28494
29612
  init_settings();
28495
29613
  var HEALTH_TIMEOUT_MS2 = 4000;
@@ -28577,7 +29695,7 @@ var status3 = async (o) => {
28577
29695
  const log = o.log ?? console.log;
28578
29696
  const nowMs = (o.now ?? Date.now)();
28579
29697
  const cfg = readConfig(o.home);
28580
- const settings = existsSync31(settingsPath(o.home)) ? readSettings(o.home) : {};
29698
+ const settings = existsSync32(settingsPath(o.home)) ? readSettings(o.home) : {};
28581
29699
  const inSettings = readProxy(settings);
28582
29700
  const picked = selectOrg(cfg, undefined, o.env ?? process.env);
28583
29701
  const viaEnv = picked.from === "env";
@@ -28770,7 +29888,7 @@ var announce = (o, events) => {
28770
29888
 
28771
29889
  // src/commands/stopEvent.ts
28772
29890
  init_meter();
28773
- import { existsSync as existsSync32, readFileSync as readFileSync33 } from "node:fs";
29891
+ import { existsSync as existsSync33, readFileSync as readFileSync34 } from "node:fs";
28774
29892
 
28775
29893
  // src/transcript.ts
28776
29894
  var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
@@ -28871,7 +29989,7 @@ async function postTranscript(o, payload, now) {
28871
29989
  }
28872
29990
  if (!src)
28873
29991
  return false;
28874
- const read = o.readFile ?? ((p) => existsSync32(p) ? readFileSync33(p, "utf8") : null);
29992
+ const read = o.readFile ?? ((p) => existsSync33(p) ? readFileSync34(p, "utf8") : null);
28875
29993
  let text;
28876
29994
  try {
28877
29995
  text = read(path);
@@ -28949,7 +30067,7 @@ var stopEvent = async (o) => {
28949
30067
 
28950
30068
  // src/commands/uninstall.ts
28951
30069
  import { spawnSync as spawnSync9 } from "node:child_process";
28952
- import { existsSync as existsSync33, rmSync as rmSync22 } from "node:fs";
30070
+ import { existsSync as existsSync34, rmSync as rmSync22 } from "node:fs";
28953
30071
  init_config();
28954
30072
  init_meter();
28955
30073
  init_settings();
@@ -28962,7 +30080,7 @@ var uninstall2 = (o) => {
28962
30080
  (o.log ?? console.log)(` ${codexLine}`);
28963
30081
  const log = o.log ?? console.log;
28964
30082
  const removed = [];
28965
- if (existsSync33(settingsPath(o.home))) {
30083
+ if (existsSync34(settingsPath(o.home))) {
28966
30084
  let settings = readSettings(o.home);
28967
30085
  let changed = false;
28968
30086
  const cfg = readConfig(o.home);
@@ -29015,18 +30133,18 @@ var uninstall2 = (o) => {
29015
30133
  writeSettings(o.home, settings);
29016
30134
  }
29017
30135
  for (const file of [meterCachePath(o.home), ...allSeatStatePaths(o.home)]) {
29018
- if (existsSync33(file)) {
30136
+ if (existsSync34(file)) {
29019
30137
  rmSync22(file, { force: true });
29020
30138
  removed.push(file);
29021
30139
  }
29022
30140
  }
29023
30141
  const skill = skillDir(o.home);
29024
- if (existsSync33(skill)) {
30142
+ if (existsSync34(skill)) {
29025
30143
  rmSync22(skill, { recursive: true, force: true });
29026
30144
  removed.push(skill);
29027
30145
  }
29028
30146
  const plist = musherPlistPath(o.home);
29029
- if (existsSync33(plist)) {
30147
+ if (existsSync34(plist)) {
29030
30148
  (o.unloadAgent ?? defaultUnload)(plist);
29031
30149
  rmSync22(plist, { force: true });
29032
30150
  removed.push(`launchd agent ${musherLabel()}`);
@@ -29044,8 +30162,8 @@ var uninstall2 = (o) => {
29044
30162
 
29045
30163
  // src/commands/update.ts
29046
30164
  init_server();
29047
- import { existsSync as existsSync34 } from "node:fs";
29048
- import { homedir as homedir8, userInfo as userInfo4 } from "node:os";
30165
+ import { existsSync as existsSync35 } from "node:fs";
30166
+ import { homedir as homedir9, userInfo as userInfo4 } from "node:os";
29049
30167
  import { basename as basename10 } from "node:path";
29050
30168
  init_config();
29051
30169
  init_state();
@@ -29111,7 +30229,7 @@ var realPidAlive4 = (pid) => {
29111
30229
  };
29112
30230
  function machineClient(home, env) {
29113
30231
  const src = meterSource(home, undefined, { env });
29114
- return src ? fetchClient({ ...src, version: "0.1.64" }) : null;
30232
+ return src ? fetchClient({ ...src, version: "0.1.66" }) : null;
29115
30233
  }
29116
30234
  async function noteAnswer2(home, a, now) {
29117
30235
  try {
@@ -29133,7 +30251,7 @@ function busyLine(busy) {
29133
30251
  return `${n} session${n === 1 ? " is" : "s are"} running on ${on} — sleds update --now releases ${n === 1 ? "it" : "them"}`;
29134
30252
  }
29135
30253
  async function updateCmd(o) {
29136
- const home = o.home ?? homedir8();
30254
+ const home = o.home ?? homedir9();
29137
30255
  const env = o.env ?? process.env;
29138
30256
  const log = o.log ?? console.log;
29139
30257
  const error = o.error ?? console.error;
@@ -29259,7 +30377,7 @@ async function updateCmd(o) {
29259
30377
  error(`sleds ${to} could not be downloaded: ${got.why}`);
29260
30378
  return 1;
29261
30379
  }
29262
- } else if (!existsSync34(versionBin(home, to, platform))) {
30380
+ } else if (!existsSync35(versionBin(home, to, platform))) {
29263
30381
  error(`sleds ${to} is not on this machine to move back to`);
29264
30382
  return 1;
29265
30383
  }
@@ -29353,7 +30471,7 @@ var verify2 = async (o) => {
29353
30471
 
29354
30472
  // src/commands/webhooks.ts
29355
30473
  var USAGE3 = "usage: sleds webhooks [--json] | sleds webhooks deliveries <id or name> [--json]";
29356
- var str15 = (x) => typeof x === "string" ? x : "";
30474
+ var str16 = (x) => typeof x === "string" ? x : "";
29357
30475
  function ago3(at, now) {
29358
30476
  const m = Math.max(0, Math.floor((now - at) / 60000));
29359
30477
  if (m < 1)
@@ -29369,7 +30487,7 @@ function webhookLines(raw, now) {
29369
30487
  if (!rows.length)
29370
30488
  return ["no webhooks — add one in the app: Settings › Integrations › Webhooks"];
29371
30489
  return rows.map((h) => {
29372
- let host = str15(h.url);
30490
+ let host = str16(h.url);
29373
30491
  try {
29374
30492
  host = new URL(host.replace(/\{[a-z.]+\}/g, "x")).host;
29375
30493
  } catch {}
@@ -29377,21 +30495,21 @@ function webhookLines(raw, now) {
29377
30495
  const last = h.lastDelivery;
29378
30496
  const lastText = last?.at ? `last ${last.status ?? "—"} ${last.state ?? ""} ${ago3(last.at, now)}`.replace(/\s+/g, " ") : "nothing sent yet";
29379
30497
  const off = h.enabled ? "on " : "off";
29380
- const why = !h.enabled && h.disabledWhy ? ` (${str15(h.disabledWhy)})` : "";
29381
- return `${off} ${str15(h.name)} ${host} ${events.includes("*") ? "all events" : events.join(",")} ${lastText}${why} [${str15(h.id)}]`;
30498
+ const why = !h.enabled && h.disabledWhy ? ` (${str16(h.disabledWhy)})` : "";
30499
+ return `${off} ${str16(h.name)} ${host} ${events.includes("*") ? "all events" : events.join(",")} ${lastText}${why} [${str16(h.id)}]`;
29382
30500
  });
29383
30501
  }
29384
30502
  function deliveryLines(raw, now) {
29385
30503
  const j = clean(raw);
29386
30504
  const rows = Array.isArray(j.deliveries) ? j.deliveries : [];
29387
- const name = str15(j.webhook?.name);
30505
+ const name = str16(j.webhook?.name);
29388
30506
  if (!rows.length)
29389
30507
  return [`${name}: nothing sent yet`];
29390
30508
  return [
29391
30509
  `${name} — the last ${rows.length}`,
29392
30510
  ...rows.map((d) => {
29393
- const result = d.status !== null && d.status !== undefined ? String(d.status) : str15(d.error) || "—";
29394
- return `${str15(d.state).padEnd(9)} ${result.padEnd(5)} ${str15(d.event).padEnd(20)} ${str15(d.key) || "—"} ${ago3(Number(d.at), now)} tries ${d.attempts} ${str15(d.deliveryId)}`;
30511
+ const result = d.status !== null && d.status !== undefined ? String(d.status) : str16(d.error) || "—";
30512
+ return `${str16(d.state).padEnd(9)} ${result.padEnd(5)} ${str16(d.event).padEnd(20)} ${str16(d.key) || "—"} ${ago3(Number(d.at), now)} tries ${d.attempts} ${str16(d.deliveryId)}`;
29395
30513
  })
29396
30514
  ];
29397
30515
  }
@@ -29422,7 +30540,7 @@ var webhooksCmd = async (o) => {
29422
30540
  return 1;
29423
30541
  }
29424
30542
  if (r.status !== 200 || !r.json) {
29425
- const why = str15(r.json?.error) || `HTTP ${r.status}`;
30543
+ const why = str16(r.json?.error) || `HTTP ${r.status}`;
29426
30544
  error(r.status === 404 && !r.json ? "SLEDS does not know this seat — run `sleds init`" : why);
29427
30545
  return 1;
29428
30546
  }
@@ -29436,13 +30554,13 @@ var webhooksCmd = async (o) => {
29436
30554
 
29437
30555
  // src/config.ts
29438
30556
  init_settings();
29439
- import { chmodSync as chmodSync8, existsSync as existsSync35, mkdirSync as mkdirSync28, readFileSync as readFileSync34, renameSync as renameSync13, writeFileSync as writeFileSync28 } from "node:fs";
29440
- import { join as join34, resolve as resolve12 } from "node:path";
30557
+ import { chmodSync as chmodSync8, existsSync as existsSync36, mkdirSync as mkdirSync29, readFileSync as readFileSync35, renameSync as renameSync14, writeFileSync as writeFileSync29 } from "node:fs";
30558
+ import { join as join36, resolve as resolve13 } from "node:path";
29441
30559
  var configDir2 = (home, env = process.env) => {
29442
30560
  const o = env.SLEDS_HOME?.trim();
29443
- return o ? resolve12(home, o) : join34(home, ".config", "sleds");
30561
+ return o ? resolve13(home, o) : join36(home, ".config", "sleds");
29444
30562
  };
29445
- var configPath2 = (home) => join34(configDir2(home), "config.json");
30563
+ var configPath2 = (home) => join36(configDir2(home), "config.json");
29446
30564
  var isObject17 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
29447
30565
  var optString2 = (v) => typeof v === "string" ? v : undefined;
29448
30566
  var parsePriorEnv2 = (v) => {
@@ -29577,9 +30695,9 @@ var parseOrgInfo2 = (raw) => {
29577
30695
  };
29578
30696
  var readConfig2 = (home) => {
29579
30697
  const p = configPath2(home);
29580
- if (!existsSync35(p))
30698
+ if (!existsSync36(p))
29581
30699
  return {};
29582
- return parseConfig2(JSON.parse(readFileSync34(p, "utf8")));
30700
+ return parseConfig2(JSON.parse(readFileSync35(p, "utf8")));
29583
30701
  };
29584
30702
 
29585
30703
  // src/runners/exec.ts
@@ -29636,10 +30754,10 @@ function runnerFor3(name, exec = defaultExec, o = {}) {
29636
30754
  }
29637
30755
 
29638
30756
  // src/runtime.ts
29639
- import { homedir as homedir9 } from "node:os";
29640
- import { join as join35, win32 as win323 } from "node:path";
30757
+ import { homedir as homedir10 } from "node:os";
30758
+ import { join as join37, win32 as win323 } from "node:path";
29641
30759
  var isCompiled2 = (argv1 = process.argv[1]) => Boolean(argv1 && (argv1.startsWith("/$bunfs/") || argv1.startsWith("B:\\~BUN\\")));
29642
- var sledsLink2 = (home = homedir9(), platform = process.platform) => platform === "win32" ? win323.join(process.env.LOCALAPPDATA ?? win323.join(home, "AppData", "Local"), "sleds", "bin", "sleds.exe") : join35(home, ".sleds", "bin", "sleds");
30760
+ var sledsLink2 = (home = homedir10(), platform = process.platform) => platform === "win32" ? win323.join(process.env.LOCALAPPDATA ?? win323.join(home, "AppData", "Local"), "sleds", "bin", "sleds.exe") : join37(home, ".sleds", "bin", "sleds");
29643
30761
 
29644
30762
  // src/schedule.ts
29645
30763
  import { spawnSync as spawnSync11 } from "node:child_process";
@@ -29651,15 +30769,15 @@ var defaultExec5 = (cmd, args) => {
29651
30769
  };
29652
30770
 
29653
30771
  // src/settings.ts
29654
- import { dirname as dirname25, join as join36, resolve as resolve13 } from "node:path";
30772
+ import { dirname as dirname26, join as join38, resolve as resolve14 } from "node:path";
29655
30773
  var withSeatEnv2 = (command, home, env = process.env, org) => {
29656
30774
  const parts = [];
29657
30775
  const sh = env.SLEDS_HOME?.trim();
29658
30776
  const cc = env.CLAUDE_CONFIG_DIR?.trim();
29659
30777
  if (sh)
29660
- parts.push(`SLEDS_HOME=${shellWord2(resolve13(home, sh))}`);
30778
+ parts.push(`SLEDS_HOME=${shellWord2(resolve14(home, sh))}`);
29661
30779
  if (cc)
29662
- parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(resolve13(home, cc))}`);
30780
+ parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(resolve14(home, cc))}`);
29663
30781
  parts.push(command);
29664
30782
  return pinOrg4(parts.join(" "), org);
29665
30783
  };
@@ -29745,7 +30863,11 @@ commands:
29745
30863
  --run start Claude Code with the pack as the first prompt
29746
30864
  webhooks The org's outbound webhooks (admins); deliveries <id or name> lists one hook's
29747
30865
  last hundred deliveries. Registering and Redeliver stay in the app.
29748
- deploy A release step's one line: tell SLEDS this commit is now running in an environment.
30866
+ sleuth export --org <slug> --out <dir>: archive a Sleuth.io org before it closes \u2014
30867
+ every deploy as JSONL by month, its projects, environments, teams, incidents and
30868
+ flag events, and a manifest whose counts sit beside Sleuth's own. Key from
30869
+ SLEUTH_API_KEY; re-run to resume. \`sleds sleuth\` alone lists every option.
30870
+ deploy A release step's one line: tell SLEDS this commit is now running in an environment.
29749
30871
  --env <slug> the environment (required); --service <name> for a monorepo
29750
30872
  --sha / --repo else the CI's own variables, then git
29751
30873
  Token from SLEDS_DEPLOY_TOKEN (Settings \u203A Deploys). SLEDS down never fails a release.
@@ -29951,17 +31073,17 @@ var hookStdin = async () => {
29951
31073
  var sledsArgv = (execPath = process.execPath, script = process.argv[1], realpath, o = {}) => {
29952
31074
  if (isCompiled2(script)) {
29953
31075
  const link = sledsLink2(o.home);
29954
- return stableArgv2([(o.exists ?? existsSync36)(link) ? link : execPath], realpath, o.home);
31076
+ return stableArgv2([(o.exists ?? existsSync37)(link) ? link : execPath], realpath, o.home);
29955
31077
  }
29956
31078
  return stableArgv2(script ? [execPath, script] : [execPath], realpath, o.home);
29957
31079
  };
29958
31080
  var shellQuote = (s) => /^[\w./-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
29959
- var statusLineCommand = (argv, home = homedir10()) => withSeatEnv2([...argv, "statusline"].map(shellQuote).join(" "), home);
29960
- var seatEventCommand = (argv, home = homedir10()) => withSeatEnv2([...argv, "seat-event"].map(shellQuote).join(" "), home);
29961
- var promptEventCommand = (argv, home = homedir10()) => withSeatEnv2([...argv, "prompt-event"].map(shellQuote).join(" "), home);
29962
- var sessionEventCommand = (argv, home = homedir10()) => withSeatEnv2([...argv, "session-event"].map(shellQuote).join(" "), home);
29963
- var stopEventCommand = (argv, home = homedir10()) => withSeatEnv2([...argv, "stop-event"].map(shellQuote).join(" "), home);
29964
- var specEventCommand = (argv, home = homedir10()) => withSeatEnv2([...argv, "spec-event"].map(shellQuote).join(" "), home);
31081
+ var statusLineCommand = (argv, home = homedir11()) => withSeatEnv2([...argv, "statusline"].map(shellQuote).join(" "), home);
31082
+ var seatEventCommand = (argv, home = homedir11()) => withSeatEnv2([...argv, "seat-event"].map(shellQuote).join(" "), home);
31083
+ var promptEventCommand = (argv, home = homedir11()) => withSeatEnv2([...argv, "prompt-event"].map(shellQuote).join(" "), home);
31084
+ var sessionEventCommand = (argv, home = homedir11()) => withSeatEnv2([...argv, "session-event"].map(shellQuote).join(" "), home);
31085
+ var stopEventCommand = (argv, home = homedir11()) => withSeatEnv2([...argv, "stop-event"].map(shellQuote).join(" "), home);
31086
+ var specEventCommand = (argv, home = homedir11()) => withSeatEnv2([...argv, "spec-event"].map(shellQuote).join(" "), home);
29965
31087
  var mcpFlag = (flags) => flags["no-mcp"] === true ? false : flags.mcp === true ? true : undefined;
29966
31088
  var main = async (argv) => {
29967
31089
  const { cmd, flags } = parseArgs(argv);
@@ -29969,7 +31091,7 @@ var main = async (argv) => {
29969
31091
  console.log(commandUsage(cmd));
29970
31092
  return 0;
29971
31093
  }
29972
- const home = homedir10();
31094
+ const home = homedir11();
29973
31095
  const scheduler = {
29974
31096
  platform: platform3(),
29975
31097
  home,
@@ -29981,7 +31103,7 @@ var main = async (argv) => {
29981
31103
  now: Date.now(),
29982
31104
  stderr: (line) => console.error(line),
29983
31105
  isTTY: !!process.stderr.isTTY,
29984
- version: "0.1.64"
31106
+ version: "0.1.66"
29985
31107
  });
29986
31108
  switch (cmd) {
29987
31109
  case "update":
@@ -29996,6 +31118,8 @@ var main = async (argv) => {
29996
31118
  return packCmd({ home, argv: argv.slice(1) });
29997
31119
  case "webhooks":
29998
31120
  return webhooksCmd({ home, argv: argv.slice(1) });
31121
+ case "sleuth":
31122
+ return sleuthCmd({ argv: argv.slice(1), home });
29999
31123
  case "deploy":
30000
31124
  return deployCmd({ argv: argv.slice(1) });
30001
31125
  case "sled":
@@ -30214,7 +31338,7 @@ ${renderHelpText(argv[1])}`);
30214
31338
  case "version":
30215
31339
  case "--version":
30216
31340
  case "-v":
30217
- console.log("sleds 0.1.64");
31341
+ console.log("sleds 0.1.66");
30218
31342
  return 0;
30219
31343
  default:
30220
31344
  console.error(`unknown command: ${cmd}