@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.
- package/README.md +20 -0
- package/dist/sleds.mjs +1182 -58
- 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
|
|
9075
|
-
import { homedir as
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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) =>
|
|
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
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
29048
|
-
import { homedir as
|
|
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.
|
|
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 ??
|
|
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 (!
|
|
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
|
|
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 =
|
|
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 ? ` (${
|
|
29381
|
-
return `${off} ${
|
|
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 =
|
|
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) :
|
|
29394
|
-
return `${
|
|
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 =
|
|
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
|
|
29440
|
-
import { join as
|
|
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 ?
|
|
30561
|
+
return o ? resolve13(home, o) : join36(home, ".config", "sleds");
|
|
29444
30562
|
};
|
|
29445
|
-
var configPath2 = (home) =>
|
|
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 (!
|
|
30698
|
+
if (!existsSync36(p))
|
|
29581
30699
|
return {};
|
|
29582
|
-
return parseConfig2(JSON.parse(
|
|
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
|
|
29640
|
-
import { join as
|
|
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 =
|
|
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
|
|
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(
|
|
30778
|
+
parts.push(`SLEDS_HOME=${shellWord2(resolve14(home, sh))}`);
|
|
29661
30779
|
if (cc)
|
|
29662
|
-
parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(
|
|
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
|
-
|
|
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 ??
|
|
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 =
|
|
29960
|
-
var seatEventCommand = (argv, home =
|
|
29961
|
-
var promptEventCommand = (argv, home =
|
|
29962
|
-
var sessionEventCommand = (argv, home =
|
|
29963
|
-
var stopEventCommand = (argv, home =
|
|
29964
|
-
var specEventCommand = (argv, 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 =
|
|
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.
|
|
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.
|
|
31341
|
+
console.log("sleds 0.1.66");
|
|
30218
31342
|
return 0;
|
|
30219
31343
|
default:
|
|
30220
31344
|
console.error(`unknown command: ${cmd}
|