@whittlelabs/sifter 0.30.16 → 0.30.17
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 +16 -0
- package/bin.js +69 -28
- package/bin.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,22 @@ Once the backend answers, one more line names the page it sent you to, which is
|
|
|
69
69
|
|
|
70
70
|
Pairing writes credentials to `~/.sifter/<profile>/config.json` (mode 0600), or `~/.sifter/config.json` for the default profile.
|
|
71
71
|
|
|
72
|
+
The environment also decides where a review may fetch your standards documents from: that environment's Sift API and nowhere else. A Sifter paired with production allows `https://api.sift.whittlelabs.com`; `stage` and `test` allow their own `api.sift` host, and `studio` allows `http://localhost:3008`. The Sifter finds its environment from the Jobs URL in its `sifter.yaml`, so an existing pairing needs nothing re-run. `run` and `doctor` print the list in use on their `overlay-hosts` line:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
PASS overlay-hosts https://api.sift.whittlelabs.com
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
A backend this build does not know gets no default, and `overlay-hosts` warns that reviews would run without standards. Name the origin that backend signs standards URLs with under the `claude-code` executor in `sifter.yaml`. Your own list always replaces the environment's:
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
executors:
|
|
82
|
+
claude-code:
|
|
83
|
+
type: claude-code
|
|
84
|
+
overlayAllowedHosts:
|
|
85
|
+
- http://box.local:3008
|
|
86
|
+
```
|
|
87
|
+
|
|
72
88
|
## Run
|
|
73
89
|
|
|
74
90
|
```bash
|
package/bin.js
CHANGED
|
@@ -12261,6 +12261,8 @@ var require_environment = __commonJS({
|
|
|
12261
12261
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
12262
12262
|
exports2.resolveEnvironment = resolveEnvironment;
|
|
12263
12263
|
exports2.applyEnvironment = applyEnvironment;
|
|
12264
|
+
exports2.environmentPolledBy = environmentPolledBy;
|
|
12265
|
+
exports2.executorDefaultsFor = executorDefaultsFor;
|
|
12264
12266
|
exports2.isWorthReporting = isWorthReporting;
|
|
12265
12267
|
exports2.renderEnvironmentReport = renderEnvironmentReport;
|
|
12266
12268
|
exports2.renderVerificationLine = renderVerificationLine;
|
|
@@ -12301,6 +12303,28 @@ var require_environment = __commonJS({
|
|
|
12301
12303
|
}
|
|
12302
12304
|
};
|
|
12303
12305
|
}
|
|
12306
|
+
function environmentPolledBy(brand, jobsApiUrl) {
|
|
12307
|
+
const wanted = normalizeUrl(jobsApiUrl);
|
|
12308
|
+
for (const [name, entry] of Object.entries(brand.environments ?? {})) {
|
|
12309
|
+
if (normalizeUrl(entry.jobsApiUrl) === wanted)
|
|
12310
|
+
return name;
|
|
12311
|
+
}
|
|
12312
|
+
return null;
|
|
12313
|
+
}
|
|
12314
|
+
function executorDefaultsFor(brand, jobsApiUrl) {
|
|
12315
|
+
const name = environmentPolledBy(brand, jobsApiUrl);
|
|
12316
|
+
const tier = name === null ? void 0 : brand.environments?.[name]?.executorDefaults;
|
|
12317
|
+
if (!tier)
|
|
12318
|
+
return brand.executorDefaults;
|
|
12319
|
+
const merged = { ...brand.executorDefaults ?? {} };
|
|
12320
|
+
for (const [executor, settings] of Object.entries(tier)) {
|
|
12321
|
+
merged[executor] = { ...merged[executor] ?? {}, ...settings };
|
|
12322
|
+
}
|
|
12323
|
+
return merged;
|
|
12324
|
+
}
|
|
12325
|
+
function normalizeUrl(url) {
|
|
12326
|
+
return url.replace(/\/+$/, "").toLowerCase();
|
|
12327
|
+
}
|
|
12304
12328
|
function isWorthReporting(resolved) {
|
|
12305
12329
|
return resolved.environment !== null || resolved.keep.source === "flag" || resolved.jobs.source === "flag";
|
|
12306
12330
|
}
|
|
@@ -12448,7 +12472,7 @@ var require_checks = __commonJS({
|
|
|
12448
12472
|
const user = config.executors[WORKSPACE_EXECUTOR];
|
|
12449
12473
|
if (user === void 0)
|
|
12450
12474
|
return null;
|
|
12451
|
-
const defaults = subject.brand.
|
|
12475
|
+
const defaults = (0, environment_1.executorDefaultsFor)(subject.brand, config.auth.jobsApiUrl)?.[WORKSPACE_EXECUTOR] ?? {};
|
|
12452
12476
|
return { ...defaults, ...user };
|
|
12453
12477
|
}
|
|
12454
12478
|
function runRoot(config, subject, osTmpDir) {
|
|
@@ -12485,7 +12509,7 @@ var require_checks = __commonJS({
|
|
|
12485
12509
|
return (0, apply_1.expandHome)(subject.requestedTmpDir);
|
|
12486
12510
|
}
|
|
12487
12511
|
const settings = {
|
|
12488
|
-
...subject.brand.
|
|
12512
|
+
...(0, environment_1.executorDefaultsFor)(subject.brand, subject.brand.keepRegistration.jobsApiUrl)?.[WORKSPACE_EXECUTOR] ?? {},
|
|
12489
12513
|
...plan.keptBlock ?? {}
|
|
12490
12514
|
};
|
|
12491
12515
|
const tmpDir = settings.tmpDir;
|
|
@@ -12823,7 +12847,8 @@ var require_checks = __commonJS({
|
|
|
12823
12847
|
const hosts = settings.overlayAllowedHosts;
|
|
12824
12848
|
const origins = Array.isArray(hosts) ? hosts.filter((h) => typeof h === "string") : [];
|
|
12825
12849
|
if (origins.length === 0) {
|
|
12826
|
-
|
|
12850
|
+
const fallback = (0, environment_1.executorDefaultsFor)(subject.brand, config.auth.jobsApiUrl)?.[WORKSPACE_EXECUTOR]?.overlayAllowedHosts;
|
|
12851
|
+
return warn("overlay-hosts", "the overlay allowlist is empty, so every review will run standards-blind", Array.isArray(fallback) && fallback.length > 0 ? `remove overlayAllowedHosts from the ${WORKSPACE_EXECUTOR} executor to use this tier's default` : `add overlayAllowedHosts to the ${WORKSPACE_EXECUTOR} executor, naming the origin your backend signs standards URLs with (this build names none for Jobs at ${(0, environment_1.hostOf)(config.auth.jobsApiUrl)})`);
|
|
12827
12852
|
}
|
|
12828
12853
|
return pass("overlay-hosts", origins.join(", "));
|
|
12829
12854
|
});
|
|
@@ -12904,7 +12929,7 @@ var require_checks = __commonJS({
|
|
|
12904
12929
|
};
|
|
12905
12930
|
function mergedExecutorConfigs(config, subject) {
|
|
12906
12931
|
const user = config.executors;
|
|
12907
|
-
const defaults = subject.brand.
|
|
12932
|
+
const defaults = (0, environment_1.executorDefaultsFor)(subject.brand, config.auth.jobsApiUrl);
|
|
12908
12933
|
if (!defaults)
|
|
12909
12934
|
return user;
|
|
12910
12935
|
const merged = {};
|
|
@@ -25750,6 +25775,7 @@ var require_shuttle = __commonJS({
|
|
|
25750
25775
|
var output_gates_1 = require_output_gates();
|
|
25751
25776
|
var keep_1 = require_dist2();
|
|
25752
25777
|
var apply_1 = require_apply();
|
|
25778
|
+
var environment_1 = require_environment();
|
|
25753
25779
|
var version_check_1 = require_version_check();
|
|
25754
25780
|
var typed_exit_1 = require_typed_exit();
|
|
25755
25781
|
var logger_1 = require_logger();
|
|
@@ -25951,7 +25977,7 @@ var require_shuttle = __commonJS({
|
|
|
25951
25977
|
});
|
|
25952
25978
|
const registry = new registry_1.ExecutorRegistry(this.brand.executorAllowlist);
|
|
25953
25979
|
registerBuiltInExecutors(registry, this.brand.executorAllowlist);
|
|
25954
|
-
const executors = registry.build(mergeExecutorDefaults(this.brand.
|
|
25980
|
+
const executors = registry.build(mergeExecutorDefaults((0, environment_1.executorDefaultsFor)(this.brand, this.config.auth.jobsApiUrl), this.config.executors));
|
|
25955
25981
|
const byStrategy = /* @__PURE__ */ new Map();
|
|
25956
25982
|
for (const executor of executors) {
|
|
25957
25983
|
for (const strategy of executor.strategies)
|
|
@@ -26734,8 +26760,8 @@ var require_dist5 = __commonJS({
|
|
|
26734
26760
|
"../../packages/shuttle/dist/index.js"(exports2) {
|
|
26735
26761
|
"use strict";
|
|
26736
26762
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
26737
|
-
exports2.
|
|
26738
|
-
exports2.revokePairing = exports2.listPairings = exports2.PairingConfigStore = exports2.pair = void 0;
|
|
26763
|
+
exports2.DEFAULT_REFUSAL_MESSAGE = exports2.DEFAULT_MONTHLY_PERIOD = exports2.DEFAULT_DAILY_PERIOD = exports2.SUBSTRATE_BRAND = exports2.verificationJson = exports2.resolveEnvironment = exports2.renderVerificationLine = exports2.renderEnvironmentReport = exports2.isWorthReporting = exports2.hostOf = exports2.executorDefaultsFor = exports2.environmentReportJson = exports2.environmentPolledBy = exports2.applyEnvironment = exports2.renderTemplate = exports2.expandHome = exports2.resolvePaths = exports2.SpendStore = exports2.AuditLogStore = exports2.createSpendTrackerObserver = exports2.createAuditLogObserver = exports2.createLoggerObserver = exports2.SpendCapExceeded = exports2.ObserverChain = exports2.createCustomScriptExecutor = exports2.createWebhookExecutor = exports2.createHttpApiExecutor = exports2.createClaudeCodeExecutor = exports2.ExecutorEnvironmentError = exports2.ExecutorRegistry = exports2.AgentCredentialRevokedError = exports2.runPreflight = exports2.renderLines = exports2.renderJson = exports2.printReport = exports2.hasFailure = exports2.createProbes = exports2.checksFor = exports2.PREFLIGHT_FLOORS = exports2.PREFLIGHT_EXIT_CODE = exports2.CHECKS = exports2.readResponseLatestVersion = exports2.checkResponseMinVersion = exports2.compareSemver = exports2.UpgradeRequiredError = exports2.LATEST_VERSION_HEADER = exports2.MIN_VERSION_HEADER = exports2.loadConfig = exports2.Shuttle = exports2.createCli = void 0;
|
|
26764
|
+
exports2.revokePairing = exports2.listPairings = exports2.PairingConfigStore = exports2.pair = exports2.scaffoldedConfigPath = exports2.planScaffold = void 0;
|
|
26739
26765
|
var cli_1 = require_cli();
|
|
26740
26766
|
Object.defineProperty(exports2, "createCli", { enumerable: true, get: function() {
|
|
26741
26767
|
return cli_1.createCli;
|
|
@@ -26867,9 +26893,15 @@ var require_dist5 = __commonJS({
|
|
|
26867
26893
|
Object.defineProperty(exports2, "applyEnvironment", { enumerable: true, get: function() {
|
|
26868
26894
|
return environment_1.applyEnvironment;
|
|
26869
26895
|
} });
|
|
26896
|
+
Object.defineProperty(exports2, "environmentPolledBy", { enumerable: true, get: function() {
|
|
26897
|
+
return environment_1.environmentPolledBy;
|
|
26898
|
+
} });
|
|
26870
26899
|
Object.defineProperty(exports2, "environmentReportJson", { enumerable: true, get: function() {
|
|
26871
26900
|
return environment_1.environmentReportJson;
|
|
26872
26901
|
} });
|
|
26902
|
+
Object.defineProperty(exports2, "executorDefaultsFor", { enumerable: true, get: function() {
|
|
26903
|
+
return environment_1.executorDefaultsFor;
|
|
26904
|
+
} });
|
|
26873
26905
|
Object.defineProperty(exports2, "hostOf", { enumerable: true, get: function() {
|
|
26874
26906
|
return environment_1.hostOf;
|
|
26875
26907
|
} });
|
|
@@ -26934,7 +26966,7 @@ var import_path = require("path");
|
|
|
26934
26966
|
var import_promises = require("fs/promises");
|
|
26935
26967
|
var import_yaml = __toESM(require_dist3());
|
|
26936
26968
|
var import_shuttle = __toESM(require_dist5());
|
|
26937
|
-
var buildVersion = true ? "0.30.
|
|
26969
|
+
var buildVersion = true ? "0.30.17" : pkg.version;
|
|
26938
26970
|
var sifterBrand = {
|
|
26939
26971
|
product: {
|
|
26940
26972
|
id: "sifter",
|
|
@@ -26975,24 +27007,12 @@ var sifterBrand = {
|
|
|
26975
27007
|
git: "2.20.0",
|
|
26976
27008
|
claude: "2.0.45"
|
|
26977
27009
|
},
|
|
26978
|
-
//
|
|
26979
|
-
//
|
|
26980
|
-
//
|
|
26981
|
-
//
|
|
27010
|
+
// The overlay-fetch allowlist is NOT here: each tier signs its standards
|
|
27011
|
+
// URLs with its own Sift origin (SIFT_PUBLIC_API_URL), so the origin is a
|
|
27012
|
+
// property of the tier and lives on its `environments` entry below. A
|
|
27013
|
+
// Sifter paired to production allows production's origin and nothing else.
|
|
26982
27014
|
executorDefaults: {
|
|
26983
27015
|
"claude-code": {
|
|
26984
|
-
overlayAllowedHosts: [
|
|
26985
|
-
"https://api.sift.whittlelabs.com",
|
|
26986
|
-
"https://api.sift.stage.whittlelabs.com",
|
|
26987
|
-
// The test tier signs overlay URLs too, and a Sifter the E2E suite
|
|
26988
|
-
// runs on the runner reaches Sift by this host rather than by the
|
|
26989
|
-
// in-cluster service name the hosted worker uses. Without it that
|
|
26990
|
-
// Sifter passes its own preflight — the check asks only that the list
|
|
26991
|
-
// is non-empty — and then runs every review standards-blind, which is
|
|
26992
|
-
// the exact silence of 2026-07-13.
|
|
26993
|
-
"https://api.sift.test.whittlelabs.com",
|
|
26994
|
-
"http://localhost:3008"
|
|
26995
|
-
],
|
|
26996
27016
|
// Transcript retention is a diagnostic switch a person flips for one
|
|
26997
27017
|
// session, so it rides the environment rather than a file they would
|
|
26998
27018
|
// have to remember to unset. The shared package reads no env of its
|
|
@@ -27016,22 +27036,40 @@ var sifterBrand = {
|
|
|
27016
27036
|
// `studio` is the local dev fleet on this machine, reachable over Tailscale
|
|
27017
27037
|
// at the `studio.whittlelabs.com` hostname rather than `localhost`, because
|
|
27018
27038
|
// a pairing made from a phone or a second machine has to resolve too.
|
|
27039
|
+
//
|
|
27040
|
+
// Each entry also carries the origin that tier signs overlay URLs with.
|
|
27041
|
+
// A run matches its tier by the Jobs URL its config polls (written from the
|
|
27042
|
+
// pairing, and held equal to it by `pairing-environment`), so an existing
|
|
27043
|
+
// pairing picks up its own tier's origin without re-pairing, and a user's
|
|
27044
|
+
// own `overlayAllowedHosts` still wins. Without the right origin the
|
|
27045
|
+
// executor's SSRF guard silently skips the standards overlay and every
|
|
27046
|
+
// review runs standards-blind (2026-07-13); tests/overlay-hosts.test.ts
|
|
27047
|
+
// holds every tier to naming its own.
|
|
27019
27048
|
environments: {
|
|
27020
27049
|
prod: {
|
|
27021
27050
|
keepApiUrl: "https://keep.whittlelabs.com",
|
|
27022
|
-
jobsApiUrl: "https://jobs.whittlelabs.com"
|
|
27051
|
+
jobsApiUrl: "https://jobs.whittlelabs.com",
|
|
27052
|
+
executorDefaults: overlayOrigin("https://api.sift.whittlelabs.com")
|
|
27023
27053
|
},
|
|
27024
27054
|
stage: {
|
|
27025
27055
|
keepApiUrl: "https://keep.stage.whittlelabs.com",
|
|
27026
|
-
jobsApiUrl: "https://jobs.stage.whittlelabs.com"
|
|
27056
|
+
jobsApiUrl: "https://jobs.stage.whittlelabs.com",
|
|
27057
|
+
executorDefaults: overlayOrigin("https://api.sift.stage.whittlelabs.com")
|
|
27027
27058
|
},
|
|
27028
27059
|
test: {
|
|
27029
27060
|
keepApiUrl: "https://keep.test.whittlelabs.com",
|
|
27030
|
-
jobsApiUrl: "https://jobs.test.whittlelabs.com"
|
|
27061
|
+
jobsApiUrl: "https://jobs.test.whittlelabs.com",
|
|
27062
|
+
// The public host, because a Sifter the E2E suite runs on the runner
|
|
27063
|
+
// reaches Sift by it rather than by the in-cluster service name the
|
|
27064
|
+
// test tier's hosted worker uses (that worker sets its own list).
|
|
27065
|
+
executorDefaults: overlayOrigin("https://api.sift.test.whittlelabs.com")
|
|
27031
27066
|
},
|
|
27032
27067
|
studio: {
|
|
27033
27068
|
keepApiUrl: "http://studio.whittlelabs.com:3007",
|
|
27034
|
-
jobsApiUrl: "http://studio.whittlelabs.com:3005"
|
|
27069
|
+
jobsApiUrl: "http://studio.whittlelabs.com:3005",
|
|
27070
|
+
// The dev fleet's Sift signs with SIFT_PUBLIC_API_URL from its .env,
|
|
27071
|
+
// which is localhost.
|
|
27072
|
+
executorDefaults: overlayOrigin("http://localhost:3008")
|
|
27035
27073
|
}
|
|
27036
27074
|
},
|
|
27037
27075
|
keepRegistration: {
|
|
@@ -27119,6 +27157,9 @@ var sifterBrand = {
|
|
|
27119
27157
|
}
|
|
27120
27158
|
]
|
|
27121
27159
|
};
|
|
27160
|
+
function overlayOrigin(origin) {
|
|
27161
|
+
return { "claude-code": { overlayAllowedHosts: [origin] } };
|
|
27162
|
+
}
|
|
27122
27163
|
async function buildExecutorBlock(executor, ctx) {
|
|
27123
27164
|
switch (executor) {
|
|
27124
27165
|
case "claude-code": {
|