@whittlelabs/sifter 0.30.15 → 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 +35 -1
- package/bin.js +94 -41
- package/bin.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ It needs three things on the machine first:
|
|
|
12
12
|
|---|---|---|
|
|
13
13
|
| Node.js | 20 or newer | [nodejs.org](https://nodejs.org), or a version manager such as [nvm](https://github.com/nvm-sh/nvm) or [fnm](https://github.com/Schniz/fnm) |
|
|
14
14
|
| git | 2.20 or newer | macOS: `xcode-select --install` (or Homebrew). Linux and WSL: your package manager, e.g. `sudo apt install git` |
|
|
15
|
-
| Claude Code | 2.0 or newer, signed in | `npm install -g @anthropic-ai/claude-code`, then run `claude` once and sign in |
|
|
15
|
+
| Claude Code | 2.0.45 or newer, signed in | `npm install -g @anthropic-ai/claude-code`, then run `claude` once and sign in |
|
|
16
16
|
|
|
17
17
|
On a Mac without the Command Line Tools, `/usr/bin/git` is only a placeholder that asks for them. The Sifter treats that as git not working, so install them first.
|
|
18
18
|
|
|
@@ -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
|
|
@@ -77,6 +93,24 @@ whittle-sifter run
|
|
|
77
93
|
|
|
78
94
|
The Sifter subscribes to your job pool and waits for review jobs. Leave it running in a terminal or wrap it in your favorite process supervisor.
|
|
79
95
|
|
|
96
|
+
### What a review can do
|
|
97
|
+
|
|
98
|
+
A review checks out the pull request and runs Claude Code on it. The pull request is someone else's code, so the review is held to reading:
|
|
99
|
+
|
|
100
|
+
- It is offered three tools, Read, Grep and Glob, and nothing else: no shell, no edits, no network tools, no subagents.
|
|
101
|
+
- It loads no Claude Code settings file, neither the repository's nor yours. Your allow rules, permission mode, hooks and plugins do not apply to reviews, and no MCP server starts.
|
|
102
|
+
|
|
103
|
+
Your login still works: Claude Code keeps it in your keychain or credentials file, or reads `ANTHROPIC_API_KEY`. If instead your login or provider is configured in your Claude Code settings (an `apiKeyHelper`, or an `env` block naming Bedrock, Vertex or a custom base URL), reviews will fail as signed out. Tell the Sifter to load your user settings in its `sifter.yaml` (`~/.sifter/sifter.yaml`, or `~/.sifter/<profile>/sifter.yaml` for a named profile):
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
executors:
|
|
107
|
+
claude-code:
|
|
108
|
+
type: claude-code
|
|
109
|
+
claudeSettings: user
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
With it, your allow rules apply to reviews too, so a review can read whatever they let it read. The three tools, the default permission mode and the hooks staying off still hold.
|
|
113
|
+
|
|
80
114
|
### When something goes away
|
|
81
115
|
|
|
82
116
|
A running Sifter deals with Sift or `claude` going away on its own, and says what it is doing:
|
package/bin.js
CHANGED
|
@@ -10048,17 +10048,19 @@ var require_claude_code = __commonJS({
|
|
|
10048
10048
|
var apply_1 = require_apply();
|
|
10049
10049
|
var DEFAULT_TIMEOUT = 6e5;
|
|
10050
10050
|
var REVIEW_TOOLS = ["Read", "Grep", "Glob"];
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10051
|
+
function reviewRunArgs(settingSources) {
|
|
10052
|
+
return [
|
|
10053
|
+
"--setting-sources",
|
|
10054
|
+
settingSources,
|
|
10055
|
+
"--strict-mcp-config",
|
|
10056
|
+
"--tools",
|
|
10057
|
+
REVIEW_TOOLS.join(","),
|
|
10058
|
+
"--permission-mode",
|
|
10059
|
+
"default",
|
|
10060
|
+
"--settings",
|
|
10061
|
+
JSON.stringify({ disableAllHooks: true })
|
|
10062
|
+
];
|
|
10063
|
+
}
|
|
10062
10064
|
var RETIRED_READ_TOOLS = ["LS"];
|
|
10063
10065
|
var HOSTED_ENV_PASSTHROUGH = [
|
|
10064
10066
|
"PATH",
|
|
@@ -10091,7 +10093,7 @@ var require_claude_code = __commonJS({
|
|
|
10091
10093
|
var READINESS_TIMEOUT_MS = 12e4;
|
|
10092
10094
|
var STDERR_LIMIT = 4e3;
|
|
10093
10095
|
var INSTALL_REMEDY = "install Claude Code (npm install -g @anthropic-ai/claude-code) where this process can find it on PATH";
|
|
10094
|
-
var LOGIN_REMEDY = "run `claude` once and sign in (or set a valid ANTHROPIC_API_KEY)";
|
|
10096
|
+
var LOGIN_REMEDY = "run `claude` once and sign in (or set a valid ANTHROPIC_API_KEY); if your login or provider is configured in Claude Code settings (apiKeyHelper, or an env block), set `claudeSettings: user` on the claude-code executor, since a review loads no settings file without it";
|
|
10095
10097
|
var LIMIT_REMEDY = "wait for the plan's usage limit to reset, or raise it";
|
|
10096
10098
|
var SIGNED_OUT = /invalid api key|please run \/login|not (?:logged|signed) in|oauth token (?:has )?expired|authentication_error|invalid x-api-key/i;
|
|
10097
10099
|
var OUT_OF_USAGE = /usage limit|credit balance is too low|out of (?:extra )?usage|hit your (?:\w+ )?(?:usage |spend )?limit|5-hour limit/i;
|
|
@@ -10341,8 +10343,12 @@ var require_claude_code = __commonJS({
|
|
|
10341
10343
|
const outputLabel = spec.outputLabel;
|
|
10342
10344
|
const model = modelOverride ?? this.config.model;
|
|
10343
10345
|
const allowedTools = local ? this.config.allowedTools : checkoutScopedTools(this.config.allowedTools);
|
|
10346
|
+
if (!local && this.config.claudeSettings === "user") {
|
|
10347
|
+
throw new Error("claude-code: `claudeSettings: user` is for an installed Sifter whose login lives in Claude Code settings; a hosted run authenticates with the key it is handed and loads no settings");
|
|
10348
|
+
}
|
|
10349
|
+
const settingSources = local && this.config.claudeSettings === "user" ? "user" : "";
|
|
10344
10350
|
return new Promise((resolve, reject) => {
|
|
10345
|
-
const args = ["--print", "--output-format", "stream-json", "--verbose", ...
|
|
10351
|
+
const args = ["--print", "--output-format", "stream-json", "--verbose", ...reviewRunArgs(settingSources)];
|
|
10346
10352
|
if (resumeSessionId)
|
|
10347
10353
|
args.push("--resume", resumeSessionId);
|
|
10348
10354
|
if (spec.outputSchema && typeof spec.outputSchema === "object" && Object.keys(spec.outputSchema).length > 0) {
|
|
@@ -10542,6 +10548,12 @@ var require_claude_code = __commonJS({
|
|
|
10542
10548
|
}
|
|
10543
10549
|
validated.keepTranscripts = config.keepTranscripts;
|
|
10544
10550
|
}
|
|
10551
|
+
if (config.claudeSettings !== void 0) {
|
|
10552
|
+
if (config.claudeSettings !== "user") {
|
|
10553
|
+
throw new Error('"claudeSettings" may only be "user" (leave it unset to load no settings file)');
|
|
10554
|
+
}
|
|
10555
|
+
validated.claudeSettings = "user";
|
|
10556
|
+
}
|
|
10545
10557
|
if (config.overlayAllowedHosts !== void 0) {
|
|
10546
10558
|
if (!Array.isArray(config.overlayAllowedHosts) || !config.overlayAllowedHosts.every((h) => typeof h === "string")) {
|
|
10547
10559
|
throw new Error('"overlayAllowedHosts" must be an array of strings');
|
|
@@ -12249,6 +12261,8 @@ var require_environment = __commonJS({
|
|
|
12249
12261
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
12250
12262
|
exports2.resolveEnvironment = resolveEnvironment;
|
|
12251
12263
|
exports2.applyEnvironment = applyEnvironment;
|
|
12264
|
+
exports2.environmentPolledBy = environmentPolledBy;
|
|
12265
|
+
exports2.executorDefaultsFor = executorDefaultsFor;
|
|
12252
12266
|
exports2.isWorthReporting = isWorthReporting;
|
|
12253
12267
|
exports2.renderEnvironmentReport = renderEnvironmentReport;
|
|
12254
12268
|
exports2.renderVerificationLine = renderVerificationLine;
|
|
@@ -12289,6 +12303,28 @@ var require_environment = __commonJS({
|
|
|
12289
12303
|
}
|
|
12290
12304
|
};
|
|
12291
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
|
+
}
|
|
12292
12328
|
function isWorthReporting(resolved) {
|
|
12293
12329
|
return resolved.environment !== null || resolved.keep.source === "flag" || resolved.jobs.source === "flag";
|
|
12294
12330
|
}
|
|
@@ -12436,7 +12472,7 @@ var require_checks = __commonJS({
|
|
|
12436
12472
|
const user = config.executors[WORKSPACE_EXECUTOR];
|
|
12437
12473
|
if (user === void 0)
|
|
12438
12474
|
return null;
|
|
12439
|
-
const defaults = subject.brand.
|
|
12475
|
+
const defaults = (0, environment_1.executorDefaultsFor)(subject.brand, config.auth.jobsApiUrl)?.[WORKSPACE_EXECUTOR] ?? {};
|
|
12440
12476
|
return { ...defaults, ...user };
|
|
12441
12477
|
}
|
|
12442
12478
|
function runRoot(config, subject, osTmpDir) {
|
|
@@ -12473,7 +12509,7 @@ var require_checks = __commonJS({
|
|
|
12473
12509
|
return (0, apply_1.expandHome)(subject.requestedTmpDir);
|
|
12474
12510
|
}
|
|
12475
12511
|
const settings = {
|
|
12476
|
-
...subject.brand.
|
|
12512
|
+
...(0, environment_1.executorDefaultsFor)(subject.brand, subject.brand.keepRegistration.jobsApiUrl)?.[WORKSPACE_EXECUTOR] ?? {},
|
|
12477
12513
|
...plan.keptBlock ?? {}
|
|
12478
12514
|
};
|
|
12479
12515
|
const tmpDir = settings.tmpDir;
|
|
@@ -12811,7 +12847,8 @@ var require_checks = __commonJS({
|
|
|
12811
12847
|
const hosts = settings.overlayAllowedHosts;
|
|
12812
12848
|
const origins = Array.isArray(hosts) ? hosts.filter((h) => typeof h === "string") : [];
|
|
12813
12849
|
if (origins.length === 0) {
|
|
12814
|
-
|
|
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)})`);
|
|
12815
12852
|
}
|
|
12816
12853
|
return pass("overlay-hosts", origins.join(", "));
|
|
12817
12854
|
});
|
|
@@ -12892,7 +12929,7 @@ var require_checks = __commonJS({
|
|
|
12892
12929
|
};
|
|
12893
12930
|
function mergedExecutorConfigs(config, subject) {
|
|
12894
12931
|
const user = config.executors;
|
|
12895
|
-
const defaults = subject.brand.
|
|
12932
|
+
const defaults = (0, environment_1.executorDefaultsFor)(subject.brand, config.auth.jobsApiUrl);
|
|
12896
12933
|
if (!defaults)
|
|
12897
12934
|
return user;
|
|
12898
12935
|
const merged = {};
|
|
@@ -25738,6 +25775,7 @@ var require_shuttle = __commonJS({
|
|
|
25738
25775
|
var output_gates_1 = require_output_gates();
|
|
25739
25776
|
var keep_1 = require_dist2();
|
|
25740
25777
|
var apply_1 = require_apply();
|
|
25778
|
+
var environment_1 = require_environment();
|
|
25741
25779
|
var version_check_1 = require_version_check();
|
|
25742
25780
|
var typed_exit_1 = require_typed_exit();
|
|
25743
25781
|
var logger_1 = require_logger();
|
|
@@ -25939,7 +25977,7 @@ var require_shuttle = __commonJS({
|
|
|
25939
25977
|
});
|
|
25940
25978
|
const registry = new registry_1.ExecutorRegistry(this.brand.executorAllowlist);
|
|
25941
25979
|
registerBuiltInExecutors(registry, this.brand.executorAllowlist);
|
|
25942
|
-
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));
|
|
25943
25981
|
const byStrategy = /* @__PURE__ */ new Map();
|
|
25944
25982
|
for (const executor of executors) {
|
|
25945
25983
|
for (const strategy of executor.strategies)
|
|
@@ -26722,8 +26760,8 @@ var require_dist5 = __commonJS({
|
|
|
26722
26760
|
"../../packages/shuttle/dist/index.js"(exports2) {
|
|
26723
26761
|
"use strict";
|
|
26724
26762
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
26725
|
-
exports2.
|
|
26726
|
-
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;
|
|
26727
26765
|
var cli_1 = require_cli();
|
|
26728
26766
|
Object.defineProperty(exports2, "createCli", { enumerable: true, get: function() {
|
|
26729
26767
|
return cli_1.createCli;
|
|
@@ -26855,9 +26893,15 @@ var require_dist5 = __commonJS({
|
|
|
26855
26893
|
Object.defineProperty(exports2, "applyEnvironment", { enumerable: true, get: function() {
|
|
26856
26894
|
return environment_1.applyEnvironment;
|
|
26857
26895
|
} });
|
|
26896
|
+
Object.defineProperty(exports2, "environmentPolledBy", { enumerable: true, get: function() {
|
|
26897
|
+
return environment_1.environmentPolledBy;
|
|
26898
|
+
} });
|
|
26858
26899
|
Object.defineProperty(exports2, "environmentReportJson", { enumerable: true, get: function() {
|
|
26859
26900
|
return environment_1.environmentReportJson;
|
|
26860
26901
|
} });
|
|
26902
|
+
Object.defineProperty(exports2, "executorDefaultsFor", { enumerable: true, get: function() {
|
|
26903
|
+
return environment_1.executorDefaultsFor;
|
|
26904
|
+
} });
|
|
26861
26905
|
Object.defineProperty(exports2, "hostOf", { enumerable: true, get: function() {
|
|
26862
26906
|
return environment_1.hostOf;
|
|
26863
26907
|
} });
|
|
@@ -26922,7 +26966,7 @@ var import_path = require("path");
|
|
|
26922
26966
|
var import_promises = require("fs/promises");
|
|
26923
26967
|
var import_yaml = __toESM(require_dist3());
|
|
26924
26968
|
var import_shuttle = __toESM(require_dist5());
|
|
26925
|
-
var buildVersion = true ? "0.30.
|
|
26969
|
+
var buildVersion = true ? "0.30.17" : pkg.version;
|
|
26926
26970
|
var sifterBrand = {
|
|
26927
26971
|
product: {
|
|
26928
26972
|
id: "sifter",
|
|
@@ -26963,24 +27007,12 @@ var sifterBrand = {
|
|
|
26963
27007
|
git: "2.20.0",
|
|
26964
27008
|
claude: "2.0.45"
|
|
26965
27009
|
},
|
|
26966
|
-
//
|
|
26967
|
-
//
|
|
26968
|
-
//
|
|
26969
|
-
//
|
|
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.
|
|
26970
27014
|
executorDefaults: {
|
|
26971
27015
|
"claude-code": {
|
|
26972
|
-
overlayAllowedHosts: [
|
|
26973
|
-
"https://api.sift.whittlelabs.com",
|
|
26974
|
-
"https://api.sift.stage.whittlelabs.com",
|
|
26975
|
-
// The test tier signs overlay URLs too, and a Sifter the E2E suite
|
|
26976
|
-
// runs on the runner reaches Sift by this host rather than by the
|
|
26977
|
-
// in-cluster service name the hosted worker uses. Without it that
|
|
26978
|
-
// Sifter passes its own preflight — the check asks only that the list
|
|
26979
|
-
// is non-empty — and then runs every review standards-blind, which is
|
|
26980
|
-
// the exact silence of 2026-07-13.
|
|
26981
|
-
"https://api.sift.test.whittlelabs.com",
|
|
26982
|
-
"http://localhost:3008"
|
|
26983
|
-
],
|
|
26984
27016
|
// Transcript retention is a diagnostic switch a person flips for one
|
|
26985
27017
|
// session, so it rides the environment rather than a file they would
|
|
26986
27018
|
// have to remember to unset. The shared package reads no env of its
|
|
@@ -27004,22 +27036,40 @@ var sifterBrand = {
|
|
|
27004
27036
|
// `studio` is the local dev fleet on this machine, reachable over Tailscale
|
|
27005
27037
|
// at the `studio.whittlelabs.com` hostname rather than `localhost`, because
|
|
27006
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.
|
|
27007
27048
|
environments: {
|
|
27008
27049
|
prod: {
|
|
27009
27050
|
keepApiUrl: "https://keep.whittlelabs.com",
|
|
27010
|
-
jobsApiUrl: "https://jobs.whittlelabs.com"
|
|
27051
|
+
jobsApiUrl: "https://jobs.whittlelabs.com",
|
|
27052
|
+
executorDefaults: overlayOrigin("https://api.sift.whittlelabs.com")
|
|
27011
27053
|
},
|
|
27012
27054
|
stage: {
|
|
27013
27055
|
keepApiUrl: "https://keep.stage.whittlelabs.com",
|
|
27014
|
-
jobsApiUrl: "https://jobs.stage.whittlelabs.com"
|
|
27056
|
+
jobsApiUrl: "https://jobs.stage.whittlelabs.com",
|
|
27057
|
+
executorDefaults: overlayOrigin("https://api.sift.stage.whittlelabs.com")
|
|
27015
27058
|
},
|
|
27016
27059
|
test: {
|
|
27017
27060
|
keepApiUrl: "https://keep.test.whittlelabs.com",
|
|
27018
|
-
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")
|
|
27019
27066
|
},
|
|
27020
27067
|
studio: {
|
|
27021
27068
|
keepApiUrl: "http://studio.whittlelabs.com:3007",
|
|
27022
|
-
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")
|
|
27023
27073
|
}
|
|
27024
27074
|
},
|
|
27025
27075
|
keepRegistration: {
|
|
@@ -27107,6 +27157,9 @@ var sifterBrand = {
|
|
|
27107
27157
|
}
|
|
27108
27158
|
]
|
|
27109
27159
|
};
|
|
27160
|
+
function overlayOrigin(origin) {
|
|
27161
|
+
return { "claude-code": { overlayAllowedHosts: [origin] } };
|
|
27162
|
+
}
|
|
27110
27163
|
async function buildExecutorBlock(executor, ctx) {
|
|
27111
27164
|
switch (executor) {
|
|
27112
27165
|
case "claude-code": {
|