@sleds/cli 0.1.65 → 0.1.67
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 +2 -0
- package/dist/sleds.mjs +289 -78
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -115,6 +115,8 @@ sleuth-export/
|
|
|
115
115
|
|
|
116
116
|
Nested items carry `_parents` (the project and organization they came from). The exporter reads Sleuth's schema and builds every query from it, so a field Sleuth renames or drops is followed, not a crash. Re-run the same command to resume after a stop: finished past months are skipped (`--fresh` fetches them again). `--since`/`--until YYYY-MM`, `--only deploys,projects`, `--page-size`, `--depth`; `sleds sleuth` lists them all. The command exits 1 when a section is incomplete, and the manifest says which.
|
|
117
117
|
|
|
118
|
+
The key goes as `authorization: apikey <key>`; when Sleuth refuses it or answers PERMISSION_DENIED, the export tries `Bearer <key>` once and keeps whichever answers. `SLEUTH_AUTH=bearer` (or `apikey`) sends that scheme alone. A key Sleuth denies under both stops the export at once (exit 2) rather than splitting the month in search of a smaller query.
|
|
119
|
+
|
|
118
120
|
## Docs
|
|
119
121
|
|
|
120
122
|
Repo: github.com/spdyadam/sleds · Site: sleds.ai
|
package/dist/sleds.mjs
CHANGED
|
@@ -1640,10 +1640,26 @@ var init_outcome = __esm(() => {
|
|
|
1640
1640
|
OUTCOME_WINDOW_MS = OUTCOME_WINDOW_DAYS * 86400000;
|
|
1641
1641
|
});
|
|
1642
1642
|
|
|
1643
|
+
// ../../packages/shared/src/deploys/workingHours.ts
|
|
1644
|
+
var MIN_MS, DAY_MS2, WEEK_MS, DAY_MIN, WORKING_HOURS_DEFAULT;
|
|
1645
|
+
var init_workingHours = __esm(() => {
|
|
1646
|
+
MIN_MS = 60 * 1000;
|
|
1647
|
+
DAY_MS2 = 24 * 60 * MIN_MS;
|
|
1648
|
+
WEEK_MS = 7 * DAY_MS2;
|
|
1649
|
+
DAY_MIN = 24 * 60;
|
|
1650
|
+
WORKING_HOURS_DEFAULT = Object.freeze({
|
|
1651
|
+
on: false,
|
|
1652
|
+
days: Object.freeze([1, 2, 3, 4, 5]),
|
|
1653
|
+
start: 9 * 60,
|
|
1654
|
+
end: 17 * 60
|
|
1655
|
+
});
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1643
1658
|
// ../../packages/shared/src/deploys/metrics.ts
|
|
1644
|
-
var
|
|
1659
|
+
var WEEK_MS2;
|
|
1645
1660
|
var init_metrics = __esm(() => {
|
|
1646
|
-
|
|
1661
|
+
init_workingHours();
|
|
1662
|
+
WEEK_MS2 = 7 * 24 * 60 * 60 * 1000;
|
|
1647
1663
|
});
|
|
1648
1664
|
|
|
1649
1665
|
// ../../packages/shared/src/deploys/batchSize.ts
|
|
@@ -1666,9 +1682,9 @@ function periodChange(cur, prev, better) {
|
|
|
1666
1682
|
const dir = pct > 0 ? "up" : "down";
|
|
1667
1683
|
return { pct, dir, tone: dir === better ? "success" : "error" };
|
|
1668
1684
|
}
|
|
1669
|
-
var
|
|
1685
|
+
var DAY_MS3, FLAT_PCT = 0.5;
|
|
1670
1686
|
var init_period = __esm(() => {
|
|
1671
|
-
|
|
1687
|
+
DAY_MS3 = 24 * 60 * 60 * 1000;
|
|
1672
1688
|
});
|
|
1673
1689
|
|
|
1674
1690
|
// ../../packages/shared/src/deploys/report.ts
|
|
@@ -1681,23 +1697,28 @@ function duration(ms) {
|
|
|
1681
1697
|
return `${hours.toFixed(1)}h`;
|
|
1682
1698
|
return `${(hours / 24).toFixed(1)}d`;
|
|
1683
1699
|
}
|
|
1684
|
-
function
|
|
1700
|
+
function changeWord(pair, better) {
|
|
1685
1701
|
const { current, previous } = pair;
|
|
1686
1702
|
if (current == null)
|
|
1687
|
-
return
|
|
1703
|
+
return null;
|
|
1688
1704
|
if (previous == null)
|
|
1689
|
-
return "
|
|
1705
|
+
return "none before";
|
|
1690
1706
|
const c = periodChange(current, previous, better);
|
|
1691
1707
|
if (c.dir === "flat")
|
|
1692
|
-
return "
|
|
1708
|
+
return "flat";
|
|
1693
1709
|
if (c.pct == null)
|
|
1694
|
-
return "
|
|
1695
|
-
return
|
|
1710
|
+
return "none before";
|
|
1711
|
+
return `${c.pct > 0 ? "+" : "−"}${Math.round(Math.abs(c.pct))}%`;
|
|
1696
1712
|
}
|
|
1697
|
-
function
|
|
1713
|
+
function change(pair, better) {
|
|
1714
|
+
const word = changeWord(pair, better);
|
|
1715
|
+
return word == null ? "" : ` (${word})`;
|
|
1716
|
+
}
|
|
1717
|
+
function timeFigure(label, pair, note) {
|
|
1698
1718
|
if (pair.current == null)
|
|
1699
1719
|
return `${label} none`;
|
|
1700
|
-
|
|
1720
|
+
const notes = [note, changeWord(pair, "down")].filter(Boolean).join(", ");
|
|
1721
|
+
return `${label} ${duration(pair.current)}${notes ? ` (${notes})` : ""}`;
|
|
1701
1722
|
}
|
|
1702
1723
|
function rateFigure(pair) {
|
|
1703
1724
|
const { current, previous } = pair;
|
|
@@ -1715,29 +1736,31 @@ function batchFigure(pair) {
|
|
|
1715
1736
|
const before = total(pair.previous) === 0 ? "none before" : `was ${tiers(pair.previous)}`;
|
|
1716
1737
|
return `batch ${tiers(pair.current)} (${before})`;
|
|
1717
1738
|
}
|
|
1718
|
-
function lineText(l) {
|
|
1739
|
+
function lineText(l, clock) {
|
|
1719
1740
|
const head = `${l.name} (${l.slug})`;
|
|
1720
1741
|
if (l.production == null)
|
|
1721
1742
|
return `${head} · no production environment set, so no deploy metrics`;
|
|
1722
1743
|
const deploys = `${l.deploys.current} deploy${l.deploys.current === 1 ? "" : "s"}${change(l.deploys, "up")}`;
|
|
1723
1744
|
return [
|
|
1724
1745
|
`${head} · ${l.production}: ${deploys}`,
|
|
1725
|
-
timeFigure("lead time", l.leadTime),
|
|
1746
|
+
timeFigure("lead time", l.leadTime, clock === "working" ? "working hours" : undefined),
|
|
1726
1747
|
rateFigure(l.failRate),
|
|
1727
1748
|
timeFigure("time to recover", l.recover),
|
|
1728
1749
|
batchFigure(l.batch)
|
|
1729
1750
|
].join(" · ");
|
|
1730
1751
|
}
|
|
1731
1752
|
function metricsText(r) {
|
|
1753
|
+
const clock = r.clock ?? "wall";
|
|
1732
1754
|
const sameYear = r.from.slice(0, 4) === r.to.slice(0, 4);
|
|
1733
1755
|
const span = `${dayLabel(r.from, !sameYear)} – ${dayLabel(r.to, true)}`;
|
|
1734
1756
|
const days = `${r.days} day${r.days === 1 ? "" : "s"}`;
|
|
1735
|
-
const
|
|
1736
|
-
const
|
|
1757
|
+
const window = clock === "working" ? ` · lead time in working hours${r.hours ? `, ${r.hours}` : ""}` : "";
|
|
1758
|
+
const header = `${r.scope === "org" ? "Org" : "Workspace"} · ${span} (${days}), against the ${days} before${window}`;
|
|
1759
|
+
const lines = r.lines.length ? r.lines.map((l) => lineText(l, clock)) : [r.scope === "org" ? "No workspaces in this org." : "No workspace."];
|
|
1737
1760
|
return [header, ...lines].join(`
|
|
1738
1761
|
`);
|
|
1739
1762
|
}
|
|
1740
|
-
var
|
|
1763
|
+
var DAY_MS4, METRICS_RANGES, tiers = (c) => TIERS.map((t) => `${t}${c[t]}`).join(" "), total = (c) => TIERS.reduce((n, t) => n + c[t], 0), dayLabel = (day, year) => new Date(`${day}T00:00:00Z`).toLocaleDateString("en-US", {
|
|
1741
1764
|
month: "short",
|
|
1742
1765
|
day: "numeric",
|
|
1743
1766
|
...year ? { year: "numeric" } : {},
|
|
@@ -1746,7 +1769,7 @@ var DAY_MS3, METRICS_RANGES, tiers = (c) => TIERS.map((t) => `${t}${c[t]}`).join
|
|
|
1746
1769
|
var init_report = __esm(() => {
|
|
1747
1770
|
init_batchSize();
|
|
1748
1771
|
init_period();
|
|
1749
|
-
|
|
1772
|
+
DAY_MS4 = 24 * 60 * 60 * 1000;
|
|
1750
1773
|
METRICS_RANGES = [
|
|
1751
1774
|
{ key: "2w", days: 14 },
|
|
1752
1775
|
{ key: "1m", days: 30 },
|
|
@@ -2241,6 +2264,15 @@ var init_route = __esm(() => {
|
|
|
2241
2264
|
};
|
|
2242
2265
|
});
|
|
2243
2266
|
|
|
2267
|
+
// ../../packages/shared/src/route/verdict.ts
|
|
2268
|
+
function toneOf(x) {
|
|
2269
|
+
return typeof x === "string" && VERDICT_TONES.includes(x) ? x : undefined;
|
|
2270
|
+
}
|
|
2271
|
+
var VERDICT_TONES;
|
|
2272
|
+
var init_verdict = __esm(() => {
|
|
2273
|
+
VERDICT_TONES = ["go", "change", "stop"];
|
|
2274
|
+
});
|
|
2275
|
+
|
|
2244
2276
|
// ../../packages/shared/src/mcp/annotations.ts
|
|
2245
2277
|
function withAnnotations(tools) {
|
|
2246
2278
|
return tools.map((t) => {
|
|
@@ -3698,6 +3730,8 @@ async function runTool(ctx, args) {
|
|
|
3698
3730
|
const answer = a ? {
|
|
3699
3731
|
md: str5(a.md),
|
|
3700
3732
|
cites: strList(a.cites) ?? [],
|
|
3733
|
+
...str5(a.verdict)?.trim() ? { verdict: str5(a.verdict)?.trim() } : {},
|
|
3734
|
+
...toneOf(a.tone) ? { tone: toneOf(a.tone) } : {},
|
|
3701
3735
|
...typeof a.irreversible === "boolean" ? { irreversible: a.irreversible } : {}
|
|
3702
3736
|
} : null;
|
|
3703
3737
|
const r = isObject4(args.release) ? args.release : null;
|
|
@@ -4664,6 +4698,7 @@ var init_server = __esm(() => {
|
|
|
4664
4698
|
init_brief();
|
|
4665
4699
|
init_due();
|
|
4666
4700
|
init_route();
|
|
4701
|
+
init_verdict();
|
|
4667
4702
|
init_annotations();
|
|
4668
4703
|
init_format();
|
|
4669
4704
|
init_plan2();
|
|
@@ -5052,7 +5087,7 @@ var init_server = __esm(() => {
|
|
|
5052
5087
|
},
|
|
5053
5088
|
{
|
|
5054
5089
|
name: "run",
|
|
5055
|
-
description: "An engine's word on a run it holds (spec 26): with nothing but `runId`, a heartbeat that renews the lease (every few minutes while the session runs; a lapsed lease returns the node to ready). `park: {question}` when the session needs a person — the node parks with the question, the seat is free, the engine takes the next node. `fail: {log}` when the session exited without a PR — back to ready once, parked the second time. `watch` updates where to watch it. On a decider's (answerer's) run: `answer: {md, cites}` when the record settles the question, `release: {why, waitsOn, cites}` when the record says the node must wait on other items (its session stops, the node goes back to ready without a seat, and it is not taken again until those land), `escalate: {why, suggested?, forPerson?}` when it does not — `forPerson: {relation: {key, how}}` when the fix is a relation only a person can set (`blockedBy SL-1474`), which the person's card then sets in one press — SLEDS may refuse an answer and escalate it, and says why. When the PR opens: `actual` (or `land`) with `node` and state `pr`; the merge lands the node from the PR body's `SLEDS-Node:` line.",
|
|
5090
|
+
description: "An engine's word on a run it holds (spec 26): with nothing but `runId`, a heartbeat that renews the lease (every few minutes while the session runs; a lapsed lease returns the node to ready). `park: {question}` when the session needs a person — the node parks with the question, the seat is free, the engine takes the next node. `fail: {log}` when the session exited without a PR — back to ready once, parked the second time. `watch` updates where to watch it. On a decider's (answerer's) run: `answer: {verdict, tone, md, cites}` when the record settles the question, `release: {why, waitsOn, cites}` when the record says the node must wait on other items (its session stops, the node goes back to ready without a seat, and it is not taken again until those land), `escalate: {why, suggested?, forPerson?}` when it does not — `forPerson: {relation: {key, how}}` when the fix is a relation only a person can set (`blockedBy SL-1474`), which the person's card then sets in one press — SLEDS may refuse an answer and escalate it, and says why. When the PR opens: `actual` (or `land`) with `node` and state `pr`; the merge lands the node from the PR body's `SLEDS-Node:` line.",
|
|
5056
5091
|
inputSchema: {
|
|
5057
5092
|
type: "object",
|
|
5058
5093
|
properties: {
|
|
@@ -5078,6 +5113,15 @@ var init_server = __esm(() => {
|
|
|
5078
5113
|
description: "A decider's answer, on its own run: the record settles the question.",
|
|
5079
5114
|
properties: {
|
|
5080
5115
|
md: { type: "string", description: "The answer as you would tell the session." },
|
|
5116
|
+
verdict: {
|
|
5117
|
+
type: "string",
|
|
5118
|
+
description: "SL-2172: the verdict on one line, in plain words — md's first line. The card leads with it."
|
|
5119
|
+
},
|
|
5120
|
+
tone: {
|
|
5121
|
+
type: "string",
|
|
5122
|
+
enum: ["go", "change", "stop"],
|
|
5123
|
+
description: "go: go ahead as filed · change: change the plan · stop: stop or hold."
|
|
5124
|
+
},
|
|
5081
5125
|
cites: {
|
|
5082
5126
|
type: "array",
|
|
5083
5127
|
items: { type: "string" },
|
|
@@ -6821,11 +6865,11 @@ var init_deploys = __esm(() => {
|
|
|
6821
6865
|
});
|
|
6822
6866
|
|
|
6823
6867
|
// ../../packages/shared/src/deploys/series.ts
|
|
6824
|
-
var
|
|
6868
|
+
var DAY_MS5, WEEK_MS3;
|
|
6825
6869
|
var init_series = __esm(() => {
|
|
6826
6870
|
init_metrics();
|
|
6827
|
-
|
|
6828
|
-
|
|
6871
|
+
DAY_MS5 = 24 * 60 * 60 * 1000;
|
|
6872
|
+
WEEK_MS3 = 7 * DAY_MS5;
|
|
6829
6873
|
});
|
|
6830
6874
|
|
|
6831
6875
|
// ../../packages/shared/src/deploys/stability.ts
|
|
@@ -6836,27 +6880,40 @@ var init_stability = __esm(() => {
|
|
|
6836
6880
|
});
|
|
6837
6881
|
|
|
6838
6882
|
// ../../packages/shared/src/deploys/shipping.ts
|
|
6839
|
-
var
|
|
6883
|
+
var DAY_MS6;
|
|
6840
6884
|
var init_shipping = __esm(() => {
|
|
6841
|
-
|
|
6885
|
+
DAY_MS6 = 24 * 60 * 60 * 1000;
|
|
6842
6886
|
});
|
|
6843
6887
|
|
|
6844
6888
|
// ../../packages/shared/src/deploys/doraBands.ts
|
|
6845
|
-
var HOUR_MS2,
|
|
6889
|
+
var HOUR_MS2, DAY_MS7, WEEK_MS4, MONTH_DAYS = 30, MONTH_MS;
|
|
6846
6890
|
var init_doraBands = __esm(() => {
|
|
6847
6891
|
HOUR_MS2 = 60 * 60 * 1000;
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
MONTH_MS = MONTH_DAYS *
|
|
6892
|
+
DAY_MS7 = 24 * HOUR_MS2;
|
|
6893
|
+
WEEK_MS4 = 7 * DAY_MS7;
|
|
6894
|
+
MONTH_MS = MONTH_DAYS * DAY_MS7;
|
|
6851
6895
|
});
|
|
6852
6896
|
|
|
6853
6897
|
// ../../packages/shared/src/deploys/trends.ts
|
|
6854
|
-
var
|
|
6898
|
+
var DAY_MS8;
|
|
6855
6899
|
var init_trends = __esm(() => {
|
|
6856
6900
|
init_batchSize();
|
|
6857
6901
|
init_metrics();
|
|
6858
6902
|
init_series();
|
|
6859
|
-
|
|
6903
|
+
DAY_MS8 = 24 * 60 * 60 * 1000;
|
|
6904
|
+
});
|
|
6905
|
+
|
|
6906
|
+
// ../../packages/shared/src/deploys/sleuth.ts
|
|
6907
|
+
var SLEUTH_WORKING_HOURS;
|
|
6908
|
+
var init_sleuth = __esm(() => {
|
|
6909
|
+
init_deploys();
|
|
6910
|
+
init_workingHours();
|
|
6911
|
+
SLEUTH_WORKING_HOURS = Object.freeze({
|
|
6912
|
+
on: true,
|
|
6913
|
+
days: Object.freeze([1, 2, 3, 4, 5]),
|
|
6914
|
+
start: 14 * 60,
|
|
6915
|
+
end: 23 * 60
|
|
6916
|
+
});
|
|
6860
6917
|
});
|
|
6861
6918
|
|
|
6862
6919
|
// ../../packages/shared/src/newrelic/newrelic.ts
|
|
@@ -7489,7 +7546,7 @@ function eventFrame(o) {
|
|
|
7489
7546
|
"",
|
|
7490
7547
|
"## Post one verdict, then stop",
|
|
7491
7548
|
"",
|
|
7492
|
-
`- ${o.answer} Post it with the SLEDS \`${run}\` tool, \`{runId: "${a.runId}", answer: {md, cites}}\` — \`md\` as the session it goes to would read it, \`cites\` the list of pointers.`,
|
|
7549
|
+
`- ${o.answer} Post it with the SLEDS \`${run}\` tool, \`{runId: "${a.runId}", answer: {verdict, tone, md, cites}}\` — \`md\` as the session it goes to would read it, \`cites\` the list of pointers.`,
|
|
7493
7550
|
`- ${o.escalate} Post it with \`${run}\`, \`{runId: "${a.runId}", escalate: {why, suggested?, cites?, irreversible?}}\` — \`why\` in one line for the person (with any action only a person can take), \`suggested\` your best answer as the session would be told it, when you have one. Mark \`irreversible: true\` when it is.`,
|
|
7494
7551
|
...VERDICT_SHAPE,
|
|
7495
7552
|
"- Read what the tool answers: an answer can come back `escalated` with `refused` (irreversible, no cites, or the autonomy is ask). That is SLEDS's call, not a failure; do not post again.",
|
|
@@ -7701,6 +7758,7 @@ var init_engine = __esm(() => {
|
|
|
7701
7758
|
};
|
|
7702
7759
|
VERDICT_SHAPE = [
|
|
7703
7760
|
"- **Verdict first.** The first line of `md` (and of `why`) is the verdict on its own, in plain words: what to do, or what is so. No file paths, line numbers or links in it.",
|
|
7761
|
+
"- On an answer, state that line as `verdict` too, and its `tone`: `go` (go ahead as filed), `change` (change the plan) or `stop` (stop or hold).",
|
|
7704
7762
|
"- Then at most 3 short points, one line each, as a `-` list. Cites go in `cites`, not in the prose.",
|
|
7705
7763
|
"- **One cite per span, no prose in it.** Code is `code:<path>:<line>`, the path from the repo root (`code:apps/web/src/App.tsx:42`); cite each line on its own, the cards merge them by file."
|
|
7706
7764
|
];
|
|
@@ -8379,6 +8437,8 @@ var init_src = __esm(() => {
|
|
|
8379
8437
|
init_shipping();
|
|
8380
8438
|
init_doraBands();
|
|
8381
8439
|
init_trends();
|
|
8440
|
+
init_workingHours();
|
|
8441
|
+
init_sleuth();
|
|
8382
8442
|
init_sentry();
|
|
8383
8443
|
init_newrelic();
|
|
8384
8444
|
init_musher();
|
|
@@ -8390,6 +8450,7 @@ var init_src = __esm(() => {
|
|
|
8390
8450
|
init_say();
|
|
8391
8451
|
init_engine();
|
|
8392
8452
|
init_sources();
|
|
8453
|
+
init_verdict();
|
|
8393
8454
|
init_otlp();
|
|
8394
8455
|
init_session();
|
|
8395
8456
|
init_effort();
|
|
@@ -9153,6 +9214,8 @@ init_series();
|
|
|
9153
9214
|
init_shipping();
|
|
9154
9215
|
init_doraBands();
|
|
9155
9216
|
init_trends();
|
|
9217
|
+
init_workingHours();
|
|
9218
|
+
init_sleuth();
|
|
9156
9219
|
init_sentry();
|
|
9157
9220
|
init_newrelic();
|
|
9158
9221
|
init_musher();
|
|
@@ -9164,6 +9227,7 @@ init_reshape();
|
|
|
9164
9227
|
init_say();
|
|
9165
9228
|
init_engine();
|
|
9166
9229
|
init_sources();
|
|
9230
|
+
init_verdict();
|
|
9167
9231
|
init_otlp();
|
|
9168
9232
|
init_session();
|
|
9169
9233
|
init_effort();
|
|
@@ -11822,7 +11886,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
11822
11886
|
name: flags.name,
|
|
11823
11887
|
os: host.os ?? process.platform,
|
|
11824
11888
|
runner: flags.runner,
|
|
11825
|
-
version: host.version ?? "0.1.
|
|
11889
|
+
version: host.version ?? "0.1.67" ?? "dev",
|
|
11826
11890
|
running: live.length,
|
|
11827
11891
|
items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
|
|
11828
11892
|
...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
|
|
@@ -12938,7 +13002,7 @@ function answerPrompt(o) {
|
|
|
12938
13002
|
"",
|
|
12939
13003
|
"## Post one verdict, then stop",
|
|
12940
13004
|
"",
|
|
12941
|
-
`- Settled by the record: the SLEDS \`${run}\` tool with \`{runId: "${c.runId}", answer: {md, cites}}\` — \`md\` is the answer as you would tell the session, \`cites\` the list of pointers.`,
|
|
13005
|
+
`- Settled by the record: the SLEDS \`${run}\` tool with \`{runId: "${c.runId}", answer: {verdict, tone, md, cites}}\` — \`md\` is the answer as you would tell the session, \`cites\` the list of pointers.`,
|
|
12942
13006
|
`- The record says node ${c.node} must wait for another item to land ("build nothing until #123 merges") and that item is on the board: \`${run}\` with \`{runId: "${c.runId}", release: {why, waitsOn, cites}}\` — \`waitsOn\` the keys of the items it waits on (\`SL-123\`). It frees the seat: node ${c.node}'s session is closed, and the node is retaken fresh once they land. Release rather than answer "stay parked", which only parks it again.`,
|
|
12943
13007
|
`- Not settled, irreversible, or unsure: \`${run}\` with \`{runId: "${c.runId}", escalate: {why, suggested?, cites?, irreversible?}}\` — \`why\` in one line for the person (with any action only a person can take), \`suggested\` your best answer as the session would be told it, when you have one. Mark \`irreversible: true\` when it is, even if the session did not.`,
|
|
12944
13008
|
`- When the fix is a relation on the item only a person can set (node ${c.node} looks like it waits on SL-1474, but the record does not settle it, so it is not a release), escalate with \`forPerson: {relation: {key, how}}\` — \`{key: "SL-1474", how: "blockedBy"}\` — so the person's card sets it in one press. Never tell the session to set it: a session cannot set relations.`,
|
|
@@ -18640,7 +18704,7 @@ async function fetchVersion(o) {
|
|
|
18640
18704
|
const platform = o.platform ?? process.platform;
|
|
18641
18705
|
const arch = o.arch ?? process.arch;
|
|
18642
18706
|
const publicKey = o.publicKey ?? RELEASE_PUBLIC_KEY;
|
|
18643
|
-
const running = o.running ?? "0.1.
|
|
18707
|
+
const running = o.running ?? "0.1.67";
|
|
18644
18708
|
if (!publicKey)
|
|
18645
18709
|
return { ok: false, why: "no release key yet" };
|
|
18646
18710
|
if (!VERSION_RE2.test(o.version))
|
|
@@ -19866,7 +19930,7 @@ function engineUpdater(o) {
|
|
|
19866
19930
|
|
|
19867
19931
|
// src/version.ts
|
|
19868
19932
|
function cliVersion(env) {
|
|
19869
|
-
return "0.1.
|
|
19933
|
+
return "0.1.67";
|
|
19870
19934
|
}
|
|
19871
19935
|
|
|
19872
19936
|
// src/commands/update.ts
|
|
@@ -20252,7 +20316,7 @@ function engineClient(o, org, breaker) {
|
|
|
20252
20316
|
...src,
|
|
20253
20317
|
...breaker ? { fetchFn: breaker.fetch } : {},
|
|
20254
20318
|
timeoutMs: ENGINE_TIMEOUT_MS,
|
|
20255
|
-
version: "0.1.
|
|
20319
|
+
version: "0.1.67"
|
|
20256
20320
|
});
|
|
20257
20321
|
const asked = org?.trim() || env.SLEDS_ORG?.trim();
|
|
20258
20322
|
if (asked && !selectOrg(readConfig(o.home), org, env).seat)
|
|
@@ -20558,7 +20622,7 @@ async function driveEngine(o, flags, deps, until = {}) {
|
|
|
20558
20622
|
runner: deps.runner.name,
|
|
20559
20623
|
atOnce: flags.atOnce,
|
|
20560
20624
|
interval: flags.interval,
|
|
20561
|
-
cliVersion: "0.1.
|
|
20625
|
+
cliVersion: "0.1.67"
|
|
20562
20626
|
};
|
|
20563
20627
|
const beat = (checkedAt) => {
|
|
20564
20628
|
const m = deps.machine;
|
|
@@ -22427,7 +22491,7 @@ function engineClient2(o, org, breaker) {
|
|
|
22427
22491
|
...src,
|
|
22428
22492
|
...breaker ? { fetchFn: breaker.fetch } : {},
|
|
22429
22493
|
timeoutMs: ENGINE_TIMEOUT_MS2,
|
|
22430
|
-
version: "0.1.
|
|
22494
|
+
version: "0.1.67"
|
|
22431
22495
|
});
|
|
22432
22496
|
const asked = org?.trim() || env.SLEDS_ORG?.trim();
|
|
22433
22497
|
if (asked && !selectOrg(readConfig(o.home), org, env).seat)
|
|
@@ -22768,7 +22832,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
|
|
|
22768
22832
|
runner: deps.runner.name,
|
|
22769
22833
|
atOnce: flags.atOnce,
|
|
22770
22834
|
interval: flags.interval,
|
|
22771
|
-
cliVersion: "0.1.
|
|
22835
|
+
cliVersion: "0.1.67"
|
|
22772
22836
|
};
|
|
22773
22837
|
const beat = (checkedAt) => {
|
|
22774
22838
|
const m = deps.machine;
|
|
@@ -24879,7 +24943,7 @@ function contextFor(deps) {
|
|
|
24879
24943
|
...src,
|
|
24880
24944
|
fetchFn: deps.fetchFn,
|
|
24881
24945
|
timeoutMs: deps.timeoutMs,
|
|
24882
|
-
version: "0.1.
|
|
24946
|
+
version: "0.1.67"
|
|
24883
24947
|
}), deps.home, now) : null;
|
|
24884
24948
|
return {
|
|
24885
24949
|
channel: "code",
|
|
@@ -28414,6 +28478,15 @@ class SleuthError extends Error {
|
|
|
28414
28478
|
this.adaptable = adaptable;
|
|
28415
28479
|
}
|
|
28416
28480
|
}
|
|
28481
|
+
|
|
28482
|
+
class SleuthDenied extends SleuthError {
|
|
28483
|
+
constructor(message) {
|
|
28484
|
+
super(message, false);
|
|
28485
|
+
}
|
|
28486
|
+
}
|
|
28487
|
+
var DENIED = /\bPERMISSION_DENIED\b/;
|
|
28488
|
+
var isDenial = (e) => DENIED.test(e.message) || typeof e.extensions?.code === "string" && DENIED.test(e.extensions.code);
|
|
28489
|
+
var deniedOutright = (data, errors) => errors.some(isDenial) && (!data || Object.values(data).every((v) => v === null));
|
|
28417
28490
|
var RATE = /rate.?limit|throttl|too many requests/i;
|
|
28418
28491
|
var backoffMs = (attempt, retryAfter) => {
|
|
28419
28492
|
const s = Number(retryAfter);
|
|
@@ -28427,11 +28500,17 @@ var sleuthClient = (o) => {
|
|
|
28427
28500
|
const sleep = o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
28428
28501
|
const retries = o.retries ?? 6;
|
|
28429
28502
|
const timeoutMs = o.timeoutMs ?? 120000;
|
|
28430
|
-
|
|
28431
|
-
let
|
|
28503
|
+
const forced = o.scheme !== undefined;
|
|
28504
|
+
let scheme = o.scheme ?? "apikey";
|
|
28505
|
+
let settled = forced;
|
|
28506
|
+
let deniedOnce = false;
|
|
28507
|
+
let kept = forced;
|
|
28432
28508
|
let count = 0;
|
|
28509
|
+
const other = (s) => s === "apikey" ? "Bearer" : "apikey";
|
|
28433
28510
|
const request = async (query) => {
|
|
28434
28511
|
let lastWhy = "";
|
|
28512
|
+
const startedAs = scheme;
|
|
28513
|
+
let switched = false;
|
|
28435
28514
|
for (let attempt = 0;attempt <= retries; attempt++) {
|
|
28436
28515
|
let res;
|
|
28437
28516
|
count++;
|
|
@@ -28455,7 +28534,7 @@ var sleuthClient = (o) => {
|
|
|
28455
28534
|
continue;
|
|
28456
28535
|
}
|
|
28457
28536
|
if (res.status === 401 || res.status === 403)
|
|
28458
|
-
throw new SleuthError(`Sleuth refused the key (HTTP ${res.status}) — check SLEUTH_API_KEY`, false);
|
|
28537
|
+
throw new SleuthError(`Sleuth refused the key (HTTP ${res.status})${forced ? ` as ${scheme} (SLEUTH_AUTH)` : ""} — check SLEUTH_API_KEY`, false);
|
|
28459
28538
|
if (res.status === 429 || res.status >= 500) {
|
|
28460
28539
|
lastWhy = `HTTP ${res.status}`;
|
|
28461
28540
|
const wait = backoffMs(attempt, res.headers.get("retry-after"));
|
|
@@ -28474,21 +28553,71 @@ var sleuthClient = (o) => {
|
|
|
28474
28553
|
if (res.status >= 400 && !errors.length)
|
|
28475
28554
|
throw new SleuthError(`Sleuth answered HTTP ${res.status}`, false);
|
|
28476
28555
|
settled = true;
|
|
28477
|
-
|
|
28556
|
+
const data = body.data ?? null;
|
|
28557
|
+
if (!data && errors.some((e) => RATE.test(e.message))) {
|
|
28478
28558
|
lastWhy = errors[0]?.message ?? "rate limited";
|
|
28479
28559
|
await sleep(backoffMs(attempt, null));
|
|
28480
28560
|
continue;
|
|
28481
28561
|
}
|
|
28482
|
-
|
|
28562
|
+
if (deniedOutright(data, errors)) {
|
|
28563
|
+
deniedOnce = true;
|
|
28564
|
+
const root = errors.find(isDenial)?.path?.[0];
|
|
28565
|
+
const what = typeof root === "string" ? ` to ${root}` : "";
|
|
28566
|
+
if (!kept && !switched) {
|
|
28567
|
+
switched = true;
|
|
28568
|
+
scheme = other(scheme);
|
|
28569
|
+
o.log?.(`sleuth: PERMISSION_DENIED${what} as ${startedAs} — trying ${scheme}`);
|
|
28570
|
+
attempt--;
|
|
28571
|
+
continue;
|
|
28572
|
+
}
|
|
28573
|
+
if (switched) {
|
|
28574
|
+
scheme = startedAs;
|
|
28575
|
+
throw new SleuthDenied(`Sleuth answered PERMISSION_DENIED${what} as both apikey and Bearer — the key cannot read it; check its scopes in Sleuth › Settings › API`);
|
|
28576
|
+
}
|
|
28577
|
+
throw new SleuthDenied(forced ? `Sleuth answered PERMISSION_DENIED${what} as ${scheme} (SLEUTH_AUTH) — unset SLEUTH_AUTH to try both schemes, or check the key's scopes` : `Sleuth answered PERMISSION_DENIED${what} as ${scheme}, the scheme its other queries take — the key cannot read it`);
|
|
28578
|
+
}
|
|
28579
|
+
if (deniedOnce)
|
|
28580
|
+
kept = true;
|
|
28581
|
+
return { data, errors };
|
|
28483
28582
|
}
|
|
28484
28583
|
throw new SleuthError(`Sleuth did not answer after ${retries + 1} tries (${lastWhy})`, true);
|
|
28485
28584
|
};
|
|
28486
|
-
return { request, calls: () => count };
|
|
28585
|
+
return { request, calls: () => count, scheme: () => scheme };
|
|
28487
28586
|
};
|
|
28488
28587
|
|
|
28489
28588
|
// src/sleuth/schema.ts
|
|
28490
|
-
var
|
|
28589
|
+
var MAX_QUERY_DEPTH = 10;
|
|
28590
|
+
var TYPE_REF = "kind name ofType { kind name ofType { kind name ofType { kind name } } }";
|
|
28491
28591
|
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} } } } } }`;
|
|
28592
|
+
var queryDepth = (query) => {
|
|
28593
|
+
let braces = 0;
|
|
28594
|
+
let parens = 0;
|
|
28595
|
+
let inString = false;
|
|
28596
|
+
let max = 0;
|
|
28597
|
+
for (let i = 0;i < query.length; i++) {
|
|
28598
|
+
const c = query[i];
|
|
28599
|
+
if (inString) {
|
|
28600
|
+
if (c === "\\")
|
|
28601
|
+
i++;
|
|
28602
|
+
else if (c === '"')
|
|
28603
|
+
inString = false;
|
|
28604
|
+
} else if (c === '"')
|
|
28605
|
+
inString = true;
|
|
28606
|
+
else if (c === "(")
|
|
28607
|
+
parens++;
|
|
28608
|
+
else if (c === ")")
|
|
28609
|
+
parens--;
|
|
28610
|
+
else if (parens > 0)
|
|
28611
|
+
continue;
|
|
28612
|
+
else if (c === "{")
|
|
28613
|
+
braces++;
|
|
28614
|
+
else if (c === "}")
|
|
28615
|
+
braces--;
|
|
28616
|
+
else if (braces > max && /[A-Za-z_]/.test(c))
|
|
28617
|
+
max = braces;
|
|
28618
|
+
}
|
|
28619
|
+
return max;
|
|
28620
|
+
};
|
|
28492
28621
|
var indexSchema = (intro) => ({
|
|
28493
28622
|
query: intro.__schema.queryType.name,
|
|
28494
28623
|
types: new Map(intro.__schema.types.map((t) => [t.name, t]))
|
|
@@ -28514,9 +28643,20 @@ var START = /^(start|startDate|startTime|startAt|from|fromDate|dateFrom|since|be
|
|
|
28514
28643
|
var END = /^(end|endDate|endTime|endAt|to|toDate|dateTo|until|before)$/i;
|
|
28515
28644
|
var CURSOR = /^(after|cursor|nextCursor|pageCursor|pageToken)$/i;
|
|
28516
28645
|
var PAGE = /^(page|pageNumber|pageNum)$/i;
|
|
28517
|
-
var
|
|
28646
|
+
var SIZE_NAMES = [
|
|
28647
|
+
"pagesize",
|
|
28648
|
+
"first",
|
|
28649
|
+
"limit",
|
|
28650
|
+
"perpage",
|
|
28651
|
+
"per_page",
|
|
28652
|
+
"pagelimit",
|
|
28653
|
+
"size",
|
|
28654
|
+
"count"
|
|
28655
|
+
];
|
|
28656
|
+
var sizeRank = (name) => SIZE_NAMES.indexOf(name.toLowerCase());
|
|
28518
28657
|
var OFFSET = /^(offset|skip)$/i;
|
|
28519
28658
|
var DATEISH = /date|time/i;
|
|
28659
|
+
var NUMERIC = /^(Int|Float|Long|BigInt|PositiveInt|NonNegativeInt)$/i;
|
|
28520
28660
|
var dateArg = (path, typeName) => ({
|
|
28521
28661
|
path,
|
|
28522
28662
|
dateOnly: /^date$/i.test(typeName)
|
|
@@ -28524,18 +28664,21 @@ var dateArg = (path, typeName) => ({
|
|
|
28524
28664
|
var planArgs = (s, args) => {
|
|
28525
28665
|
const plan = { org: [], missing: [] };
|
|
28526
28666
|
const visit = (vals, prefix, depth) => {
|
|
28667
|
+
const unfilled = [];
|
|
28527
28668
|
for (const a of vals) {
|
|
28528
28669
|
const u = unwrap(a.type);
|
|
28529
28670
|
const path = [...prefix, a.name];
|
|
28530
28671
|
const leaf = isLeaf(s, u.name);
|
|
28672
|
+
const numeric = leaf && !u.list && NUMERIC.test(u.name);
|
|
28531
28673
|
const dated = leaf && (DATEISH.test(u.name) || u.name === "String" && DATEISH.test(a.name) && (START.test(a.name) || END.test(a.name)));
|
|
28532
28674
|
if (kindOf(s, u.name) === "INPUT_OBJECT") {
|
|
28533
28675
|
const before = JSON.stringify(plan);
|
|
28534
28676
|
const inputs = s.types.get(u.name)?.inputFields ?? [];
|
|
28535
|
-
|
|
28536
|
-
|
|
28537
|
-
|
|
28538
|
-
|
|
28677
|
+
const inner = depth < 3 ? visit(inputs, path, depth + 1) : [];
|
|
28678
|
+
if (JSON.stringify(plan) !== before)
|
|
28679
|
+
unfilled.push(...inner);
|
|
28680
|
+
else if (isRequired(a) && inputs.some(isRequired))
|
|
28681
|
+
unfilled.push(path.join("."));
|
|
28539
28682
|
continue;
|
|
28540
28683
|
}
|
|
28541
28684
|
if (leaf && !u.list && ORG.test(a.name))
|
|
@@ -28546,17 +28689,20 @@ var planArgs = (s, args) => {
|
|
|
28546
28689
|
plan.end = dateArg(path, u.name);
|
|
28547
28690
|
else if (leaf && !dated && CURSOR.test(a.name) && !plan.cursor)
|
|
28548
28691
|
plan.cursor = path;
|
|
28549
|
-
else if (
|
|
28692
|
+
else if (numeric && PAGE.test(a.name) && !plan.page)
|
|
28550
28693
|
plan.page = { path, first: Number(a.defaultValue ?? 1) || 0 };
|
|
28551
|
-
else if (
|
|
28552
|
-
plan.size
|
|
28553
|
-
|
|
28694
|
+
else if (numeric && sizeRank(a.name) >= 0) {
|
|
28695
|
+
const held = plan.size?.[plan.size.length - 1];
|
|
28696
|
+
if (!held || sizeRank(a.name) < sizeRank(held))
|
|
28697
|
+
plan.size = path;
|
|
28698
|
+
} else if (numeric && OFFSET.test(a.name) && !plan.offset)
|
|
28554
28699
|
plan.offset = path;
|
|
28555
28700
|
else if (isRequired(a))
|
|
28556
|
-
|
|
28701
|
+
unfilled.push(path.join("."));
|
|
28557
28702
|
}
|
|
28703
|
+
return unfilled;
|
|
28558
28704
|
};
|
|
28559
|
-
visit(args, [], 0);
|
|
28705
|
+
plan.missing = visit(args, [], 0);
|
|
28560
28706
|
return plan;
|
|
28561
28707
|
};
|
|
28562
28708
|
var windowed = (p) => !!(p.start && p.end);
|
|
@@ -28877,6 +29023,24 @@ var jsonl = (items) => items.length ? `${items.map((i) => JSON.stringify(i)).joi
|
|
|
28877
29023
|
var readJsonl = (path) => readFileSync32(path, "utf8").split(`
|
|
28878
29024
|
`).filter((l) => l.trim()).map((l) => JSON.parse(l));
|
|
28879
29025
|
var CANNOT_QUERY = /Cannot query field "(\w+)" on type "(\w+)"/;
|
|
29026
|
+
var TOO_DEEP = /\bmax(imum)?\b.{0,24}\bdepth\b|\btoo deep\b/i;
|
|
29027
|
+
var PERMISSION_DENIED = /\bPERMISSION_DENIED\b/;
|
|
29028
|
+
var ORG_DENIED_NOTE = "Sleuth refused this key the organization object. Deploys come through orgChanges and do not need it; what is read under the organization (projects, teams, labels, metricsRecap counts) is missing from this export.";
|
|
29029
|
+
var whyNotFound = (schema, patterns) => {
|
|
29030
|
+
for (const re of patterns)
|
|
29031
|
+
for (const [type, t] of schema.types) {
|
|
29032
|
+
const f = (t.fields ?? []).find((x) => re.test(x.name));
|
|
29033
|
+
if (!f)
|
|
29034
|
+
continue;
|
|
29035
|
+
const args = planArgs(schema, f.args);
|
|
29036
|
+
if (args.missing.length)
|
|
29037
|
+
return `${type}.${f.name} needs ${args.missing.join(", ")}, which the export cannot fill`;
|
|
29038
|
+
if (type === schema.query && !args.org.length)
|
|
29039
|
+
return `${type}.${f.name} takes no org argument`;
|
|
29040
|
+
return `${type}.${f.name} is not reachable from the org`;
|
|
29041
|
+
}
|
|
29042
|
+
return "Sleuth's schema has no field by that name";
|
|
29043
|
+
};
|
|
28880
29044
|
var exportSleuth = async (o) => {
|
|
28881
29045
|
const log = o.log ?? (() => {});
|
|
28882
29046
|
const now = (o.now ?? (() => new Date))();
|
|
@@ -28922,13 +29086,26 @@ var exportSleuth = async (o) => {
|
|
|
28922
29086
|
`);
|
|
28923
29087
|
const schema = indexSchema(intro.data);
|
|
28924
29088
|
manifest.schema.types = schema.types.size;
|
|
29089
|
+
const fitDepth = new Map;
|
|
29090
|
+
let answered = false;
|
|
28925
29091
|
const ask = async (build, steps) => {
|
|
28926
29092
|
const keep = structural(steps);
|
|
28927
29093
|
const root = steps[0]?.field.name ?? "";
|
|
29094
|
+
const path = steps.map((s) => s.field.name).join(".");
|
|
29095
|
+
let depth = fitDepth.get(path) ?? o.depth;
|
|
28928
29096
|
for (let i = 0;i < 25; i++) {
|
|
28929
|
-
const r = await o.client.request(build());
|
|
28930
|
-
if (r.data && !r.errors.length)
|
|
29097
|
+
const r = await o.client.request(build(depth));
|
|
29098
|
+
if (r.data && !r.errors.length) {
|
|
29099
|
+
answered = true;
|
|
28931
29100
|
return r.data;
|
|
29101
|
+
}
|
|
29102
|
+
const tooDeep = r.errors.some((e) => TOO_DEEP.test(e.message));
|
|
29103
|
+
if (!r.data && tooDeep && depth > 0) {
|
|
29104
|
+
depth--;
|
|
29105
|
+
fitDepth.set(path, depth);
|
|
29106
|
+
log(`sleuth said the query was too deep — reading nested objects ${depth} deep`);
|
|
29107
|
+
continue;
|
|
29108
|
+
}
|
|
28932
29109
|
const prune = new Set;
|
|
28933
29110
|
for (const e of r.errors) {
|
|
28934
29111
|
const m = CANNOT_QUERY.exec(e.message);
|
|
@@ -28945,17 +29122,21 @@ var exportSleuth = async (o) => {
|
|
|
28945
29122
|
continue;
|
|
28946
29123
|
}
|
|
28947
29124
|
const why = r.errors.slice(0, 3).map((e) => e.message).join("; ");
|
|
28948
|
-
if (empty)
|
|
28949
|
-
|
|
29125
|
+
if (empty) {
|
|
29126
|
+
if (r.errors.some(isDenial))
|
|
29127
|
+
throw new SleuthDenied(why);
|
|
29128
|
+
throw new SleuthError(why || "Sleuth answered with no data", !tooDeep);
|
|
29129
|
+
}
|
|
28950
29130
|
for (const e of r.errors) {
|
|
28951
29131
|
const tf = e.path ? typeFieldAt(schema, e.path) : undefined;
|
|
28952
29132
|
warn(`${tf ? `${tf}: ` : ""}${e.message}`);
|
|
28953
29133
|
}
|
|
29134
|
+
answered = true;
|
|
28954
29135
|
return r.data;
|
|
28955
29136
|
}
|
|
28956
29137
|
throw new SleuthError("Sleuth kept refusing fields after 25 tries", false);
|
|
28957
29138
|
};
|
|
28958
|
-
const buildQuery = (steps, leafValues) => {
|
|
29139
|
+
const buildQuery = (steps, leafValues, depth) => {
|
|
28959
29140
|
const last = steps.length - 1;
|
|
28960
29141
|
const argsAt = (i) => {
|
|
28961
29142
|
const st = steps[i];
|
|
@@ -28964,15 +29145,24 @@ var exportSleuth = async (o) => {
|
|
|
28964
29145
|
return renderArgs(st.args, { org: o.org, ...st.args.size ? { size: NESTED_SIZE } : {} });
|
|
28965
29146
|
};
|
|
28966
29147
|
const leaf = steps[last];
|
|
28967
|
-
const
|
|
28968
|
-
|
|
28969
|
-
|
|
28970
|
-
|
|
28971
|
-
|
|
28972
|
-
|
|
28973
|
-
|
|
28974
|
-
|
|
28975
|
-
|
|
29148
|
+
const render = (d) => {
|
|
29149
|
+
const sel = buildSelection(schema, leaf.shape.item, d, exclude) ?? "{ __typename }";
|
|
29150
|
+
let inner = `${leaf.field.name}${argsAt(last)} ${wrapShape(leaf.shape, sel)}`;
|
|
29151
|
+
for (let i = last - 1;i >= 0; i--) {
|
|
29152
|
+
const st = steps[i];
|
|
29153
|
+
const ident = identitySelection(schema, st.shape.item, exclude);
|
|
29154
|
+
const body = `{ ${ident ? `${ident.slice(2, -2)} ` : ""}${inner} }`;
|
|
29155
|
+
inner = `${st.field.name}${argsAt(i)} ${wrapShape(st.shape, body)}`;
|
|
29156
|
+
}
|
|
29157
|
+
return `query { ${inner} }`;
|
|
29158
|
+
};
|
|
29159
|
+
let d = depth;
|
|
29160
|
+
let q = render(d);
|
|
29161
|
+
while (d > 0 && queryDepth(q) > MAX_QUERY_DEPTH)
|
|
29162
|
+
q = render(--d);
|
|
29163
|
+
if (d < o.depth)
|
|
29164
|
+
warn(`${leaf.field.name}: nested objects read ${d} deep, not ${o.depth} — Sleuth's max depth`);
|
|
29165
|
+
return q;
|
|
28976
29166
|
};
|
|
28977
29167
|
const extract = (steps, data) => {
|
|
28978
29168
|
let level = [
|
|
@@ -29018,7 +29208,7 @@ var exportSleuth = async (o) => {
|
|
|
29018
29208
|
...canPage && a.page ? { page } : {},
|
|
29019
29209
|
...canPage && !a.cursor && !a.page && a.offset ? { offset } : {}
|
|
29020
29210
|
};
|
|
29021
|
-
const data = await ask(() => buildQuery(steps, values), steps);
|
|
29211
|
+
const data = await ask((depth) => buildQuery(steps, values, depth), steps);
|
|
29022
29212
|
const { items, infos } = extract(steps, data);
|
|
29023
29213
|
if (!canPage) {
|
|
29024
29214
|
const full = oneParent && a.size !== undefined && items.length >= (values.size ?? 0);
|
|
@@ -29086,10 +29276,11 @@ var exportSleuth = async (o) => {
|
|
|
29086
29276
|
}
|
|
29087
29277
|
};
|
|
29088
29278
|
const months = monthsBetween(o.since, o.until);
|
|
29279
|
+
let denial;
|
|
29089
29280
|
const runSection = async (sec) => {
|
|
29090
29281
|
const steps = findSection(schema, sec.patterns);
|
|
29091
29282
|
if (!steps)
|
|
29092
|
-
return { found: false, why:
|
|
29283
|
+
return { found: false, why: whyNotFound(schema, sec.patterns) };
|
|
29093
29284
|
const leaf = steps[steps.length - 1];
|
|
29094
29285
|
const res = {
|
|
29095
29286
|
found: true,
|
|
@@ -29145,6 +29336,11 @@ var exportSleuth = async (o) => {
|
|
|
29145
29336
|
const why = e instanceof Error ? e.message : String(e);
|
|
29146
29337
|
res.months.push({ month, file, count: 0, error: why });
|
|
29147
29338
|
log(`${sec.name} ${month}: failed — ${why}`);
|
|
29339
|
+
if (e instanceof SleuthDenied) {
|
|
29340
|
+
res.error = `stopped at ${month} — ${why}`;
|
|
29341
|
+
denial = e;
|
|
29342
|
+
break;
|
|
29343
|
+
}
|
|
29148
29344
|
if (++failedInARow >= MAX_FAILED_MONTHS) {
|
|
29149
29345
|
res.error = `${MAX_FAILED_MONTHS} months in a row failed; stopped at ${month} — ${why}`;
|
|
29150
29346
|
break;
|
|
@@ -29199,7 +29395,8 @@ var exportSleuth = async (o) => {
|
|
|
29199
29395
|
`);
|
|
29200
29396
|
manifest.organization = { file: "organization.json" };
|
|
29201
29397
|
} catch (e) {
|
|
29202
|
-
|
|
29398
|
+
const error = e instanceof Error ? e.message : String(e);
|
|
29399
|
+
manifest.organization = e instanceof SleuthDenied || PERMISSION_DENIED.test(error) ? { error, note: ORG_DENIED_NOTE } : { error };
|
|
29203
29400
|
}
|
|
29204
29401
|
} else
|
|
29205
29402
|
manifest.organization = { error: "Sleuth's schema has no organization(orgSlug) field" };
|
|
@@ -29211,6 +29408,8 @@ var exportSleuth = async (o) => {
|
|
|
29211
29408
|
try {
|
|
29212
29409
|
manifest.sections[sec.name] = await runSection(sec);
|
|
29213
29410
|
} catch (e) {
|
|
29411
|
+
if (e instanceof SleuthDenied)
|
|
29412
|
+
denial = e;
|
|
29214
29413
|
manifest.sections[sec.name] = {
|
|
29215
29414
|
found: true,
|
|
29216
29415
|
path: [],
|
|
@@ -29223,6 +29422,8 @@ var exportSleuth = async (o) => {
|
|
|
29223
29422
|
log(`${sec.name}: failed — ${e instanceof Error ? e.message : String(e)}`);
|
|
29224
29423
|
}
|
|
29225
29424
|
save();
|
|
29425
|
+
if (denial && !answered)
|
|
29426
|
+
throw denial;
|
|
29226
29427
|
}
|
|
29227
29428
|
const deploys = manifest.sections.deploys;
|
|
29228
29429
|
const recap = orgRoot ? fieldOf2(schema, unwrap(orgRoot.type).name, "metricsRecap") : undefined;
|
|
@@ -29282,6 +29483,8 @@ var SLEUTH_USAGE = `usage: sleds sleuth export --org <slug> --out <dir> [options
|
|
|
29282
29483
|
Sleuth reports. Re-run to resume: finished past months on disk are not fetched again.
|
|
29283
29484
|
|
|
29284
29485
|
SLEUTH_API_KEY the org's API key or access token (Sleuth › Settings › API)
|
|
29486
|
+
SLEUTH_AUTH apikey or bearer: send only that scheme (default: apikey, switching to
|
|
29487
|
+
Bearer once if Sleuth refuses or answers PERMISSION_DENIED)
|
|
29285
29488
|
--org <slug> the Sleuth org slug (required)
|
|
29286
29489
|
--out <dir> where to write (required; created if missing)
|
|
29287
29490
|
--since <YYYY-MM> first month (default 2022-01)
|
|
@@ -29316,6 +29519,7 @@ var parse3 = (argv) => {
|
|
|
29316
29519
|
return { words, flags };
|
|
29317
29520
|
};
|
|
29318
29521
|
var outDir = (raw, home) => resolve12(raw.startsWith("~/") ? join35(home, raw.slice(2)) : raw);
|
|
29522
|
+
var SCHEMES = { apikey: "apikey", bearer: "Bearer" };
|
|
29319
29523
|
var str15 = (f, k) => typeof f[k] === "string" ? f[k] : undefined;
|
|
29320
29524
|
var MONTH2 = /^\d{4}-(0[1-9]|1[0-2])$/;
|
|
29321
29525
|
var KNOWN = [
|
|
@@ -29355,6 +29559,12 @@ var sleuthCmd = async (o) => {
|
|
|
29355
29559
|
error("SLEUTH_API_KEY is not set — the org's key from Sleuth › Settings › API");
|
|
29356
29560
|
return 2;
|
|
29357
29561
|
}
|
|
29562
|
+
const authRaw = env.SLEUTH_AUTH?.trim().toLowerCase();
|
|
29563
|
+
const scheme = authRaw && Object.hasOwn(SCHEMES, authRaw) ? SCHEMES[authRaw] : undefined;
|
|
29564
|
+
if (authRaw && !scheme) {
|
|
29565
|
+
error(`SLEUTH_AUTH is "${env.SLEUTH_AUTH}" — apikey or bearer, or unset to try both`);
|
|
29566
|
+
return 2;
|
|
29567
|
+
}
|
|
29358
29568
|
const now = (o.now ?? (() => new Date))();
|
|
29359
29569
|
const since = str15(flags, "since") ?? "2022-01";
|
|
29360
29570
|
const until = str15(flags, "until") ?? monthOf(now);
|
|
@@ -29377,6 +29587,7 @@ var sleuthCmd = async (o) => {
|
|
|
29377
29587
|
const progress = error;
|
|
29378
29588
|
const client = sleuthClient({
|
|
29379
29589
|
key,
|
|
29590
|
+
...scheme ? { scheme } : {},
|
|
29380
29591
|
url: endpoint,
|
|
29381
29592
|
...o.fetch ? { fetch: o.fetch } : {},
|
|
29382
29593
|
...o.sleep ? { sleep: o.sleep } : {},
|
|
@@ -30179,7 +30390,7 @@ var realPidAlive4 = (pid) => {
|
|
|
30179
30390
|
};
|
|
30180
30391
|
function machineClient(home, env) {
|
|
30181
30392
|
const src = meterSource(home, undefined, { env });
|
|
30182
|
-
return src ? fetchClient({ ...src, version: "0.1.
|
|
30393
|
+
return src ? fetchClient({ ...src, version: "0.1.67" }) : null;
|
|
30183
30394
|
}
|
|
30184
30395
|
async function noteAnswer2(home, a, now) {
|
|
30185
30396
|
try {
|
|
@@ -31053,7 +31264,7 @@ var main = async (argv) => {
|
|
|
31053
31264
|
now: Date.now(),
|
|
31054
31265
|
stderr: (line) => console.error(line),
|
|
31055
31266
|
isTTY: !!process.stderr.isTTY,
|
|
31056
|
-
version: "0.1.
|
|
31267
|
+
version: "0.1.67"
|
|
31057
31268
|
});
|
|
31058
31269
|
switch (cmd) {
|
|
31059
31270
|
case "update":
|
|
@@ -31288,7 +31499,7 @@ ${renderHelpText(argv[1])}`);
|
|
|
31288
31499
|
case "version":
|
|
31289
31500
|
case "--version":
|
|
31290
31501
|
case "-v":
|
|
31291
|
-
console.log("sleds 0.1.
|
|
31502
|
+
console.log("sleds 0.1.67");
|
|
31292
31503
|
return 0;
|
|
31293
31504
|
default:
|
|
31294
31505
|
console.error(`unknown command: ${cmd}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleds/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.67",
|
|
4
4
|
"description": "SLEDS CLI — the cost and limits of AI agent work, decided at the wall. Point Claude Code at a SLEDS proxy; estimate before you run; hold, switch model, or run overnight when you hit a limit.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|