@whittlelabs/sifter 0.7.1 → 0.9.0
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/bin.js +650 -56
- package/bin.js.map +4 -4
- package/package.json +1 -1
package/bin.js
CHANGED
|
@@ -3226,7 +3226,9 @@ var require_device_code = __commonJS({
|
|
|
3226
3226
|
body: JSON.stringify({
|
|
3227
3227
|
client_id: opts.oauthClientId,
|
|
3228
3228
|
device_code: opts.deviceCode,
|
|
3229
|
-
display_name: opts.displayName
|
|
3229
|
+
display_name: opts.displayName,
|
|
3230
|
+
hostname: (0, os_1.hostname)(),
|
|
3231
|
+
os_user: (0, os_1.userInfo)().username
|
|
3230
3232
|
})
|
|
3231
3233
|
});
|
|
3232
3234
|
if (response.status === 202) {
|
|
@@ -3254,7 +3256,9 @@ var require_device_code = __commonJS({
|
|
|
3254
3256
|
body: JSON.stringify({
|
|
3255
3257
|
client_id: opts.oauthClientId,
|
|
3256
3258
|
user_code: opts.userCode,
|
|
3257
|
-
display_name: opts.displayName
|
|
3259
|
+
display_name: opts.displayName,
|
|
3260
|
+
hostname: (0, os_1.hostname)(),
|
|
3261
|
+
os_user: (0, os_1.userInfo)().username
|
|
3258
3262
|
})
|
|
3259
3263
|
});
|
|
3260
3264
|
(0, version_check_1.checkResponseMinVersion)({
|
|
@@ -15665,6 +15669,21 @@ var require_prompt_execution = __commonJS({
|
|
|
15665
15669
|
if (typeof spec.outputSchema !== "object" || spec.outputSchema === null) {
|
|
15666
15670
|
throw new Error("prompt-execution: spec.outputSchema must be a JSON Schema object");
|
|
15667
15671
|
}
|
|
15672
|
+
if (spec.workspace !== void 0) {
|
|
15673
|
+
const ws = spec.workspace;
|
|
15674
|
+
if (typeof ws !== "object" || ws === null || Array.isArray(ws)) {
|
|
15675
|
+
throw new Error("prompt-execution: spec.workspace must be an object when present");
|
|
15676
|
+
}
|
|
15677
|
+
const namespaces = Object.entries(ws);
|
|
15678
|
+
if (namespaces.length === 0) {
|
|
15679
|
+
throw new Error("prompt-execution: spec.workspace must carry at least one namespace");
|
|
15680
|
+
}
|
|
15681
|
+
for (const [name, value] of namespaces) {
|
|
15682
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
15683
|
+
throw new Error(`prompt-execution: spec.workspace.${name} must be an object`);
|
|
15684
|
+
}
|
|
15685
|
+
}
|
|
15686
|
+
}
|
|
15668
15687
|
return spec;
|
|
15669
15688
|
}
|
|
15670
15689
|
function renderPromptExecution(inputs) {
|
|
@@ -15876,6 +15895,8 @@ var require_prompt_execution2 = __commonJS({
|
|
|
15876
15895
|
outputLabel: options.output.label,
|
|
15877
15896
|
outputSchema: options.output.schema
|
|
15878
15897
|
};
|
|
15898
|
+
if (options.workspace)
|
|
15899
|
+
spec.workspace = options.workspace;
|
|
15879
15900
|
if (options.providerHints)
|
|
15880
15901
|
spec.providerHints = options.providerHints;
|
|
15881
15902
|
if (options.costCapHint)
|
|
@@ -18418,6 +18439,17 @@ var require_client2 = __commonJS({
|
|
|
18418
18439
|
async getManagedSifterStatus(userId) {
|
|
18419
18440
|
return this.request("GET", `/api/users/${encodeURIComponent(userId)}/managed-sifter-status`);
|
|
18420
18441
|
}
|
|
18442
|
+
// ── GitHub OAuth token ──────────────────────────────────────────
|
|
18443
|
+
/**
|
|
18444
|
+
* Reveal a user's decrypted GitHub OAuth token for a service-to-service
|
|
18445
|
+
* call on their behalf (e.g. the hosted Sifter cloning the repo under
|
|
18446
|
+
* review). Service-token only; requires `keep:oauth-tokens:read`. Treat
|
|
18447
|
+
* the returned token as a single-use, in-memory secret. Throws (404 →
|
|
18448
|
+
* NotFound) when the user has not connected GitHub.
|
|
18449
|
+
*/
|
|
18450
|
+
async getUserGithubToken(userId) {
|
|
18451
|
+
return this.request("GET", `/api/users/${encodeURIComponent(userId)}/github-token`);
|
|
18452
|
+
}
|
|
18421
18453
|
// ── HTTP ────────────────────────────────────────────────────────
|
|
18422
18454
|
async request(method, path, body) {
|
|
18423
18455
|
const url = `${this.baseUrl}${path}`;
|
|
@@ -18516,7 +18548,15 @@ var require_agent_token = __commonJS({
|
|
|
18516
18548
|
"../../packages/keep/dist/agent-token.js"(exports2) {
|
|
18517
18549
|
"use strict";
|
|
18518
18550
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18519
|
-
exports2.AgentTokenManager = void 0;
|
|
18551
|
+
exports2.AgentTokenManager = exports2.AgentCredentialRevokedError = void 0;
|
|
18552
|
+
var AgentCredentialRevokedError2 = class extends Error {
|
|
18553
|
+
code = "PAIRING_REVOKED";
|
|
18554
|
+
constructor(message) {
|
|
18555
|
+
super(message);
|
|
18556
|
+
this.name = "AgentCredentialRevokedError";
|
|
18557
|
+
}
|
|
18558
|
+
};
|
|
18559
|
+
exports2.AgentCredentialRevokedError = AgentCredentialRevokedError2;
|
|
18520
18560
|
var AgentTokenManager = class {
|
|
18521
18561
|
token = null;
|
|
18522
18562
|
expiresAt = 0;
|
|
@@ -18563,6 +18603,9 @@ var require_agent_token = __commonJS({
|
|
|
18563
18603
|
const json = await response.json();
|
|
18564
18604
|
if (!response.ok || !json.success) {
|
|
18565
18605
|
const msg = json.error?.message ?? `Agent token exchange failed with status ${response.status}`;
|
|
18606
|
+
if (response.status === 401) {
|
|
18607
|
+
throw new AgentCredentialRevokedError2(`[AgentTokenManager] Keep refused this agent's credentials (401): the pairing was revoked or the secret rotated. Re-pair to continue.`);
|
|
18608
|
+
}
|
|
18566
18609
|
throw new Error(`[AgentTokenManager] ${msg}`);
|
|
18567
18610
|
}
|
|
18568
18611
|
this.token = json.data.token;
|
|
@@ -18579,7 +18622,7 @@ var require_dist4 = __commonJS({
|
|
|
18579
18622
|
"../../packages/keep/dist/index.js"(exports2) {
|
|
18580
18623
|
"use strict";
|
|
18581
18624
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18582
|
-
exports2.AgentTokenManager = exports2.ServiceTokenManager = exports2.KeepClient = void 0;
|
|
18625
|
+
exports2.AgentCredentialRevokedError = exports2.AgentTokenManager = exports2.ServiceTokenManager = exports2.KeepClient = void 0;
|
|
18583
18626
|
var client_1 = require_client2();
|
|
18584
18627
|
Object.defineProperty(exports2, "KeepClient", { enumerable: true, get: function() {
|
|
18585
18628
|
return client_1.KeepClient;
|
|
@@ -18592,6 +18635,9 @@ var require_dist4 = __commonJS({
|
|
|
18592
18635
|
Object.defineProperty(exports2, "AgentTokenManager", { enumerable: true, get: function() {
|
|
18593
18636
|
return agent_token_1.AgentTokenManager;
|
|
18594
18637
|
} });
|
|
18638
|
+
Object.defineProperty(exports2, "AgentCredentialRevokedError", { enumerable: true, get: function() {
|
|
18639
|
+
return agent_token_1.AgentCredentialRevokedError;
|
|
18640
|
+
} });
|
|
18595
18641
|
}
|
|
18596
18642
|
});
|
|
18597
18643
|
|
|
@@ -18728,15 +18774,415 @@ var require_validate_output = __commonJS({
|
|
|
18728
18774
|
}
|
|
18729
18775
|
});
|
|
18730
18776
|
|
|
18777
|
+
// ../../packages/shuttle/dist/executors/workspace.js
|
|
18778
|
+
var require_workspace = __commonJS({
|
|
18779
|
+
"../../packages/shuttle/dist/executors/workspace.js"(exports2) {
|
|
18780
|
+
"use strict";
|
|
18781
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
18782
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
18783
|
+
};
|
|
18784
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18785
|
+
exports2.prepareWorkspace = prepareWorkspace;
|
|
18786
|
+
exports2.buildGitAuthEnv = buildGitAuthEnv;
|
|
18787
|
+
var child_process_1 = require("child_process");
|
|
18788
|
+
var fs_1 = require("fs");
|
|
18789
|
+
var os_1 = __importDefault(require("os"));
|
|
18790
|
+
var path_1 = __importDefault(require("path"));
|
|
18791
|
+
var DEFAULT_GIT_TIMEOUT_MS = 12e4;
|
|
18792
|
+
var DEFAULT_GIT_PROTOCOLS = "https:ssh:git";
|
|
18793
|
+
async function prepareWorkspace(repoDir, workspace, options = {}) {
|
|
18794
|
+
const resolved = readGitWorkspace(workspace);
|
|
18795
|
+
if ("warning" in resolved) {
|
|
18796
|
+
return degradedWorkspace(resolved.warning);
|
|
18797
|
+
}
|
|
18798
|
+
const gitWorkspace = resolved.git;
|
|
18799
|
+
const timeoutMs = options.commandTimeoutMs ?? DEFAULT_GIT_TIMEOUT_MS;
|
|
18800
|
+
const allowedProtocols = options.allowedProtocols ?? DEFAULT_GIT_PROTOCOLS;
|
|
18801
|
+
const signal = options.signal;
|
|
18802
|
+
const authEnv = options.gitToken !== void 0 ? buildGitAuthEnv(gitWorkspace.remote, options.gitToken) : void 0;
|
|
18803
|
+
const git = (args, cwd) => runGit(args, cwd, { timeoutMs, signal, allowedProtocols, extraEnv: authEnv });
|
|
18804
|
+
const gitCleanup = (args, cwd) => runGit(args, cwd, { timeoutMs, allowedProtocols });
|
|
18805
|
+
const hasCommit = (dir) => git(["cat-file", "-e", `${gitWorkspace.commit}^{commit}`], dir).then((r) => r.code === 0);
|
|
18806
|
+
const fetchCommit = async (dir, opts) => {
|
|
18807
|
+
if (await hasCommit(dir))
|
|
18808
|
+
return [];
|
|
18809
|
+
const depth = opts.shallow ? ["--depth=1"] : [];
|
|
18810
|
+
const attempts = [];
|
|
18811
|
+
for (const ref of gitWorkspace.fetchRefs ?? []) {
|
|
18812
|
+
if (opts.includeOrigin) {
|
|
18813
|
+
attempts.push({ source: `origin ${ref}`, args: ["fetch", "--no-tags", ...depth, "origin", ref] });
|
|
18814
|
+
}
|
|
18815
|
+
attempts.push({
|
|
18816
|
+
source: `${gitWorkspace.remote} ${ref}`,
|
|
18817
|
+
args: ["fetch", "--no-tags", ...depth, gitWorkspace.remote, ref]
|
|
18818
|
+
});
|
|
18819
|
+
}
|
|
18820
|
+
attempts.push({
|
|
18821
|
+
source: `${gitWorkspace.remote} commit`,
|
|
18822
|
+
args: ["fetch", "--no-tags", ...depth, gitWorkspace.remote, gitWorkspace.commit]
|
|
18823
|
+
});
|
|
18824
|
+
const failures2 = [];
|
|
18825
|
+
for (const { source, args } of attempts) {
|
|
18826
|
+
const result = await git(args, dir);
|
|
18827
|
+
if (result.code !== 0) {
|
|
18828
|
+
failures2.push(`${source}: ${firstLine(result.stderr)}`);
|
|
18829
|
+
continue;
|
|
18830
|
+
}
|
|
18831
|
+
if (await hasCommit(dir))
|
|
18832
|
+
return [];
|
|
18833
|
+
failures2.push(`${source}: fetch succeeded but ${gitWorkspace.commit} is still absent`);
|
|
18834
|
+
}
|
|
18835
|
+
return failures2;
|
|
18836
|
+
};
|
|
18837
|
+
const notMaterialized = (failures2) => `could not materialize ${gitWorkspace.commit} from ${gitWorkspace.remote}` + (failures2.length > 0 ? ` (${failures2.join("; ")})` : "");
|
|
18838
|
+
const isRepo = (await git(["rev-parse", "--git-dir"], repoDir)).code === 0;
|
|
18839
|
+
if (isRepo) {
|
|
18840
|
+
const failures2 = await fetchCommit(repoDir, { includeOrigin: true, shallow: false });
|
|
18841
|
+
if (!await hasCommit(repoDir))
|
|
18842
|
+
return degradedWorkspace(notMaterialized(failures2));
|
|
18843
|
+
const tempRoot2 = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "shuttle-ws-"));
|
|
18844
|
+
const worktreeDir = path_1.default.join(tempRoot2, "checkout");
|
|
18845
|
+
const add = await git(["worktree", "add", "--detach", worktreeDir, gitWorkspace.commit], repoDir);
|
|
18846
|
+
if (add.code !== 0) {
|
|
18847
|
+
await fs_1.promises.rm(tempRoot2, { recursive: true, force: true }).catch(() => {
|
|
18848
|
+
});
|
|
18849
|
+
return degradedWorkspace(`git worktree add failed: ${firstLine(add.stderr)}`);
|
|
18850
|
+
}
|
|
18851
|
+
return {
|
|
18852
|
+
cwd: worktreeDir,
|
|
18853
|
+
materialized: true,
|
|
18854
|
+
cleanup: async () => {
|
|
18855
|
+
await gitCleanup(["worktree", "remove", "--force", worktreeDir], repoDir);
|
|
18856
|
+
await gitCleanup(["worktree", "prune"], repoDir);
|
|
18857
|
+
await fs_1.promises.rm(tempRoot2, { recursive: true, force: true }).catch(() => {
|
|
18858
|
+
});
|
|
18859
|
+
}
|
|
18860
|
+
};
|
|
18861
|
+
}
|
|
18862
|
+
const tempRoot = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "shuttle-ws-"));
|
|
18863
|
+
const checkoutDir = path_1.default.join(tempRoot, "checkout");
|
|
18864
|
+
const removeTempRoot = async () => {
|
|
18865
|
+
await fs_1.promises.rm(tempRoot, { recursive: true, force: true }).catch(() => {
|
|
18866
|
+
});
|
|
18867
|
+
};
|
|
18868
|
+
await fs_1.promises.mkdir(checkoutDir);
|
|
18869
|
+
const init = await git(["init", "-q"], checkoutDir);
|
|
18870
|
+
if (init.code !== 0) {
|
|
18871
|
+
await removeTempRoot();
|
|
18872
|
+
return degradedWorkspace(`git init failed: ${firstLine(init.stderr)}`);
|
|
18873
|
+
}
|
|
18874
|
+
const failures = await fetchCommit(checkoutDir, { includeOrigin: false, shallow: true });
|
|
18875
|
+
if (!await hasCommit(checkoutDir)) {
|
|
18876
|
+
await removeTempRoot();
|
|
18877
|
+
return degradedWorkspace(notMaterialized(failures));
|
|
18878
|
+
}
|
|
18879
|
+
const checkout = await git(["checkout", "--detach", gitWorkspace.commit], checkoutDir);
|
|
18880
|
+
if (checkout.code !== 0) {
|
|
18881
|
+
await removeTempRoot();
|
|
18882
|
+
return degradedWorkspace(`git checkout failed: ${firstLine(checkout.stderr)}`);
|
|
18883
|
+
}
|
|
18884
|
+
return { cwd: checkoutDir, materialized: true, cleanup: removeTempRoot };
|
|
18885
|
+
}
|
|
18886
|
+
function buildGitAuthEnv(remote, token) {
|
|
18887
|
+
let origin;
|
|
18888
|
+
try {
|
|
18889
|
+
const url = new URL(remote);
|
|
18890
|
+
if (url.protocol !== "https:")
|
|
18891
|
+
return void 0;
|
|
18892
|
+
origin = `${url.protocol}//${url.host}/`;
|
|
18893
|
+
} catch {
|
|
18894
|
+
return void 0;
|
|
18895
|
+
}
|
|
18896
|
+
const basic = Buffer.from(`x-access-token:${token}`).toString("base64");
|
|
18897
|
+
return {
|
|
18898
|
+
GIT_CONFIG_COUNT: "1",
|
|
18899
|
+
GIT_CONFIG_KEY_0: `http.${origin}.extraheader`,
|
|
18900
|
+
GIT_CONFIG_VALUE_0: `Authorization: Basic ${basic}`
|
|
18901
|
+
};
|
|
18902
|
+
}
|
|
18903
|
+
var COMMIT_PATTERN = /^[0-9a-f]{40}$/i;
|
|
18904
|
+
var REF_PATTERN = /^[A-Za-z0-9][A-Za-z0-9/_.@^~+-]*$/;
|
|
18905
|
+
function readGitWorkspace(workspace) {
|
|
18906
|
+
const git = workspace.git;
|
|
18907
|
+
if (git === void 0) {
|
|
18908
|
+
const offered = Object.keys(workspace).join(", ") || "(none)";
|
|
18909
|
+
return {
|
|
18910
|
+
warning: `workspace carries no namespace this executor understands (offered: ${offered}; supported: git)`
|
|
18911
|
+
};
|
|
18912
|
+
}
|
|
18913
|
+
if (typeof git.remote !== "string" || git.remote.length === 0 || git.remote.startsWith("-")) {
|
|
18914
|
+
return { warning: "workspace.git.remote must be a non-empty remote URL or path" };
|
|
18915
|
+
}
|
|
18916
|
+
if (typeof git.commit !== "string" || !COMMIT_PATTERN.test(git.commit)) {
|
|
18917
|
+
return { warning: "workspace.git.commit must be a full 40-char commit SHA" };
|
|
18918
|
+
}
|
|
18919
|
+
if (git.fetchRefs !== void 0) {
|
|
18920
|
+
if (!Array.isArray(git.fetchRefs) || !git.fetchRefs.every((r) => typeof r === "string")) {
|
|
18921
|
+
return { warning: "workspace.git.fetchRefs must be an array of ref strings" };
|
|
18922
|
+
}
|
|
18923
|
+
const bad = git.fetchRefs.find((r) => !REF_PATTERN.test(r));
|
|
18924
|
+
if (bad !== void 0) {
|
|
18925
|
+
return { warning: `workspace.git.fetchRefs contains an invalid ref: ${JSON.stringify(bad)}` };
|
|
18926
|
+
}
|
|
18927
|
+
}
|
|
18928
|
+
return { git };
|
|
18929
|
+
}
|
|
18930
|
+
async function degradedWorkspace(warning) {
|
|
18931
|
+
const scratchDir = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "shuttle-ws-degraded-"));
|
|
18932
|
+
return {
|
|
18933
|
+
cwd: scratchDir,
|
|
18934
|
+
materialized: false,
|
|
18935
|
+
warning,
|
|
18936
|
+
cleanup: async () => {
|
|
18937
|
+
await fs_1.promises.rm(scratchDir, { recursive: true, force: true }).catch(() => {
|
|
18938
|
+
});
|
|
18939
|
+
}
|
|
18940
|
+
};
|
|
18941
|
+
}
|
|
18942
|
+
function runGit(args, cwd, opts) {
|
|
18943
|
+
return new Promise((resolve) => {
|
|
18944
|
+
const child = (0, child_process_1.spawn)("git", args, {
|
|
18945
|
+
cwd,
|
|
18946
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18947
|
+
// `extraEnv` (e.g. the credential extraheader) goes first so the two
|
|
18948
|
+
// security-critical vars below always win and can't be overridden.
|
|
18949
|
+
env: {
|
|
18950
|
+
...process.env,
|
|
18951
|
+
...opts.extraEnv ?? {},
|
|
18952
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
18953
|
+
// Transport allowlist. `remote` passes readGitWorkspace as any
|
|
18954
|
+
// non-empty non-`-` string, which still admits `ext::sh -c …` and
|
|
18955
|
+
// `fd::` remotes that execute arbitrary commands on this host.
|
|
18956
|
+
// Restricting to the transports we actually use turns those into a
|
|
18957
|
+
// fast failure (→ degrade) rather than RCE across the boundary this
|
|
18958
|
+
// executor is meant to defend.
|
|
18959
|
+
GIT_ALLOW_PROTOCOL: opts.allowedProtocols
|
|
18960
|
+
}
|
|
18961
|
+
});
|
|
18962
|
+
const stdout = [];
|
|
18963
|
+
const stderr = [];
|
|
18964
|
+
let settled = false;
|
|
18965
|
+
const settle = (result) => {
|
|
18966
|
+
if (settled)
|
|
18967
|
+
return;
|
|
18968
|
+
settled = true;
|
|
18969
|
+
clearTimeout(timer);
|
|
18970
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
18971
|
+
resolve(result);
|
|
18972
|
+
};
|
|
18973
|
+
const killChild = () => {
|
|
18974
|
+
child.kill("SIGTERM");
|
|
18975
|
+
const escalate = setTimeout(() => child.kill("SIGKILL"), 5e3);
|
|
18976
|
+
escalate.unref();
|
|
18977
|
+
child.once("close", () => clearTimeout(escalate));
|
|
18978
|
+
};
|
|
18979
|
+
const timer = setTimeout(() => {
|
|
18980
|
+
killChild();
|
|
18981
|
+
settle({
|
|
18982
|
+
code: -1,
|
|
18983
|
+
stdout: Buffer.concat(stdout).toString("utf-8"),
|
|
18984
|
+
stderr: `git ${args[0]} timed out after ${opts.timeoutMs}ms`
|
|
18985
|
+
});
|
|
18986
|
+
}, opts.timeoutMs);
|
|
18987
|
+
const onAbort = () => {
|
|
18988
|
+
killChild();
|
|
18989
|
+
settle({
|
|
18990
|
+
code: -1,
|
|
18991
|
+
stdout: Buffer.concat(stdout).toString("utf-8"),
|
|
18992
|
+
stderr: "aborted"
|
|
18993
|
+
});
|
|
18994
|
+
};
|
|
18995
|
+
if (opts.signal?.aborted) {
|
|
18996
|
+
onAbort();
|
|
18997
|
+
return;
|
|
18998
|
+
}
|
|
18999
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
19000
|
+
child.stdout.on("data", (c) => stdout.push(c));
|
|
19001
|
+
child.stderr.on("data", (c) => stderr.push(c));
|
|
19002
|
+
child.on("error", (err) => settle({ code: -1, stdout: "", stderr: err.message }));
|
|
19003
|
+
child.on("close", (code) => settle({
|
|
19004
|
+
code: code ?? -1,
|
|
19005
|
+
stdout: Buffer.concat(stdout).toString("utf-8"),
|
|
19006
|
+
stderr: Buffer.concat(stderr).toString("utf-8")
|
|
19007
|
+
}));
|
|
19008
|
+
});
|
|
19009
|
+
}
|
|
19010
|
+
function firstLine(text) {
|
|
19011
|
+
const line = text.trim().split("\n", 1)[0];
|
|
19012
|
+
return line.length > 0 ? line : "(no output)";
|
|
19013
|
+
}
|
|
19014
|
+
}
|
|
19015
|
+
});
|
|
19016
|
+
|
|
19017
|
+
// ../../packages/shuttle/dist/executors/overlay.js
|
|
19018
|
+
var require_overlay = __commonJS({
|
|
19019
|
+
"../../packages/shuttle/dist/executors/overlay.js"(exports2) {
|
|
19020
|
+
"use strict";
|
|
19021
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
19022
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
19023
|
+
};
|
|
19024
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19025
|
+
exports2.applyOverlay = applyOverlay;
|
|
19026
|
+
var fs_1 = require("fs");
|
|
19027
|
+
var path_1 = __importDefault(require("path"));
|
|
19028
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
19029
|
+
var DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
|
|
19030
|
+
async function applyOverlay(runDir, overlay, options) {
|
|
19031
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
19032
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
19033
|
+
const allowed = new Set(options.allowedHosts.map(normalizeOrigin).filter((o) => !!o));
|
|
19034
|
+
const rootReal = await fs_1.promises.realpath(runDir);
|
|
19035
|
+
const result = { written: 0, skipped: [] };
|
|
19036
|
+
const files = Array.isArray(overlay.files) ? overlay.files : [];
|
|
19037
|
+
for (const file of files) {
|
|
19038
|
+
const skip = (reason) => result.skipped.push({ path: String(file?.path ?? "?"), reason });
|
|
19039
|
+
if (typeof file?.path !== "string" || typeof file?.url !== "string") {
|
|
19040
|
+
skip("malformed overlay entry (path/url must be strings)");
|
|
19041
|
+
continue;
|
|
19042
|
+
}
|
|
19043
|
+
const dest = safeDestination(rootReal, file.path);
|
|
19044
|
+
if (!dest) {
|
|
19045
|
+
skip("path escapes the workspace root");
|
|
19046
|
+
continue;
|
|
19047
|
+
}
|
|
19048
|
+
let origin;
|
|
19049
|
+
try {
|
|
19050
|
+
const u = new URL(file.url);
|
|
19051
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") {
|
|
19052
|
+
skip(`unsupported url protocol ${u.protocol}`);
|
|
19053
|
+
continue;
|
|
19054
|
+
}
|
|
19055
|
+
origin = normalizeOrigin(u.origin);
|
|
19056
|
+
} catch {
|
|
19057
|
+
skip("invalid url");
|
|
19058
|
+
continue;
|
|
19059
|
+
}
|
|
19060
|
+
if (!origin || !allowed.has(origin)) {
|
|
19061
|
+
skip(`origin not in the overlay allowlist (${origin})`);
|
|
19062
|
+
continue;
|
|
19063
|
+
}
|
|
19064
|
+
try {
|
|
19065
|
+
const content = await fetchCapped(file.url, { timeoutMs, maxBytes, signal: options.signal });
|
|
19066
|
+
await fs_1.promises.mkdir(path_1.default.dirname(dest), { recursive: true });
|
|
19067
|
+
await fs_1.promises.writeFile(dest, content);
|
|
19068
|
+
result.written += 1;
|
|
19069
|
+
} catch (err) {
|
|
19070
|
+
skip(err instanceof Error ? err.message : String(err));
|
|
19071
|
+
}
|
|
19072
|
+
}
|
|
19073
|
+
return result;
|
|
19074
|
+
}
|
|
19075
|
+
function safeDestination(rootReal, relPath) {
|
|
19076
|
+
if (relPath.length === 0 || path_1.default.isAbsolute(relPath))
|
|
19077
|
+
return void 0;
|
|
19078
|
+
const resolved = path_1.default.resolve(rootReal, relPath);
|
|
19079
|
+
const rootWithSep = rootReal.endsWith(path_1.default.sep) ? rootReal : rootReal + path_1.default.sep;
|
|
19080
|
+
if (resolved !== rootReal && !resolved.startsWith(rootWithSep))
|
|
19081
|
+
return void 0;
|
|
19082
|
+
if (relPath.split(/[/\\]/).includes(".."))
|
|
19083
|
+
return void 0;
|
|
19084
|
+
return resolved;
|
|
19085
|
+
}
|
|
19086
|
+
function normalizeOrigin(value) {
|
|
19087
|
+
try {
|
|
19088
|
+
const u = new URL(value.includes("://") ? value : `https://${value}`);
|
|
19089
|
+
return u.origin;
|
|
19090
|
+
} catch {
|
|
19091
|
+
return void 0;
|
|
19092
|
+
}
|
|
19093
|
+
}
|
|
19094
|
+
async function fetchCapped(url, opts) {
|
|
19095
|
+
const controller = new AbortController();
|
|
19096
|
+
const onParentAbort = () => controller.abort();
|
|
19097
|
+
opts.signal?.addEventListener("abort", onParentAbort, { once: true });
|
|
19098
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs);
|
|
19099
|
+
try {
|
|
19100
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
19101
|
+
if (!response.ok) {
|
|
19102
|
+
throw new Error(`overlay fetch returned HTTP ${response.status}`);
|
|
19103
|
+
}
|
|
19104
|
+
if (!response.body) {
|
|
19105
|
+
const buf = Buffer.from(await response.arrayBuffer());
|
|
19106
|
+
if (buf.byteLength > opts.maxBytes)
|
|
19107
|
+
throw new Error("overlay file exceeds size cap");
|
|
19108
|
+
return buf;
|
|
19109
|
+
}
|
|
19110
|
+
const chunks = [];
|
|
19111
|
+
let total = 0;
|
|
19112
|
+
for await (const chunk of response.body) {
|
|
19113
|
+
const buf = Buffer.from(chunk);
|
|
19114
|
+
total += buf.byteLength;
|
|
19115
|
+
if (total > opts.maxBytes)
|
|
19116
|
+
throw new Error("overlay file exceeds size cap");
|
|
19117
|
+
chunks.push(buf);
|
|
19118
|
+
}
|
|
19119
|
+
return Buffer.concat(chunks);
|
|
19120
|
+
} finally {
|
|
19121
|
+
clearTimeout(timer);
|
|
19122
|
+
opts.signal?.removeEventListener("abort", onParentAbort);
|
|
19123
|
+
}
|
|
19124
|
+
}
|
|
19125
|
+
}
|
|
19126
|
+
});
|
|
19127
|
+
|
|
19128
|
+
// ../../packages/shuttle/dist/config/env-ref.js
|
|
19129
|
+
var require_env_ref = __commonJS({
|
|
19130
|
+
"../../packages/shuttle/dist/config/env-ref.js"(exports2) {
|
|
19131
|
+
"use strict";
|
|
19132
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19133
|
+
exports2.resolveEnvRef = resolveEnvRef;
|
|
19134
|
+
exports2.isEnvRef = isEnvRef;
|
|
19135
|
+
exports2.resolveConfigStringOrEnv = resolveConfigStringOrEnv;
|
|
19136
|
+
var ENV_REF_PATTERN = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/;
|
|
19137
|
+
function resolveEnvRef(value, fieldName) {
|
|
19138
|
+
const match = ENV_REF_PATTERN.exec(value);
|
|
19139
|
+
if (!match)
|
|
19140
|
+
return value;
|
|
19141
|
+
const varName = match[1];
|
|
19142
|
+
const resolved = process.env[varName];
|
|
19143
|
+
if (resolved === void 0 || resolved === "") {
|
|
19144
|
+
const subject = fieldName ? `${fieldName} (${value})` : value;
|
|
19145
|
+
throw new Error(`Environment variable "${varName}" referenced by ${subject} is not set. Export it before starting the Sifter, or hard-code the value in the YAML.`);
|
|
19146
|
+
}
|
|
19147
|
+
return resolved;
|
|
19148
|
+
}
|
|
19149
|
+
function isEnvRef(value) {
|
|
19150
|
+
return ENV_REF_PATTERN.test(value);
|
|
19151
|
+
}
|
|
19152
|
+
function resolveConfigStringOrEnv(config, field, envFallback) {
|
|
19153
|
+
const raw = config[field];
|
|
19154
|
+
if (typeof raw === "string" && raw.length > 0) {
|
|
19155
|
+
return resolveEnvRef(raw, field);
|
|
19156
|
+
}
|
|
19157
|
+
const fromEnv = process.env[envFallback];
|
|
19158
|
+
if (fromEnv && fromEnv.length > 0) {
|
|
19159
|
+
return fromEnv;
|
|
19160
|
+
}
|
|
19161
|
+
throw new Error(`dynamic-key mode requires "${field}" in config or the ${envFallback} env var`);
|
|
19162
|
+
}
|
|
19163
|
+
}
|
|
19164
|
+
});
|
|
19165
|
+
|
|
18731
19166
|
// ../../packages/shuttle/dist/executors/claude-code.js
|
|
18732
19167
|
var require_claude_code = __commonJS({
|
|
18733
19168
|
"../../packages/shuttle/dist/executors/claude-code.js"(exports2) {
|
|
18734
19169
|
"use strict";
|
|
19170
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
19171
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
19172
|
+
};
|
|
18735
19173
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18736
19174
|
exports2.createClaudeCodeExecutor = createClaudeCodeExecutor;
|
|
19175
|
+
exports2.extractJsonObject = extractJsonObject;
|
|
18737
19176
|
var child_process_1 = require("child_process");
|
|
19177
|
+
var fs_1 = require("fs");
|
|
19178
|
+
var os_1 = __importDefault(require("os"));
|
|
19179
|
+
var path_1 = __importDefault(require("path"));
|
|
18738
19180
|
var loom_1 = require_dist3();
|
|
19181
|
+
var keep_1 = require_dist4();
|
|
18739
19182
|
var validate_output_1 = require_validate_output();
|
|
19183
|
+
var workspace_1 = require_workspace();
|
|
19184
|
+
var overlay_1 = require_overlay();
|
|
19185
|
+
var env_ref_1 = require_env_ref();
|
|
18740
19186
|
var apply_1 = require_apply();
|
|
18741
19187
|
var DEFAULT_TIMEOUT = 6e5;
|
|
18742
19188
|
var ClaudeCodeExecutor = class {
|
|
@@ -18754,13 +19200,77 @@ var require_claude_code = __commonJS({
|
|
|
18754
19200
|
outputTokens: spec.costCapHint?.estimatedOutputTokens
|
|
18755
19201
|
};
|
|
18756
19202
|
}
|
|
18757
|
-
execute(dispatch, signal) {
|
|
19203
|
+
async execute(dispatch, signal) {
|
|
18758
19204
|
const { prompt, spec } = (0, loom_1.renderPromptExecution)(dispatch.inputs);
|
|
19205
|
+
let credentials = null;
|
|
19206
|
+
if (this.config.resolveCredentials) {
|
|
19207
|
+
const hints = (0, loom_1.pickProviderHints)(spec, "anthropic-api");
|
|
19208
|
+
const customerId = typeof hints.customerId === "string" ? hints.customerId : void 0;
|
|
19209
|
+
if (!customerId) {
|
|
19210
|
+
throw new Error("claude-code dynamic-key mode requires providerHints.anthropicApi.customerId on every job");
|
|
19211
|
+
}
|
|
19212
|
+
credentials = await this.config.resolveCredentials({
|
|
19213
|
+
customerId,
|
|
19214
|
+
needGithubToken: !!spec.workspace
|
|
19215
|
+
});
|
|
19216
|
+
}
|
|
19217
|
+
let jobScratch = null;
|
|
19218
|
+
let configDir;
|
|
19219
|
+
if (credentials) {
|
|
19220
|
+
jobScratch = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "shuttle-cc-"));
|
|
19221
|
+
configDir = path_1.default.join(jobScratch, "claude-config");
|
|
19222
|
+
await fs_1.promises.mkdir(configDir);
|
|
19223
|
+
}
|
|
19224
|
+
let workspace = null;
|
|
19225
|
+
let overlayResult = null;
|
|
19226
|
+
try {
|
|
19227
|
+
if (spec.workspace) {
|
|
19228
|
+
workspace = await (0, workspace_1.prepareWorkspace)(this.config.cwd, spec.workspace, {
|
|
19229
|
+
signal,
|
|
19230
|
+
...credentials?.githubToken ? { gitToken: credentials.githubToken } : {}
|
|
19231
|
+
});
|
|
19232
|
+
if (spec.workspace.overlay) {
|
|
19233
|
+
overlayResult = await (0, overlay_1.applyOverlay)(workspace.cwd, spec.workspace.overlay, {
|
|
19234
|
+
allowedHosts: this.config.overlayAllowedHosts ?? [],
|
|
19235
|
+
signal
|
|
19236
|
+
});
|
|
19237
|
+
}
|
|
19238
|
+
}
|
|
19239
|
+
const childEnv = buildChildEnv(credentials, configDir);
|
|
19240
|
+
const claudeHints = (0, loom_1.pickProviderHints)(spec, "claude-code");
|
|
19241
|
+
const hintedModel = typeof claudeHints.model === "string" && claudeHints.model.length > 0 ? claudeHints.model : void 0;
|
|
19242
|
+
const response = await this.runClaude(prompt, spec, workspace?.cwd ?? this.config.cwd, childEnv, signal, hintedModel);
|
|
19243
|
+
if (workspace) {
|
|
19244
|
+
response.outputs.push({
|
|
19245
|
+
label: "workspace",
|
|
19246
|
+
content: {
|
|
19247
|
+
materialized: workspace.materialized,
|
|
19248
|
+
...workspace.warning ? { warning: workspace.warning } : {},
|
|
19249
|
+
...overlayResult ? {
|
|
19250
|
+
overlay: {
|
|
19251
|
+
written: overlayResult.written,
|
|
19252
|
+
...overlayResult.skipped.length > 0 ? { skipped: overlayResult.skipped } : {}
|
|
19253
|
+
}
|
|
19254
|
+
} : {}
|
|
19255
|
+
}
|
|
19256
|
+
});
|
|
19257
|
+
}
|
|
19258
|
+
return response;
|
|
19259
|
+
} finally {
|
|
19260
|
+
if (workspace)
|
|
19261
|
+
await workspace.cleanup();
|
|
19262
|
+
if (jobScratch)
|
|
19263
|
+
await fs_1.promises.rm(jobScratch, { recursive: true, force: true }).catch(() => {
|
|
19264
|
+
});
|
|
19265
|
+
}
|
|
19266
|
+
}
|
|
19267
|
+
runClaude(prompt, spec, cwd, childEnv, signal, modelOverride) {
|
|
18759
19268
|
const outputLabel = spec.outputLabel;
|
|
18760
19269
|
return new Promise((resolve, reject) => {
|
|
18761
19270
|
const args = ["--print"];
|
|
18762
|
-
|
|
18763
|
-
|
|
19271
|
+
const model = modelOverride ?? this.config.model;
|
|
19272
|
+
if (model)
|
|
19273
|
+
args.push("--model", model);
|
|
18764
19274
|
if (this.config.maxTurns !== void 0)
|
|
18765
19275
|
args.push("--max-turns", String(this.config.maxTurns));
|
|
18766
19276
|
if (this.config.allowedTools && this.config.allowedTools.length > 0) {
|
|
@@ -18769,8 +19279,11 @@ var require_claude_code = __commonJS({
|
|
|
18769
19279
|
let child;
|
|
18770
19280
|
try {
|
|
18771
19281
|
child = (0, child_process_1.spawn)("claude", args, {
|
|
18772
|
-
cwd
|
|
18773
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
19282
|
+
cwd,
|
|
19283
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
19284
|
+
// Undefined in local mode → inherit the ambient env (and its
|
|
19285
|
+
// `~/.claude` login) unchanged.
|
|
19286
|
+
...childEnv ? { env: childEnv } : {}
|
|
18774
19287
|
});
|
|
18775
19288
|
} catch (err) {
|
|
18776
19289
|
reject(err);
|
|
@@ -18814,7 +19327,7 @@ var require_claude_code = __commonJS({
|
|
|
18814
19327
|
const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
|
|
18815
19328
|
if (code === 0) {
|
|
18816
19329
|
try {
|
|
18817
|
-
const parsed =
|
|
19330
|
+
const parsed = extractJsonObject(stdout);
|
|
18818
19331
|
(0, validate_output_1.assertOutputValid)(spec, parsed);
|
|
18819
19332
|
settle(() => resolve({
|
|
18820
19333
|
outputs: [{ label: outputLabel, content: parsed ?? { raw: stdout } }],
|
|
@@ -18832,7 +19345,7 @@ var require_claude_code = __commonJS({
|
|
|
18832
19345
|
});
|
|
18833
19346
|
}
|
|
18834
19347
|
};
|
|
18835
|
-
function createClaudeCodeExecutor(config, capabilityId = "claude-code") {
|
|
19348
|
+
function createClaudeCodeExecutor(config, capabilityId = "claude-code", deps = {}) {
|
|
18836
19349
|
const cwdRaw = config.cwd;
|
|
18837
19350
|
if (typeof cwdRaw !== "string" || cwdRaw.length === 0) {
|
|
18838
19351
|
throw new Error('ClaudeCodeExecutor requires a non-empty "cwd" string in config');
|
|
@@ -18862,12 +19375,108 @@ var require_claude_code = __commonJS({
|
|
|
18862
19375
|
}
|
|
18863
19376
|
validated.timeout = config.timeout;
|
|
18864
19377
|
}
|
|
19378
|
+
if (config.apiKeyFrom !== void 0) {
|
|
19379
|
+
validated.resolveCredentials = buildCredentialResolver(config, deps);
|
|
19380
|
+
}
|
|
19381
|
+
if (config.overlayAllowedHosts !== void 0) {
|
|
19382
|
+
if (!Array.isArray(config.overlayAllowedHosts) || !config.overlayAllowedHosts.every((h) => typeof h === "string")) {
|
|
19383
|
+
throw new Error('"overlayAllowedHosts" must be an array of strings');
|
|
19384
|
+
}
|
|
19385
|
+
validated.overlayAllowedHosts = config.overlayAllowedHosts;
|
|
19386
|
+
}
|
|
18865
19387
|
return new ClaudeCodeExecutor(validated);
|
|
18866
19388
|
}
|
|
18867
|
-
function
|
|
19389
|
+
function buildChildEnv(credentials, configDir) {
|
|
19390
|
+
if (!credentials && !configDir)
|
|
19391
|
+
return void 0;
|
|
19392
|
+
return {
|
|
19393
|
+
...process.env,
|
|
19394
|
+
...credentials ? { ANTHROPIC_API_KEY: credentials.anthropicApiKey } : {},
|
|
19395
|
+
...configDir ? { CLAUDE_CONFIG_DIR: configDir } : {}
|
|
19396
|
+
};
|
|
19397
|
+
}
|
|
19398
|
+
function buildCredentialResolver(config, deps) {
|
|
19399
|
+
if (config.apiKeyFrom !== "job-metadata") {
|
|
19400
|
+
throw new Error(`Unsupported "apiKeyFrom" value: ${JSON.stringify(config.apiKeyFrom)}. Only "job-metadata" is supported.`);
|
|
19401
|
+
}
|
|
19402
|
+
const keepApiUrl = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepApiUrl", "KEEP_API_URL");
|
|
19403
|
+
const clientId = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepClientId", "KEEP_CLIENT_ID");
|
|
19404
|
+
const clientSecret = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepClientSecret", "KEEP_CLIENT_SECRET");
|
|
19405
|
+
const stmFactory = deps.buildServiceTokenManager ?? ((cfg) => new keep_1.ServiceTokenManager(cfg));
|
|
19406
|
+
const stm = stmFactory({ keepApiUrl, clientId, clientSecret });
|
|
19407
|
+
const keepClientFactory = deps.buildKeepClient ?? ((cfg) => new keep_1.KeepClient(cfg));
|
|
19408
|
+
return async ({ customerId, needGithubToken }) => {
|
|
19409
|
+
const serviceToken = await stm.getToken();
|
|
19410
|
+
if (!serviceToken) {
|
|
19411
|
+
throw new Error("claude-code dynamic-key mode failed to exchange a Keep service token. Check KEEP_CLIENT_ID / KEEP_CLIENT_SECRET.");
|
|
19412
|
+
}
|
|
19413
|
+
const keep = keepClientFactory({ baseUrl: keepApiUrl, serviceToken });
|
|
19414
|
+
const reveal = await keep.revealManagedSifterKey(customerId);
|
|
19415
|
+
const credentials = { anthropicApiKey: reveal.apiKey };
|
|
19416
|
+
if (needGithubToken) {
|
|
19417
|
+
try {
|
|
19418
|
+
const gh = await keep.getUserGithubToken(customerId);
|
|
19419
|
+
credentials.githubToken = gh.access_token;
|
|
19420
|
+
} catch {
|
|
19421
|
+
}
|
|
19422
|
+
}
|
|
19423
|
+
return credentials;
|
|
19424
|
+
};
|
|
19425
|
+
}
|
|
19426
|
+
function extractJsonObject(s) {
|
|
19427
|
+
const exact = tryParseObject(s);
|
|
19428
|
+
if (exact)
|
|
19429
|
+
return exact;
|
|
19430
|
+
let last;
|
|
19431
|
+
let i = 0;
|
|
19432
|
+
while (i < s.length) {
|
|
19433
|
+
if (s[i] !== "{") {
|
|
19434
|
+
i++;
|
|
19435
|
+
continue;
|
|
19436
|
+
}
|
|
19437
|
+
let depth = 0;
|
|
19438
|
+
let inString = false;
|
|
19439
|
+
let escaped = false;
|
|
19440
|
+
let end = -1;
|
|
19441
|
+
for (let j = i; j < s.length; j++) {
|
|
19442
|
+
const ch = s[j];
|
|
19443
|
+
if (escaped) {
|
|
19444
|
+
escaped = false;
|
|
19445
|
+
continue;
|
|
19446
|
+
}
|
|
19447
|
+
if (ch === "\\") {
|
|
19448
|
+
escaped = inString;
|
|
19449
|
+
continue;
|
|
19450
|
+
}
|
|
19451
|
+
if (ch === '"') {
|
|
19452
|
+
inString = !inString;
|
|
19453
|
+
continue;
|
|
19454
|
+
}
|
|
19455
|
+
if (inString)
|
|
19456
|
+
continue;
|
|
19457
|
+
if (ch === "{")
|
|
19458
|
+
depth++;
|
|
19459
|
+
else if (ch === "}") {
|
|
19460
|
+
depth--;
|
|
19461
|
+
if (depth === 0) {
|
|
19462
|
+
end = j;
|
|
19463
|
+
break;
|
|
19464
|
+
}
|
|
19465
|
+
}
|
|
19466
|
+
}
|
|
19467
|
+
if (end === -1)
|
|
19468
|
+
break;
|
|
19469
|
+
const candidate = tryParseObject(s.slice(i, end + 1));
|
|
19470
|
+
if (candidate)
|
|
19471
|
+
last = candidate;
|
|
19472
|
+
i = end + 1;
|
|
19473
|
+
}
|
|
19474
|
+
return last;
|
|
19475
|
+
}
|
|
19476
|
+
function tryParseObject(s) {
|
|
18868
19477
|
try {
|
|
18869
19478
|
const parsed = JSON.parse(s);
|
|
18870
|
-
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
19479
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : void 0;
|
|
18871
19480
|
} catch {
|
|
18872
19481
|
return void 0;
|
|
18873
19482
|
}
|
|
@@ -19028,32 +19637,6 @@ var require_http_api = __commonJS({
|
|
|
19028
19637
|
}
|
|
19029
19638
|
});
|
|
19030
19639
|
|
|
19031
|
-
// ../../packages/shuttle/dist/config/env-ref.js
|
|
19032
|
-
var require_env_ref = __commonJS({
|
|
19033
|
-
"../../packages/shuttle/dist/config/env-ref.js"(exports2) {
|
|
19034
|
-
"use strict";
|
|
19035
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19036
|
-
exports2.resolveEnvRef = resolveEnvRef;
|
|
19037
|
-
exports2.isEnvRef = isEnvRef;
|
|
19038
|
-
var ENV_REF_PATTERN = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/;
|
|
19039
|
-
function resolveEnvRef(value, fieldName) {
|
|
19040
|
-
const match = ENV_REF_PATTERN.exec(value);
|
|
19041
|
-
if (!match)
|
|
19042
|
-
return value;
|
|
19043
|
-
const varName = match[1];
|
|
19044
|
-
const resolved = process.env[varName];
|
|
19045
|
-
if (resolved === void 0 || resolved === "") {
|
|
19046
|
-
const subject = fieldName ? `${fieldName} (${value})` : value;
|
|
19047
|
-
throw new Error(`Environment variable "${varName}" referenced by ${subject} is not set. Export it before starting the Sifter, or hard-code the value in the YAML.`);
|
|
19048
|
-
}
|
|
19049
|
-
return resolved;
|
|
19050
|
-
}
|
|
19051
|
-
function isEnvRef(value) {
|
|
19052
|
-
return ENV_REF_PATTERN.test(value);
|
|
19053
|
-
}
|
|
19054
|
-
}
|
|
19055
|
-
});
|
|
19056
|
-
|
|
19057
19640
|
// ../../packages/shuttle/dist/executors/anthropic-api.js
|
|
19058
19641
|
var require_anthropic_api = __commonJS({
|
|
19059
19642
|
"../../packages/shuttle/dist/executors/anthropic-api.js"(exports2) {
|
|
@@ -19089,8 +19672,9 @@ var require_anthropic_api = __commonJS({
|
|
|
19089
19672
|
const outputLabel = spec.outputLabel;
|
|
19090
19673
|
const providerHints = (0, loom_1.pickProviderHints)(spec, this.instance.capabilityId);
|
|
19091
19674
|
const apiKey = await this.instance.resolveKey(providerHints);
|
|
19675
|
+
const hintedModel = providerHints.model;
|
|
19092
19676
|
const body = {
|
|
19093
|
-
model: this.instance.model,
|
|
19677
|
+
model: typeof hintedModel === "string" && hintedModel.length > 0 ? hintedModel : this.instance.model,
|
|
19094
19678
|
max_tokens: this.instance.maxTokens,
|
|
19095
19679
|
messages: [{ role: "user", content: prompt }]
|
|
19096
19680
|
};
|
|
@@ -19117,6 +19701,12 @@ var require_anthropic_api = __commonJS({
|
|
|
19117
19701
|
bodyText = await response.text();
|
|
19118
19702
|
} catch {
|
|
19119
19703
|
}
|
|
19704
|
+
const envelope = bodyText ? safeJsonParse(bodyText) : void 0;
|
|
19705
|
+
const apiErr = envelope?.error;
|
|
19706
|
+
if (apiErr?.message) {
|
|
19707
|
+
const kind = apiErr.type ? ` (${apiErr.type})` : "";
|
|
19708
|
+
throw new Error(`Anthropic API error${kind}: ${apiErr.message} [HTTP ${response.status}]`);
|
|
19709
|
+
}
|
|
19120
19710
|
throw new Error(`HTTP ${response.status}${bodyText ? `: ${bodyText}` : ""}`);
|
|
19121
19711
|
}
|
|
19122
19712
|
const data = await response.json();
|
|
@@ -19198,9 +19788,9 @@ var require_anthropic_api = __commonJS({
|
|
|
19198
19788
|
if (config.apiKeyFrom !== "job-metadata") {
|
|
19199
19789
|
throw new Error(`Unsupported "apiKeyFrom" value: ${JSON.stringify(config.apiKeyFrom)}. Only "job-metadata" is supported in v0.2.`);
|
|
19200
19790
|
}
|
|
19201
|
-
const keepApiUrl =
|
|
19202
|
-
const clientId =
|
|
19203
|
-
const clientSecret =
|
|
19791
|
+
const keepApiUrl = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepApiUrl", "KEEP_API_URL");
|
|
19792
|
+
const clientId = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepClientId", "KEEP_CLIENT_ID");
|
|
19793
|
+
const clientSecret = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepClientSecret", "KEEP_CLIENT_SECRET");
|
|
19204
19794
|
const stmFactory = deps.buildServiceTokenManager ?? ((cfg) => new keep_1.ServiceTokenManager(cfg));
|
|
19205
19795
|
const stm = stmFactory({ keepApiUrl, clientId, clientSecret });
|
|
19206
19796
|
const keepClientFactory = deps.buildKeepClient ?? ((cfg) => new keep_1.KeepClient(cfg));
|
|
@@ -19219,17 +19809,6 @@ var require_anthropic_api = __commonJS({
|
|
|
19219
19809
|
return reveal.apiKey;
|
|
19220
19810
|
};
|
|
19221
19811
|
}
|
|
19222
|
-
function resolveStringConfig(config, field, envFallback) {
|
|
19223
|
-
const raw = config[field];
|
|
19224
|
-
if (typeof raw === "string" && raw.length > 0) {
|
|
19225
|
-
return (0, env_ref_1.resolveEnvRef)(raw, field);
|
|
19226
|
-
}
|
|
19227
|
-
const fromEnv = process.env[envFallback];
|
|
19228
|
-
if (fromEnv && fromEnv.length > 0) {
|
|
19229
|
-
return fromEnv;
|
|
19230
|
-
}
|
|
19231
|
-
throw new Error(`AnthropicApiExecutor dynamic-key mode requires "${field}" in config or the ${envFallback} env var`);
|
|
19232
|
-
}
|
|
19233
19812
|
function safeJsonParse(s) {
|
|
19234
19813
|
try {
|
|
19235
19814
|
const parsed = JSON.parse(s);
|
|
@@ -20594,7 +21173,7 @@ var require_dist5 = __commonJS({
|
|
|
20594
21173
|
"../../packages/shuttle/dist/index.js"(exports2) {
|
|
20595
21174
|
"use strict";
|
|
20596
21175
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20597
|
-
exports2.revokePairing = exports2.listPairings = exports2.PairingConfigStore = exports2.pair = exports2.DEFAULT_REFUSAL_MESSAGE = exports2.DEFAULT_MONTHLY_PERIOD = exports2.DEFAULT_DAILY_PERIOD = exports2.SUBSTRATE_BRAND = 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.ExecutorRegistry = exports2.checkResponseMinVersion = exports2.compareSemver = exports2.UpgradeRequiredError = exports2.MIN_VERSION_HEADER = exports2.loadConfig = exports2.Shuttle = exports2.createCli = void 0;
|
|
21176
|
+
exports2.revokePairing = exports2.listPairings = exports2.PairingConfigStore = exports2.pair = exports2.DEFAULT_REFUSAL_MESSAGE = exports2.DEFAULT_MONTHLY_PERIOD = exports2.DEFAULT_DAILY_PERIOD = exports2.SUBSTRATE_BRAND = 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.ExecutorRegistry = exports2.AgentCredentialRevokedError = exports2.checkResponseMinVersion = exports2.compareSemver = exports2.UpgradeRequiredError = exports2.MIN_VERSION_HEADER = exports2.loadConfig = exports2.Shuttle = exports2.createCli = void 0;
|
|
20598
21177
|
var cli_1 = require_cli();
|
|
20599
21178
|
Object.defineProperty(exports2, "createCli", { enumerable: true, get: function() {
|
|
20600
21179
|
return cli_1.createCli;
|
|
@@ -20620,6 +21199,10 @@ var require_dist5 = __commonJS({
|
|
|
20620
21199
|
Object.defineProperty(exports2, "checkResponseMinVersion", { enumerable: true, get: function() {
|
|
20621
21200
|
return version_check_1.checkResponseMinVersion;
|
|
20622
21201
|
} });
|
|
21202
|
+
var keep_1 = require_dist4();
|
|
21203
|
+
Object.defineProperty(exports2, "AgentCredentialRevokedError", { enumerable: true, get: function() {
|
|
21204
|
+
return keep_1.AgentCredentialRevokedError;
|
|
21205
|
+
} });
|
|
20623
21206
|
var registry_1 = require_registry();
|
|
20624
21207
|
Object.defineProperty(exports2, "ExecutorRegistry", { enumerable: true, get: function() {
|
|
20625
21208
|
return registry_1.ExecutorRegistry;
|
|
@@ -20721,7 +21304,7 @@ var import_shuttle = __toESM(require_dist5());
|
|
|
20721
21304
|
// package.json
|
|
20722
21305
|
var package_default = {
|
|
20723
21306
|
name: "@whittlelabs/sifter",
|
|
20724
|
-
version: "0.
|
|
21307
|
+
version: "0.9.0",
|
|
20725
21308
|
description: "Whittle Sifter: paired AI reviewer for Whittle Sift job pools.",
|
|
20726
21309
|
bin: {
|
|
20727
21310
|
"whittle-sifter": "./dist/bin.js"
|
|
@@ -20905,6 +21488,17 @@ async function promptForCwd(prompt, fallback) {
|
|
|
20905
21488
|
console.error("");
|
|
20906
21489
|
process.exit(2);
|
|
20907
21490
|
}
|
|
21491
|
+
if (err instanceof import_shuttle2.AgentCredentialRevokedError) {
|
|
21492
|
+
console.error("");
|
|
21493
|
+
console.error(`This ${sifterBrand.product.title}'s pairing has been revoked.`);
|
|
21494
|
+
console.error("");
|
|
21495
|
+
console.error("Keep refused its credentials \u2014 it was decommissioned from the");
|
|
21496
|
+
console.error("web app, or its secret was rotated by a newer pairing.");
|
|
21497
|
+
console.error("");
|
|
21498
|
+
console.error(`Run \`${sifterBrand.product.command} init\` to pair again.`);
|
|
21499
|
+
console.error("");
|
|
21500
|
+
process.exit(3);
|
|
21501
|
+
}
|
|
20908
21502
|
const message = err instanceof Error ? err.message : String(err);
|
|
20909
21503
|
console.error(`Fatal: ${message}`);
|
|
20910
21504
|
process.exit(1);
|