@whittlelabs/sifter 0.20.0 → 0.22.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 +1149 -854
- package/bin.js.map +4 -4
- package/package.json +1 -1
package/bin.js
CHANGED
|
@@ -15184,6 +15184,20 @@ var require_schema4 = __commonJS({
|
|
|
15184
15184
|
identityId: zod_1.z.string().uuid(),
|
|
15185
15185
|
metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional()
|
|
15186
15186
|
}),
|
|
15187
|
+
/**
|
|
15188
|
+
* Runner-owned per-dispatch credential reveal (ADR 0027): the deployment's
|
|
15189
|
+
* reveal service identity. Present ⇒ hosted (the runner builds a credential
|
|
15190
|
+
* broker and reveals customer keys/tokens per dispatch); absent ⇒ local (no
|
|
15191
|
+
* reveals, ambient credentials). Distinct from the pairing-derived AGENT
|
|
15192
|
+
* credential (`auth`), which authenticates the claim loop. `keepClientId` /
|
|
15193
|
+
* `keepClientSecret` fall back to the KEEP_CLIENT_ID / KEEP_CLIENT_SECRET env.
|
|
15194
|
+
*/
|
|
15195
|
+
credentials: zod_1.z.object({
|
|
15196
|
+
apiKeyFrom: zod_1.z.literal("job-metadata"),
|
|
15197
|
+
keepApiUrl: zod_1.z.string().url(),
|
|
15198
|
+
keepClientId: zod_1.z.string().optional(),
|
|
15199
|
+
keepClientSecret: zod_1.z.string().optional()
|
|
15200
|
+
}).passthrough().optional(),
|
|
15187
15201
|
pools: zod_1.z.array(poolSchema).min(1, "At least one pool is required"),
|
|
15188
15202
|
polling: zod_1.z.object({
|
|
15189
15203
|
intervalMs: zod_1.z.number().int().positive().default(defaults_1.DEFAULTS.polling.intervalMs),
|
|
@@ -18435,226 +18449,610 @@ var require_chain = __commonJS({
|
|
|
18435
18449
|
}
|
|
18436
18450
|
});
|
|
18437
18451
|
|
|
18438
|
-
// ../../packages/shuttle/dist/
|
|
18439
|
-
var
|
|
18440
|
-
"../../packages/shuttle/dist/
|
|
18452
|
+
// ../../packages/shuttle/dist/workspace/git.js
|
|
18453
|
+
var require_git = __commonJS({
|
|
18454
|
+
"../../packages/shuttle/dist/workspace/git.js"(exports2) {
|
|
18441
18455
|
"use strict";
|
|
18456
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
18457
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
18458
|
+
};
|
|
18442
18459
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18443
|
-
exports2.
|
|
18444
|
-
exports2.
|
|
18445
|
-
|
|
18446
|
-
var
|
|
18447
|
-
|
|
18448
|
-
|
|
18449
|
-
|
|
18450
|
-
|
|
18451
|
-
|
|
18452
|
-
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18460
|
+
exports2.gitMaterializer = void 0;
|
|
18461
|
+
exports2.buildGitAuthEnv = buildGitAuthEnv;
|
|
18462
|
+
var child_process_1 = require("child_process");
|
|
18463
|
+
var fs_1 = require("fs");
|
|
18464
|
+
var path_1 = __importDefault(require("path"));
|
|
18465
|
+
var DEFAULT_GIT_TIMEOUT_MS = 12e4;
|
|
18466
|
+
var DEFAULT_GIT_PROTOCOLS = "https:ssh:git";
|
|
18467
|
+
var DEFAULT_MAX_CONTEXT_REPOS = 5;
|
|
18468
|
+
exports2.gitMaterializer = {
|
|
18469
|
+
namespace: "git",
|
|
18470
|
+
materialize: materializeGit
|
|
18471
|
+
};
|
|
18472
|
+
async function materializeGit(request, runRoot, options) {
|
|
18473
|
+
const resolvedPrimary = readGitWorkspace(request);
|
|
18474
|
+
const primaryDir = path_1.default.join(runRoot, "primary");
|
|
18475
|
+
await fs_1.promises.mkdir(primaryDir, { recursive: true });
|
|
18476
|
+
if ("warning" in resolvedPrimary) {
|
|
18477
|
+
return { materialized: false, warning: resolvedPrimary.warning, context: [] };
|
|
18478
|
+
}
|
|
18479
|
+
const primary = resolvedPrimary.git;
|
|
18480
|
+
const timeoutMs = options.commandTimeoutMs ?? DEFAULT_GIT_TIMEOUT_MS;
|
|
18481
|
+
const allowedProtocols = options.allowedProtocols ?? DEFAULT_GIT_PROTOCOLS;
|
|
18482
|
+
const signal = options.signal;
|
|
18483
|
+
const maxContext = options.maxContextRepos ?? DEFAULT_MAX_CONTEXT_REPOS;
|
|
18484
|
+
const contextPlan = planContextRepos(primary.context, maxContext);
|
|
18485
|
+
const ref = primary.fetchCredential;
|
|
18486
|
+
let gitToken = null;
|
|
18487
|
+
if (ref && typeof ref.name === "string" && typeof ref.principal === "string" && options.credentials) {
|
|
18488
|
+
try {
|
|
18489
|
+
gitToken = await options.credentials.reveal(ref);
|
|
18490
|
+
} catch {
|
|
18491
|
+
gitToken = null;
|
|
18459
18492
|
}
|
|
18460
18493
|
}
|
|
18461
|
-
|
|
18462
|
-
|
|
18463
|
-
|
|
18464
|
-
if (
|
|
18465
|
-
|
|
18466
|
-
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
return
|
|
18494
|
+
const authEnv = gitToken !== null ? buildGitAuthEnv([primary.remote, ...contextPlan.materialize.map((c) => c.git.remote)], gitToken) : void 0;
|
|
18495
|
+
const git = (args, cwd) => runGit(args, cwd, { timeoutMs, signal, allowedProtocols, extraEnv: authEnv });
|
|
18496
|
+
const primaryResult = await materializeCommit(primary, primaryDir, git);
|
|
18497
|
+
if (!primaryResult.ok) {
|
|
18498
|
+
await fs_1.promises.rm(primaryDir, { recursive: true, force: true }).catch(() => {
|
|
18499
|
+
});
|
|
18500
|
+
await fs_1.promises.mkdir(primaryDir).catch(() => {
|
|
18501
|
+
});
|
|
18502
|
+
return { materialized: false, warning: primaryResult.warning, context: [] };
|
|
18503
|
+
}
|
|
18504
|
+
const context = [];
|
|
18505
|
+
for (const planned of contextPlan.skip) {
|
|
18506
|
+
context.push({ ...planned, materialized: false });
|
|
18507
|
+
}
|
|
18508
|
+
if (contextPlan.materialize.length > 0) {
|
|
18509
|
+
const contextRoot = path_1.default.join(runRoot, "context");
|
|
18510
|
+
await fs_1.promises.mkdir(contextRoot, { recursive: true });
|
|
18511
|
+
for (const entry of contextPlan.materialize) {
|
|
18512
|
+
const dir = path_1.default.join(contextRoot, entry.name);
|
|
18513
|
+
await fs_1.promises.mkdir(dir);
|
|
18514
|
+
const result = await materializeCommit(entry.git, dir, git);
|
|
18515
|
+
context.push({
|
|
18516
|
+
name: entry.name,
|
|
18517
|
+
requestedName: entry.requestedName,
|
|
18518
|
+
materialized: result.ok,
|
|
18519
|
+
...result.ok ? {} : { warning: result.warning }
|
|
18520
|
+
});
|
|
18521
|
+
}
|
|
18470
18522
|
}
|
|
18471
|
-
|
|
18472
|
-
|
|
18473
|
-
|
|
18474
|
-
|
|
18475
|
-
|
|
18476
|
-
if (
|
|
18477
|
-
return
|
|
18478
|
-
|
|
18479
|
-
|
|
18480
|
-
|
|
18481
|
-
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
18485
|
-
|
|
18486
|
-
|
|
18487
|
-
|
|
18488
|
-
const got = asStrings(resolveRef(String(params.got), output, inputs));
|
|
18489
|
-
const want = asStrings(resolveRef(String(params.want), output, inputs));
|
|
18490
|
-
const gotSet = new Set(got);
|
|
18491
|
-
const wantSet = new Set(want);
|
|
18492
|
-
const missing = want.filter((v) => !gotSet.has(v));
|
|
18493
|
-
const unknown = [...new Set(got.filter((v) => !wantSet.has(v)))];
|
|
18494
|
-
const seen = /* @__PURE__ */ new Set();
|
|
18495
|
-
const duplicated = [];
|
|
18496
|
-
for (const v of got) {
|
|
18497
|
-
if (seen.has(v))
|
|
18498
|
-
duplicated.push(v);
|
|
18499
|
-
else
|
|
18500
|
-
seen.add(v);
|
|
18523
|
+
context.sort((a, b) => contextOrder(contextPlan, a.requestedName) - contextOrder(contextPlan, b.requestedName));
|
|
18524
|
+
return { materialized: true, primaryDir, context };
|
|
18525
|
+
}
|
|
18526
|
+
function planContextRepos(raw, maxContext) {
|
|
18527
|
+
const plan = { materialize: [], skip: [], order: /* @__PURE__ */ new Map() };
|
|
18528
|
+
if (raw === void 0)
|
|
18529
|
+
return plan;
|
|
18530
|
+
if (!Array.isArray(raw))
|
|
18531
|
+
return plan;
|
|
18532
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
18533
|
+
raw.forEach((entry, index) => {
|
|
18534
|
+
const requestedName = entry && typeof entry.name === "string" ? entry.name : `context-${index}`;
|
|
18535
|
+
plan.order.set(requestedName, index);
|
|
18536
|
+
const resolved = readContextRepo(entry, index);
|
|
18537
|
+
if ("warning" in resolved) {
|
|
18538
|
+
plan.skip.push({ name: sanitizeMountName(requestedName, usedNames), requestedName, warning: resolved.warning });
|
|
18539
|
+
return;
|
|
18501
18540
|
}
|
|
18502
|
-
if (
|
|
18503
|
-
|
|
18504
|
-
|
|
18505
|
-
|
|
18506
|
-
|
|
18507
|
-
|
|
18508
|
-
|
|
18509
|
-
if (duplicated.length)
|
|
18510
|
-
parts.push(`assessed more than once: [${[...new Set(duplicated)].join(", ")}]`);
|
|
18511
|
-
return [
|
|
18512
|
-
`every ${subject} must be covered exactly once \u2014 ${parts.join("; ")}. Return one entry per ${subject}, using its exact id.`
|
|
18513
|
-
];
|
|
18514
|
-
},
|
|
18515
|
-
/**
|
|
18516
|
-
* `non_empty_when` — the array at `path` must be non-empty, optionally only
|
|
18517
|
-
* when `when.path` equals `when.equals`. Params:
|
|
18518
|
-
* `{ path: <output path>, when?: { path: <output path>, equals: <value> } }`.
|
|
18519
|
-
*/
|
|
18520
|
-
non_empty_when(params, output) {
|
|
18521
|
-
const when = params.when;
|
|
18522
|
-
if (when && typeof when.path === "string") {
|
|
18523
|
-
if (resolvePath(output, when.path) !== when.equals)
|
|
18524
|
-
return [];
|
|
18541
|
+
if (plan.materialize.length >= maxContext) {
|
|
18542
|
+
plan.skip.push({
|
|
18543
|
+
name: sanitizeMountName(requestedName, usedNames),
|
|
18544
|
+
requestedName,
|
|
18545
|
+
warning: `skipped: exceeds the ${maxContext}-context-repo cap`
|
|
18546
|
+
});
|
|
18547
|
+
return;
|
|
18525
18548
|
}
|
|
18526
|
-
|
|
18527
|
-
|
|
18528
|
-
|
|
18529
|
-
|
|
18530
|
-
|
|
18531
|
-
|
|
18532
|
-
|
|
18533
|
-
|
|
18549
|
+
plan.materialize.push({
|
|
18550
|
+
git: resolved.git,
|
|
18551
|
+
name: sanitizeMountName(requestedName, usedNames),
|
|
18552
|
+
requestedName
|
|
18553
|
+
});
|
|
18554
|
+
});
|
|
18555
|
+
return plan;
|
|
18556
|
+
}
|
|
18557
|
+
function contextOrder(plan, requestedName) {
|
|
18558
|
+
return plan.order.get(requestedName) ?? Number.MAX_SAFE_INTEGER;
|
|
18559
|
+
}
|
|
18560
|
+
async function materializeCommit(gitWorkspace, dir, git) {
|
|
18561
|
+
const hasCommit = () => git(["cat-file", "-e", `${gitWorkspace.commit}^{commit}`], dir).then((r) => r.code === 0);
|
|
18562
|
+
const init = await git(["init", "-q"], dir);
|
|
18563
|
+
if (init.code !== 0)
|
|
18564
|
+
return { ok: false, warning: `git init failed: ${firstLine(init.stderr)}` };
|
|
18565
|
+
const attempts = [];
|
|
18566
|
+
for (const ref of gitWorkspace.fetchRefs ?? []) {
|
|
18567
|
+
attempts.push({
|
|
18568
|
+
source: `${gitWorkspace.remote} ${ref}`,
|
|
18569
|
+
args: ["fetch", "--no-tags", "--depth=1", gitWorkspace.remote, ref]
|
|
18570
|
+
});
|
|
18534
18571
|
}
|
|
18535
|
-
|
|
18536
|
-
|
|
18537
|
-
|
|
18538
|
-
|
|
18539
|
-
|
|
18540
|
-
|
|
18541
|
-
|
|
18572
|
+
attempts.push({
|
|
18573
|
+
source: `${gitWorkspace.remote} commit`,
|
|
18574
|
+
args: ["fetch", "--no-tags", "--depth=1", gitWorkspace.remote, gitWorkspace.commit]
|
|
18575
|
+
});
|
|
18576
|
+
const failures = [];
|
|
18577
|
+
let present = await hasCommit();
|
|
18578
|
+
for (const { source, args } of attempts) {
|
|
18579
|
+
if (present)
|
|
18580
|
+
break;
|
|
18581
|
+
const result = await git(args, dir);
|
|
18582
|
+
if (result.code !== 0) {
|
|
18583
|
+
failures.push(`${source}: ${firstLine(result.stderr)}`);
|
|
18542
18584
|
continue;
|
|
18543
18585
|
}
|
|
18544
|
-
|
|
18545
|
-
|
|
18546
|
-
|
|
18586
|
+
present = await hasCommit();
|
|
18587
|
+
if (!present)
|
|
18588
|
+
failures.push(`${source}: fetch succeeded but ${gitWorkspace.commit} is still absent`);
|
|
18547
18589
|
}
|
|
18548
|
-
|
|
18549
|
-
|
|
18550
|
-
|
|
18551
|
-
|
|
18552
|
-
|
|
18553
|
-
|
|
18554
|
-
|
|
18555
|
-
|
|
18590
|
+
if (!present) {
|
|
18591
|
+
return {
|
|
18592
|
+
ok: false,
|
|
18593
|
+
warning: `could not materialize ${gitWorkspace.commit} from ${gitWorkspace.remote}` + (failures.length > 0 ? ` (${failures.join("; ")})` : "")
|
|
18594
|
+
};
|
|
18595
|
+
}
|
|
18596
|
+
const checkout = await git(["checkout", "--detach", gitWorkspace.commit], dir);
|
|
18597
|
+
if (checkout.code !== 0)
|
|
18598
|
+
return { ok: false, warning: `git checkout failed: ${firstLine(checkout.stderr)}` };
|
|
18599
|
+
return { ok: true };
|
|
18556
18600
|
}
|
|
18557
|
-
|
|
18558
|
-
|
|
18559
|
-
|
|
18560
|
-
|
|
18561
|
-
|
|
18562
|
-
|
|
18563
|
-
|
|
18564
|
-
|
|
18565
|
-
|
|
18566
|
-
|
|
18567
|
-
|
|
18568
|
-
|
|
18569
|
-
constructor(config) {
|
|
18570
|
-
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
18571
|
-
this.headers = { "Content-Type": "application/json" };
|
|
18572
|
-
if (config.serviceToken) {
|
|
18573
|
-
this.headers["Authorization"] = `Bearer ${config.serviceToken}`;
|
|
18574
|
-
} else if (config.accessToken) {
|
|
18575
|
-
this.headers["Authorization"] = `Bearer ${config.accessToken}`;
|
|
18601
|
+
function buildGitAuthEnv(remotes, token) {
|
|
18602
|
+
const origins = [];
|
|
18603
|
+
for (const remote of remotes) {
|
|
18604
|
+
try {
|
|
18605
|
+
const url = new URL(remote);
|
|
18606
|
+
if (url.protocol !== "https:")
|
|
18607
|
+
continue;
|
|
18608
|
+
const origin = `${url.protocol}//${url.host}/`;
|
|
18609
|
+
if (!origins.includes(origin))
|
|
18610
|
+
origins.push(origin);
|
|
18611
|
+
} catch {
|
|
18612
|
+
continue;
|
|
18576
18613
|
}
|
|
18577
18614
|
}
|
|
18578
|
-
|
|
18579
|
-
|
|
18580
|
-
|
|
18581
|
-
|
|
18582
|
-
|
|
18583
|
-
|
|
18584
|
-
|
|
18585
|
-
|
|
18586
|
-
|
|
18587
|
-
|
|
18588
|
-
|
|
18589
|
-
|
|
18590
|
-
|
|
18591
|
-
|
|
18592
|
-
return
|
|
18593
|
-
}
|
|
18594
|
-
async destroySession(refreshToken) {
|
|
18595
|
-
await this.request("DELETE", "/api/sessions", refreshToken ? { refreshToken } : void 0);
|
|
18596
|
-
}
|
|
18597
|
-
async refreshSession(refreshToken) {
|
|
18598
|
-
return this.request("POST", "/api/sessions/renewal", { refreshToken });
|
|
18599
|
-
}
|
|
18600
|
-
async getCurrentUser() {
|
|
18601
|
-
return this.request("GET", "/api/accounts/me");
|
|
18602
|
-
}
|
|
18603
|
-
async updateProfile(updates) {
|
|
18604
|
-
return this.request("PATCH", "/api/accounts/me", updates);
|
|
18605
|
-
}
|
|
18606
|
-
async confirmEmail(email, code) {
|
|
18607
|
-
await this.request("POST", "/api/email-confirmations", { email, code });
|
|
18608
|
-
}
|
|
18609
|
-
async requestVerificationCode(email) {
|
|
18610
|
-
await this.request("POST", "/api/verification-codes", { email });
|
|
18611
|
-
}
|
|
18612
|
-
async requestPasswordReset(email) {
|
|
18613
|
-
await this.request("POST", "/api/password-resets", { email });
|
|
18614
|
-
}
|
|
18615
|
-
async resetPassword(email, code, newPassword) {
|
|
18616
|
-
await this.request("PUT", `/api/password-resets/${code}`, { email, newPassword });
|
|
18617
|
-
}
|
|
18618
|
-
async changePassword(currentPassword, newPassword) {
|
|
18619
|
-
await this.request("PUT", "/api/passwords", { currentPassword, newPassword });
|
|
18620
|
-
}
|
|
18621
|
-
// ── Organizations ───────────────────────────────────────────────
|
|
18622
|
-
async listOrganizations() {
|
|
18623
|
-
const { organizations } = await this.request("GET", "/api/organizations");
|
|
18624
|
-
return organizations;
|
|
18625
|
-
}
|
|
18626
|
-
async getOrganization(orgId) {
|
|
18627
|
-
return this.request("GET", `/api/organizations/${orgId}`);
|
|
18628
|
-
}
|
|
18629
|
-
async createOrganization(name, description) {
|
|
18630
|
-
return this.request("POST", "/api/organizations", { name, description });
|
|
18631
|
-
}
|
|
18632
|
-
async updateOrganization(orgId, updates) {
|
|
18633
|
-
return this.request("PATCH", `/api/organizations/${orgId}`, updates);
|
|
18634
|
-
}
|
|
18635
|
-
async deleteOrganization(orgId) {
|
|
18636
|
-
await this.request("DELETE", `/api/organizations/${orgId}`);
|
|
18637
|
-
}
|
|
18638
|
-
async listMembers(orgId) {
|
|
18639
|
-
return this.request("GET", `/api/organizations/${orgId}/members`);
|
|
18615
|
+
if (origins.length === 0)
|
|
18616
|
+
return void 0;
|
|
18617
|
+
const basic = Buffer.from(`x-access-token:${token}`).toString("base64");
|
|
18618
|
+
const env = { GIT_CONFIG_COUNT: String(origins.length) };
|
|
18619
|
+
origins.forEach((origin, i) => {
|
|
18620
|
+
env[`GIT_CONFIG_KEY_${i}`] = `http.${origin}.extraheader`;
|
|
18621
|
+
env[`GIT_CONFIG_VALUE_${i}`] = `Authorization: Basic ${basic}`;
|
|
18622
|
+
});
|
|
18623
|
+
return env;
|
|
18624
|
+
}
|
|
18625
|
+
var COMMIT_PATTERN = /^[0-9a-f]{40}$/i;
|
|
18626
|
+
var REF_PATTERN = /^[A-Za-z0-9][A-Za-z0-9/_.@^~+-]*$/;
|
|
18627
|
+
function readGitWorkspace(request) {
|
|
18628
|
+
if (typeof request !== "object" || request === null || Array.isArray(request)) {
|
|
18629
|
+
return { warning: "git workspace request must be an object" };
|
|
18640
18630
|
}
|
|
18641
|
-
|
|
18642
|
-
|
|
18631
|
+
return validateGitShape(request);
|
|
18632
|
+
}
|
|
18633
|
+
function readContextRepo(entry, index) {
|
|
18634
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
18635
|
+
return { warning: `context[${index}] must be an object` };
|
|
18643
18636
|
}
|
|
18644
|
-
|
|
18645
|
-
|
|
18637
|
+
const e = entry;
|
|
18638
|
+
if (typeof e.name !== "string" || e.name.length === 0) {
|
|
18639
|
+
return { warning: `context[${index}].name must be a non-empty string` };
|
|
18646
18640
|
}
|
|
18647
|
-
|
|
18648
|
-
|
|
18641
|
+
const shape = validateGitShape(e);
|
|
18642
|
+
if ("warning" in shape)
|
|
18643
|
+
return { warning: `context[${index}] ${shape.warning}` };
|
|
18644
|
+
return { git: { ...shape.git, name: e.name } };
|
|
18645
|
+
}
|
|
18646
|
+
function validateGitShape(git) {
|
|
18647
|
+
if (typeof git.remote !== "string" || git.remote.length === 0 || git.remote.startsWith("-")) {
|
|
18648
|
+
return { warning: "git.remote must be a non-empty remote URL or path" };
|
|
18649
18649
|
}
|
|
18650
|
-
|
|
18651
|
-
return
|
|
18650
|
+
if (typeof git.commit !== "string" || !COMMIT_PATTERN.test(git.commit)) {
|
|
18651
|
+
return { warning: "git.commit must be a full 40-char commit SHA" };
|
|
18652
18652
|
}
|
|
18653
|
-
|
|
18654
|
-
|
|
18653
|
+
if (git.fetchRefs !== void 0) {
|
|
18654
|
+
if (!Array.isArray(git.fetchRefs) || !git.fetchRefs.every((r) => typeof r === "string")) {
|
|
18655
|
+
return { warning: "git.fetchRefs must be an array of ref strings" };
|
|
18656
|
+
}
|
|
18657
|
+
const bad = git.fetchRefs.find((r) => !REF_PATTERN.test(r));
|
|
18658
|
+
if (bad !== void 0) {
|
|
18659
|
+
return { warning: `git.fetchRefs contains an invalid ref: ${JSON.stringify(bad)}` };
|
|
18660
|
+
}
|
|
18655
18661
|
}
|
|
18656
|
-
|
|
18657
|
-
|
|
18662
|
+
return { git };
|
|
18663
|
+
}
|
|
18664
|
+
function sanitizeMountName(requested, used) {
|
|
18665
|
+
let base = requested.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[.-]+/, "").replace(/-+$/, "").slice(0, 100);
|
|
18666
|
+
if (base.length === 0)
|
|
18667
|
+
base = "context";
|
|
18668
|
+
let name = base;
|
|
18669
|
+
let n = 2;
|
|
18670
|
+
while (used.has(name)) {
|
|
18671
|
+
name = `${base}-${n}`;
|
|
18672
|
+
n += 1;
|
|
18673
|
+
}
|
|
18674
|
+
used.add(name);
|
|
18675
|
+
return name;
|
|
18676
|
+
}
|
|
18677
|
+
function runGit(args, cwd, opts) {
|
|
18678
|
+
return new Promise((resolve) => {
|
|
18679
|
+
const child = (0, child_process_1.spawn)("git", args, {
|
|
18680
|
+
cwd,
|
|
18681
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18682
|
+
env: {
|
|
18683
|
+
...process.env,
|
|
18684
|
+
...opts.extraEnv ?? {},
|
|
18685
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
18686
|
+
GIT_ALLOW_PROTOCOL: opts.allowedProtocols
|
|
18687
|
+
}
|
|
18688
|
+
});
|
|
18689
|
+
const stdout = [];
|
|
18690
|
+
const stderr = [];
|
|
18691
|
+
let settled = false;
|
|
18692
|
+
const settle = (result) => {
|
|
18693
|
+
if (settled)
|
|
18694
|
+
return;
|
|
18695
|
+
settled = true;
|
|
18696
|
+
clearTimeout(timer);
|
|
18697
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
18698
|
+
resolve(result);
|
|
18699
|
+
};
|
|
18700
|
+
const killChild = () => {
|
|
18701
|
+
child.kill("SIGTERM");
|
|
18702
|
+
const escalate = setTimeout(() => child.kill("SIGKILL"), 5e3);
|
|
18703
|
+
escalate.unref();
|
|
18704
|
+
child.once("close", () => clearTimeout(escalate));
|
|
18705
|
+
};
|
|
18706
|
+
const timer = setTimeout(() => {
|
|
18707
|
+
killChild();
|
|
18708
|
+
settle({ code: -1, stdout: Buffer.concat(stdout).toString("utf-8"), stderr: `git ${args[0]} timed out after ${opts.timeoutMs}ms` });
|
|
18709
|
+
}, opts.timeoutMs);
|
|
18710
|
+
const onAbort = () => {
|
|
18711
|
+
killChild();
|
|
18712
|
+
settle({ code: -1, stdout: Buffer.concat(stdout).toString("utf-8"), stderr: "aborted" });
|
|
18713
|
+
};
|
|
18714
|
+
if (opts.signal?.aborted) {
|
|
18715
|
+
onAbort();
|
|
18716
|
+
return;
|
|
18717
|
+
}
|
|
18718
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
18719
|
+
child.stdout.on("data", (c) => stdout.push(c));
|
|
18720
|
+
child.stderr.on("data", (c) => stderr.push(c));
|
|
18721
|
+
child.on("error", (err) => settle({ code: -1, stdout: "", stderr: err.message }));
|
|
18722
|
+
child.on("close", (code) => settle({ code: code ?? -1, stdout: Buffer.concat(stdout).toString("utf-8"), stderr: Buffer.concat(stderr).toString("utf-8") }));
|
|
18723
|
+
});
|
|
18724
|
+
}
|
|
18725
|
+
function firstLine(text) {
|
|
18726
|
+
const line = text.trim().split("\n", 1)[0];
|
|
18727
|
+
return line.length > 0 ? line : "(no output)";
|
|
18728
|
+
}
|
|
18729
|
+
}
|
|
18730
|
+
});
|
|
18731
|
+
|
|
18732
|
+
// ../../packages/shuttle/dist/workspace/overlay.js
|
|
18733
|
+
var require_overlay = __commonJS({
|
|
18734
|
+
"../../packages/shuttle/dist/workspace/overlay.js"(exports2) {
|
|
18735
|
+
"use strict";
|
|
18736
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
18737
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
18738
|
+
};
|
|
18739
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18740
|
+
exports2.overlayModifier = void 0;
|
|
18741
|
+
exports2.applyOverlay = applyOverlay;
|
|
18742
|
+
var fs_1 = require("fs");
|
|
18743
|
+
var path_1 = __importDefault(require("path"));
|
|
18744
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
18745
|
+
var DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
|
|
18746
|
+
exports2.overlayModifier = {
|
|
18747
|
+
namespace: "overlay",
|
|
18748
|
+
apply: applyOverlay
|
|
18749
|
+
};
|
|
18750
|
+
async function applyOverlay(request, runDir, options) {
|
|
18751
|
+
const timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
18752
|
+
const maxBytes = options.overlayMaxBytes ?? DEFAULT_MAX_BYTES;
|
|
18753
|
+
const allowed = new Set((options.overlayAllowedHosts ?? []).map(normalizeOrigin).filter((o) => !!o));
|
|
18754
|
+
const rootReal = await fs_1.promises.realpath(runDir);
|
|
18755
|
+
const result = { written: 0, writtenPaths: [], skipped: [] };
|
|
18756
|
+
const files = request && typeof request === "object" && Array.isArray(request.files) ? request.files : [];
|
|
18757
|
+
for (const file of files) {
|
|
18758
|
+
const skip = (reason) => result.skipped.push({ path: String(file?.path ?? "?"), reason });
|
|
18759
|
+
if (typeof file?.path !== "string" || typeof file?.url !== "string") {
|
|
18760
|
+
skip("malformed overlay entry (path/url must be strings)");
|
|
18761
|
+
continue;
|
|
18762
|
+
}
|
|
18763
|
+
const dest = safeDestination(rootReal, file.path);
|
|
18764
|
+
if (!dest) {
|
|
18765
|
+
skip("path escapes the workspace root");
|
|
18766
|
+
continue;
|
|
18767
|
+
}
|
|
18768
|
+
let origin;
|
|
18769
|
+
try {
|
|
18770
|
+
const u = new URL(file.url);
|
|
18771
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") {
|
|
18772
|
+
skip(`unsupported url protocol ${u.protocol}`);
|
|
18773
|
+
continue;
|
|
18774
|
+
}
|
|
18775
|
+
origin = normalizeOrigin(u.origin);
|
|
18776
|
+
} catch {
|
|
18777
|
+
skip("invalid url");
|
|
18778
|
+
continue;
|
|
18779
|
+
}
|
|
18780
|
+
if (!origin || !allowed.has(origin)) {
|
|
18781
|
+
skip(`origin not in the overlay allowlist (${origin})`);
|
|
18782
|
+
continue;
|
|
18783
|
+
}
|
|
18784
|
+
try {
|
|
18785
|
+
const content = await fetchCapped(file.url, { timeoutMs, maxBytes, signal: options.signal });
|
|
18786
|
+
const parent = path_1.default.dirname(dest);
|
|
18787
|
+
await fs_1.promises.mkdir(parent, { recursive: true });
|
|
18788
|
+
const parentReal = await fs_1.promises.realpath(parent);
|
|
18789
|
+
if (parentReal !== rootReal && !parentReal.startsWith(rootReal + path_1.default.sep)) {
|
|
18790
|
+
skip("destination parent escapes the workspace root");
|
|
18791
|
+
continue;
|
|
18792
|
+
}
|
|
18793
|
+
const handle = await fs_1.promises.open(dest, fs_1.constants.O_WRONLY | fs_1.constants.O_CREAT | fs_1.constants.O_TRUNC | fs_1.constants.O_NOFOLLOW, 420);
|
|
18794
|
+
try {
|
|
18795
|
+
await handle.writeFile(content);
|
|
18796
|
+
} finally {
|
|
18797
|
+
await handle.close();
|
|
18798
|
+
}
|
|
18799
|
+
result.written += 1;
|
|
18800
|
+
result.writtenPaths.push(file.path);
|
|
18801
|
+
} catch (err) {
|
|
18802
|
+
skip(err instanceof Error ? err.message : String(err));
|
|
18803
|
+
}
|
|
18804
|
+
}
|
|
18805
|
+
return result;
|
|
18806
|
+
}
|
|
18807
|
+
function safeDestination(rootReal, relPath) {
|
|
18808
|
+
if (relPath.length === 0 || path_1.default.isAbsolute(relPath))
|
|
18809
|
+
return void 0;
|
|
18810
|
+
const resolved = path_1.default.resolve(rootReal, relPath);
|
|
18811
|
+
const rootWithSep = rootReal.endsWith(path_1.default.sep) ? rootReal : rootReal + path_1.default.sep;
|
|
18812
|
+
if (resolved !== rootReal && !resolved.startsWith(rootWithSep))
|
|
18813
|
+
return void 0;
|
|
18814
|
+
if (relPath.split(/[/\\]/).includes(".."))
|
|
18815
|
+
return void 0;
|
|
18816
|
+
return resolved;
|
|
18817
|
+
}
|
|
18818
|
+
function normalizeOrigin(value) {
|
|
18819
|
+
try {
|
|
18820
|
+
const u = new URL(value.includes("://") ? value : `https://${value}`);
|
|
18821
|
+
return u.origin;
|
|
18822
|
+
} catch {
|
|
18823
|
+
return void 0;
|
|
18824
|
+
}
|
|
18825
|
+
}
|
|
18826
|
+
async function fetchCapped(url, opts) {
|
|
18827
|
+
const controller = new AbortController();
|
|
18828
|
+
const onParentAbort = () => controller.abort();
|
|
18829
|
+
opts.signal?.addEventListener("abort", onParentAbort, { once: true });
|
|
18830
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs);
|
|
18831
|
+
try {
|
|
18832
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
18833
|
+
if (!response.ok)
|
|
18834
|
+
throw new Error(`overlay fetch returned HTTP ${response.status}`);
|
|
18835
|
+
if (!response.body) {
|
|
18836
|
+
const buf = Buffer.from(await response.arrayBuffer());
|
|
18837
|
+
if (buf.byteLength > opts.maxBytes)
|
|
18838
|
+
throw new Error("overlay file exceeds size cap");
|
|
18839
|
+
return buf;
|
|
18840
|
+
}
|
|
18841
|
+
const chunks = [];
|
|
18842
|
+
let total = 0;
|
|
18843
|
+
for await (const chunk of response.body) {
|
|
18844
|
+
const buf = Buffer.from(chunk);
|
|
18845
|
+
total += buf.byteLength;
|
|
18846
|
+
if (total > opts.maxBytes)
|
|
18847
|
+
throw new Error("overlay file exceeds size cap");
|
|
18848
|
+
chunks.push(buf);
|
|
18849
|
+
}
|
|
18850
|
+
return Buffer.concat(chunks);
|
|
18851
|
+
} finally {
|
|
18852
|
+
clearTimeout(timer);
|
|
18853
|
+
opts.signal?.removeEventListener("abort", onParentAbort);
|
|
18854
|
+
}
|
|
18855
|
+
}
|
|
18856
|
+
}
|
|
18857
|
+
});
|
|
18858
|
+
|
|
18859
|
+
// ../../packages/shuttle/dist/workspace/materialize.js
|
|
18860
|
+
var require_materialize = __commonJS({
|
|
18861
|
+
"../../packages/shuttle/dist/workspace/materialize.js"(exports2) {
|
|
18862
|
+
"use strict";
|
|
18863
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
18864
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
18865
|
+
};
|
|
18866
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18867
|
+
exports2.registerMaterializer = registerMaterializer;
|
|
18868
|
+
exports2.registerModifier = registerModifier;
|
|
18869
|
+
exports2.materializeWorkspace = materializeWorkspace;
|
|
18870
|
+
var fs_1 = require("fs");
|
|
18871
|
+
var os_1 = __importDefault(require("os"));
|
|
18872
|
+
var path_1 = __importDefault(require("path"));
|
|
18873
|
+
var git_1 = require_git();
|
|
18874
|
+
var overlay_1 = require_overlay();
|
|
18875
|
+
var materializers = /* @__PURE__ */ new Map();
|
|
18876
|
+
var modifiers = /* @__PURE__ */ new Map();
|
|
18877
|
+
function registerMaterializer(provider) {
|
|
18878
|
+
materializers.set(provider.namespace, provider);
|
|
18879
|
+
}
|
|
18880
|
+
function registerModifier(provider) {
|
|
18881
|
+
modifiers.set(provider.namespace, provider);
|
|
18882
|
+
}
|
|
18883
|
+
registerMaterializer(git_1.gitMaterializer);
|
|
18884
|
+
registerModifier(overlay_1.overlayModifier);
|
|
18885
|
+
async function materializeWorkspace(bag, options = {}) {
|
|
18886
|
+
const tmpBase = options.tmpDir ?? os_1.default.tmpdir();
|
|
18887
|
+
const runRoot = await fs_1.promises.mkdtemp(path_1.default.join(tmpBase, "shuttle-ws-"));
|
|
18888
|
+
const cleanup = async () => {
|
|
18889
|
+
await fs_1.promises.rm(runRoot, { recursive: true, force: true }).catch(() => {
|
|
18890
|
+
});
|
|
18891
|
+
};
|
|
18892
|
+
let materialized = false;
|
|
18893
|
+
let primaryDir;
|
|
18894
|
+
let warning;
|
|
18895
|
+
let context = [];
|
|
18896
|
+
let ranBase = false;
|
|
18897
|
+
for (const [namespace, provider] of materializers) {
|
|
18898
|
+
if (bag[namespace] !== void 0) {
|
|
18899
|
+
const outcome = await provider.materialize(bag[namespace], runRoot, options);
|
|
18900
|
+
materialized = outcome.materialized;
|
|
18901
|
+
primaryDir = outcome.primaryDir;
|
|
18902
|
+
warning = outcome.warning;
|
|
18903
|
+
context = outcome.context;
|
|
18904
|
+
ranBase = true;
|
|
18905
|
+
break;
|
|
18906
|
+
}
|
|
18907
|
+
}
|
|
18908
|
+
if (!ranBase) {
|
|
18909
|
+
const offered = Object.keys(bag).join(", ") || "(none)";
|
|
18910
|
+
await fs_1.promises.mkdir(path_1.default.join(runRoot, "primary"), { recursive: true }).catch(() => {
|
|
18911
|
+
});
|
|
18912
|
+
warning = `workspace carries no materializer this runner understands (offered: ${offered}; supported: ${[...materializers.keys()].join(", ")})`;
|
|
18913
|
+
}
|
|
18914
|
+
let overlay;
|
|
18915
|
+
for (const [namespace, provider] of modifiers) {
|
|
18916
|
+
if (bag[namespace] !== void 0) {
|
|
18917
|
+
overlay = await provider.apply(bag[namespace], runRoot, options);
|
|
18918
|
+
}
|
|
18919
|
+
}
|
|
18920
|
+
return { cwd: runRoot, materialized, primaryDir, warning, context, overlay, cleanup };
|
|
18921
|
+
}
|
|
18922
|
+
}
|
|
18923
|
+
});
|
|
18924
|
+
|
|
18925
|
+
// ../../packages/shuttle/dist/workspace/index.js
|
|
18926
|
+
var require_workspace = __commonJS({
|
|
18927
|
+
"../../packages/shuttle/dist/workspace/index.js"(exports2) {
|
|
18928
|
+
"use strict";
|
|
18929
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18930
|
+
exports2.applyOverlay = exports2.overlayModifier = exports2.buildGitAuthEnv = exports2.gitMaterializer = exports2.registerModifier = exports2.registerMaterializer = exports2.materializeWorkspace = void 0;
|
|
18931
|
+
var materialize_1 = require_materialize();
|
|
18932
|
+
Object.defineProperty(exports2, "materializeWorkspace", { enumerable: true, get: function() {
|
|
18933
|
+
return materialize_1.materializeWorkspace;
|
|
18934
|
+
} });
|
|
18935
|
+
Object.defineProperty(exports2, "registerMaterializer", { enumerable: true, get: function() {
|
|
18936
|
+
return materialize_1.registerMaterializer;
|
|
18937
|
+
} });
|
|
18938
|
+
Object.defineProperty(exports2, "registerModifier", { enumerable: true, get: function() {
|
|
18939
|
+
return materialize_1.registerModifier;
|
|
18940
|
+
} });
|
|
18941
|
+
var git_1 = require_git();
|
|
18942
|
+
Object.defineProperty(exports2, "gitMaterializer", { enumerable: true, get: function() {
|
|
18943
|
+
return git_1.gitMaterializer;
|
|
18944
|
+
} });
|
|
18945
|
+
Object.defineProperty(exports2, "buildGitAuthEnv", { enumerable: true, get: function() {
|
|
18946
|
+
return git_1.buildGitAuthEnv;
|
|
18947
|
+
} });
|
|
18948
|
+
var overlay_1 = require_overlay();
|
|
18949
|
+
Object.defineProperty(exports2, "overlayModifier", { enumerable: true, get: function() {
|
|
18950
|
+
return overlay_1.overlayModifier;
|
|
18951
|
+
} });
|
|
18952
|
+
Object.defineProperty(exports2, "applyOverlay", { enumerable: true, get: function() {
|
|
18953
|
+
return overlay_1.applyOverlay;
|
|
18954
|
+
} });
|
|
18955
|
+
}
|
|
18956
|
+
});
|
|
18957
|
+
|
|
18958
|
+
// ../../packages/keep/dist/client.js
|
|
18959
|
+
var require_client2 = __commonJS({
|
|
18960
|
+
"../../packages/keep/dist/client.js"(exports2) {
|
|
18961
|
+
"use strict";
|
|
18962
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18963
|
+
exports2.KeepClient = void 0;
|
|
18964
|
+
var KeepClient = class {
|
|
18965
|
+
baseUrl;
|
|
18966
|
+
headers;
|
|
18967
|
+
constructor(config) {
|
|
18968
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
18969
|
+
this.headers = { "Content-Type": "application/json" };
|
|
18970
|
+
if (config.serviceToken) {
|
|
18971
|
+
this.headers["Authorization"] = `Bearer ${config.serviceToken}`;
|
|
18972
|
+
} else if (config.accessToken) {
|
|
18973
|
+
this.headers["Authorization"] = `Bearer ${config.accessToken}`;
|
|
18974
|
+
}
|
|
18975
|
+
}
|
|
18976
|
+
// ── Actor Tokens ────────────────────────────────────────────────
|
|
18977
|
+
/**
|
|
18978
|
+
* Mint a short-lived `act_` token for an end user, with this client's
|
|
18979
|
+
* service identity recorded as the conduit. Requires this client to be
|
|
18980
|
+
* constructed with a service token holding `keep:actor-tokens:write`.
|
|
18981
|
+
*/
|
|
18982
|
+
async mintActorToken(actor) {
|
|
18983
|
+
return this.request("POST", "/api/actor-tokens", { actor });
|
|
18984
|
+
}
|
|
18985
|
+
// ── Identity ────────────────────────────────────────────────────
|
|
18986
|
+
async createAccount(email, password, name) {
|
|
18987
|
+
return this.request("POST", "/api/accounts", { email, password, name });
|
|
18988
|
+
}
|
|
18989
|
+
async createSession(email, password) {
|
|
18990
|
+
return this.request("POST", "/api/sessions", { email, password });
|
|
18991
|
+
}
|
|
18992
|
+
async destroySession(refreshToken) {
|
|
18993
|
+
await this.request("DELETE", "/api/sessions", refreshToken ? { refreshToken } : void 0);
|
|
18994
|
+
}
|
|
18995
|
+
async refreshSession(refreshToken) {
|
|
18996
|
+
return this.request("POST", "/api/sessions/renewal", { refreshToken });
|
|
18997
|
+
}
|
|
18998
|
+
async getCurrentUser() {
|
|
18999
|
+
return this.request("GET", "/api/accounts/me");
|
|
19000
|
+
}
|
|
19001
|
+
async updateProfile(updates) {
|
|
19002
|
+
return this.request("PATCH", "/api/accounts/me", updates);
|
|
19003
|
+
}
|
|
19004
|
+
async confirmEmail(email, code) {
|
|
19005
|
+
await this.request("POST", "/api/email-confirmations", { email, code });
|
|
19006
|
+
}
|
|
19007
|
+
async requestVerificationCode(email) {
|
|
19008
|
+
await this.request("POST", "/api/verification-codes", { email });
|
|
19009
|
+
}
|
|
19010
|
+
async requestPasswordReset(email) {
|
|
19011
|
+
await this.request("POST", "/api/password-resets", { email });
|
|
19012
|
+
}
|
|
19013
|
+
async resetPassword(email, code, newPassword) {
|
|
19014
|
+
await this.request("PUT", `/api/password-resets/${code}`, { email, newPassword });
|
|
19015
|
+
}
|
|
19016
|
+
async changePassword(currentPassword, newPassword) {
|
|
19017
|
+
await this.request("PUT", "/api/passwords", { currentPassword, newPassword });
|
|
19018
|
+
}
|
|
19019
|
+
// ── Organizations ───────────────────────────────────────────────
|
|
19020
|
+
async listOrganizations() {
|
|
19021
|
+
const { organizations } = await this.request("GET", "/api/organizations");
|
|
19022
|
+
return organizations;
|
|
19023
|
+
}
|
|
19024
|
+
async getOrganization(orgId) {
|
|
19025
|
+
return this.request("GET", `/api/organizations/${orgId}`);
|
|
19026
|
+
}
|
|
19027
|
+
async createOrganization(name, description) {
|
|
19028
|
+
return this.request("POST", "/api/organizations", { name, description });
|
|
19029
|
+
}
|
|
19030
|
+
async updateOrganization(orgId, updates) {
|
|
19031
|
+
return this.request("PATCH", `/api/organizations/${orgId}`, updates);
|
|
19032
|
+
}
|
|
19033
|
+
async deleteOrganization(orgId) {
|
|
19034
|
+
await this.request("DELETE", `/api/organizations/${orgId}`);
|
|
19035
|
+
}
|
|
19036
|
+
async listMembers(orgId) {
|
|
19037
|
+
return this.request("GET", `/api/organizations/${orgId}/members`);
|
|
19038
|
+
}
|
|
19039
|
+
async removeMember(orgId, userId) {
|
|
19040
|
+
await this.request("DELETE", `/api/organizations/${orgId}/members/${userId}`);
|
|
19041
|
+
}
|
|
19042
|
+
async listTeams(orgId) {
|
|
19043
|
+
return this.request("GET", `/api/organizations/${orgId}/teams`);
|
|
19044
|
+
}
|
|
19045
|
+
async createTeam(orgId, name, description) {
|
|
19046
|
+
return this.request("POST", `/api/organizations/${orgId}/teams`, { name, description });
|
|
19047
|
+
}
|
|
19048
|
+
async listTeamMembers(orgId, teamId) {
|
|
19049
|
+
return this.request("GET", `/api/organizations/${orgId}/teams/${teamId}/members`);
|
|
19050
|
+
}
|
|
19051
|
+
async addTeamMember(orgId, teamId, userId) {
|
|
19052
|
+
return this.request("POST", `/api/organizations/${orgId}/teams/${teamId}/members`, { userId });
|
|
19053
|
+
}
|
|
19054
|
+
async createInvitation(orgId, email, roleGrants) {
|
|
19055
|
+
return this.request("POST", `/api/organizations/${orgId}/invitations`, { email, roleGrants });
|
|
18658
19056
|
}
|
|
18659
19057
|
async getInvitation(token) {
|
|
18660
19058
|
return this.request("GET", `/api/invitations/${token}`);
|
|
@@ -18925,6 +19323,252 @@ var require_dist4 = __commonJS({
|
|
|
18925
19323
|
}
|
|
18926
19324
|
});
|
|
18927
19325
|
|
|
19326
|
+
// ../../packages/shuttle/dist/config/env-ref.js
|
|
19327
|
+
var require_env_ref = __commonJS({
|
|
19328
|
+
"../../packages/shuttle/dist/config/env-ref.js"(exports2) {
|
|
19329
|
+
"use strict";
|
|
19330
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19331
|
+
exports2.resolveEnvRef = resolveEnvRef;
|
|
19332
|
+
exports2.isEnvRef = isEnvRef;
|
|
19333
|
+
exports2.resolveConfigStringOrEnv = resolveConfigStringOrEnv;
|
|
19334
|
+
var ENV_REF_PATTERN = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/;
|
|
19335
|
+
function resolveEnvRef(value, fieldName) {
|
|
19336
|
+
const match = ENV_REF_PATTERN.exec(value);
|
|
19337
|
+
if (!match)
|
|
19338
|
+
return value;
|
|
19339
|
+
const varName = match[1];
|
|
19340
|
+
const resolved = process.env[varName];
|
|
19341
|
+
if (resolved === void 0 || resolved === "") {
|
|
19342
|
+
const subject = fieldName ? `${fieldName} (${value})` : value;
|
|
19343
|
+
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.`);
|
|
19344
|
+
}
|
|
19345
|
+
return resolved;
|
|
19346
|
+
}
|
|
19347
|
+
function isEnvRef(value) {
|
|
19348
|
+
return ENV_REF_PATTERN.test(value);
|
|
19349
|
+
}
|
|
19350
|
+
function resolveConfigStringOrEnv(config, field, envFallback) {
|
|
19351
|
+
const raw = config[field];
|
|
19352
|
+
if (typeof raw === "string" && raw.length > 0) {
|
|
19353
|
+
return resolveEnvRef(raw, field);
|
|
19354
|
+
}
|
|
19355
|
+
const fromEnv = process.env[envFallback];
|
|
19356
|
+
if (fromEnv && fromEnv.length > 0) {
|
|
19357
|
+
return fromEnv;
|
|
19358
|
+
}
|
|
19359
|
+
throw new Error(`dynamic-key mode requires "${field}" in config or the ${envFallback} env var`);
|
|
19360
|
+
}
|
|
19361
|
+
}
|
|
19362
|
+
});
|
|
19363
|
+
|
|
19364
|
+
// ../../packages/shuttle/dist/credentials/registry.js
|
|
19365
|
+
var require_registry = __commonJS({
|
|
19366
|
+
"../../packages/shuttle/dist/credentials/registry.js"(exports2) {
|
|
19367
|
+
"use strict";
|
|
19368
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19369
|
+
exports2.registerCredentialResolver = registerCredentialResolver;
|
|
19370
|
+
exports2.getCredentialResolver = getCredentialResolver;
|
|
19371
|
+
var resolvers = /* @__PURE__ */ new Map();
|
|
19372
|
+
function registerCredentialResolver(name, resolver) {
|
|
19373
|
+
resolvers.set(name, resolver);
|
|
19374
|
+
}
|
|
19375
|
+
function getCredentialResolver(name) {
|
|
19376
|
+
return resolvers.get(name);
|
|
19377
|
+
}
|
|
19378
|
+
registerCredentialResolver("managed-model-key", async (keep, principal) => {
|
|
19379
|
+
const reveal = await keep.revealManagedSifterKey(principal);
|
|
19380
|
+
return reveal.apiKey;
|
|
19381
|
+
});
|
|
19382
|
+
registerCredentialResolver("github-user-token", async (keep, principal) => {
|
|
19383
|
+
try {
|
|
19384
|
+
const reveal = await keep.getUserGithubToken(principal);
|
|
19385
|
+
return reveal.access_token;
|
|
19386
|
+
} catch {
|
|
19387
|
+
return null;
|
|
19388
|
+
}
|
|
19389
|
+
});
|
|
19390
|
+
}
|
|
19391
|
+
});
|
|
19392
|
+
|
|
19393
|
+
// ../../packages/shuttle/dist/credentials/broker.js
|
|
19394
|
+
var require_broker = __commonJS({
|
|
19395
|
+
"../../packages/shuttle/dist/credentials/broker.js"(exports2) {
|
|
19396
|
+
"use strict";
|
|
19397
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19398
|
+
exports2.createCredentialBroker = createCredentialBroker;
|
|
19399
|
+
var keep_1 = require_dist4();
|
|
19400
|
+
var env_ref_1 = require_env_ref();
|
|
19401
|
+
var registry_1 = require_registry();
|
|
19402
|
+
function createCredentialBroker(config, deps = {}) {
|
|
19403
|
+
if (typeof config !== "object" || config === null || config.apiKeyFrom !== "job-metadata") {
|
|
19404
|
+
return null;
|
|
19405
|
+
}
|
|
19406
|
+
const cfg = config;
|
|
19407
|
+
const keepApiUrl = (0, env_ref_1.resolveConfigStringOrEnv)(cfg, "keepApiUrl", "KEEP_API_URL");
|
|
19408
|
+
const clientId = (0, env_ref_1.resolveConfigStringOrEnv)(cfg, "keepClientId", "KEEP_CLIENT_ID");
|
|
19409
|
+
const clientSecret = (0, env_ref_1.resolveConfigStringOrEnv)(cfg, "keepClientSecret", "KEEP_CLIENT_SECRET");
|
|
19410
|
+
const stmFactory = deps.buildServiceTokenManager ?? ((c) => new keep_1.ServiceTokenManager(c));
|
|
19411
|
+
const stm = stmFactory({ keepApiUrl, clientId, clientSecret });
|
|
19412
|
+
const keepFactory = deps.buildKeepClient ?? ((c) => new keep_1.KeepClient(c));
|
|
19413
|
+
return {
|
|
19414
|
+
async reveal({ name, principal }) {
|
|
19415
|
+
const resolver = (0, registry_1.getCredentialResolver)(name);
|
|
19416
|
+
if (!resolver)
|
|
19417
|
+
return null;
|
|
19418
|
+
const serviceToken = await stm.getToken();
|
|
19419
|
+
if (!serviceToken) {
|
|
19420
|
+
throw new Error("credential broker: Keep service-token exchange failed. Check KEEP_CLIENT_ID / KEEP_CLIENT_SECRET.");
|
|
19421
|
+
}
|
|
19422
|
+
const keep = keepFactory({ baseUrl: keepApiUrl, serviceToken });
|
|
19423
|
+
return resolver(keep, principal);
|
|
19424
|
+
}
|
|
19425
|
+
};
|
|
19426
|
+
}
|
|
19427
|
+
}
|
|
19428
|
+
});
|
|
19429
|
+
|
|
19430
|
+
// ../../packages/shuttle/dist/credentials/index.js
|
|
19431
|
+
var require_credentials = __commonJS({
|
|
19432
|
+
"../../packages/shuttle/dist/credentials/index.js"(exports2) {
|
|
19433
|
+
"use strict";
|
|
19434
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19435
|
+
exports2.getCredentialResolver = exports2.registerCredentialResolver = exports2.createCredentialBroker = void 0;
|
|
19436
|
+
var broker_1 = require_broker();
|
|
19437
|
+
Object.defineProperty(exports2, "createCredentialBroker", { enumerable: true, get: function() {
|
|
19438
|
+
return broker_1.createCredentialBroker;
|
|
19439
|
+
} });
|
|
19440
|
+
var registry_1 = require_registry();
|
|
19441
|
+
Object.defineProperty(exports2, "registerCredentialResolver", { enumerable: true, get: function() {
|
|
19442
|
+
return registry_1.registerCredentialResolver;
|
|
19443
|
+
} });
|
|
19444
|
+
Object.defineProperty(exports2, "getCredentialResolver", { enumerable: true, get: function() {
|
|
19445
|
+
return registry_1.getCredentialResolver;
|
|
19446
|
+
} });
|
|
19447
|
+
}
|
|
19448
|
+
});
|
|
19449
|
+
|
|
19450
|
+
// ../../packages/shuttle/dist/executors/output-gates.js
|
|
19451
|
+
var require_output_gates = __commonJS({
|
|
19452
|
+
"../../packages/shuttle/dist/executors/output-gates.js"(exports2) {
|
|
19453
|
+
"use strict";
|
|
19454
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19455
|
+
exports2.resolvePath = resolvePath;
|
|
19456
|
+
exports2.evaluateOutputGates = evaluateOutputGates;
|
|
19457
|
+
exports2.renderRepairPrompt = renderRepairPrompt;
|
|
19458
|
+
var INPUT_REF = "input:";
|
|
19459
|
+
function resolvePath(root, path) {
|
|
19460
|
+
let ctx = root;
|
|
19461
|
+
for (const seg of path.split(".")) {
|
|
19462
|
+
const project = seg.endsWith("[*]");
|
|
19463
|
+
const key = project ? seg.slice(0, -3) : seg;
|
|
19464
|
+
if (Array.isArray(ctx)) {
|
|
19465
|
+
ctx = ctx.map((el) => key ? el?.[key] : el);
|
|
19466
|
+
} else {
|
|
19467
|
+
ctx = key ? ctx?.[key] : ctx;
|
|
19468
|
+
}
|
|
19469
|
+
if (project && !Array.isArray(ctx)) {
|
|
19470
|
+
ctx = ctx == null ? [] : [ctx];
|
|
19471
|
+
}
|
|
19472
|
+
}
|
|
19473
|
+
return ctx;
|
|
19474
|
+
}
|
|
19475
|
+
function resolveRef(ref, output, inputs) {
|
|
19476
|
+
if (ref.startsWith(INPUT_REF)) {
|
|
19477
|
+
const label = ref.slice(INPUT_REF.length);
|
|
19478
|
+
const row = inputs.find((i) => i.label === label) ?? inputs.find((i) => i.label === "artifacts" && i.payload.label === label);
|
|
19479
|
+
if (!row)
|
|
19480
|
+
return void 0;
|
|
19481
|
+
return Object.prototype.hasOwnProperty.call(row.payload, "value") ? row.payload.value : row.payload;
|
|
19482
|
+
}
|
|
19483
|
+
return resolvePath(output, ref);
|
|
19484
|
+
}
|
|
19485
|
+
function asStrings(value) {
|
|
19486
|
+
if (Array.isArray(value))
|
|
19487
|
+
return value.map((v) => String(v));
|
|
19488
|
+
if (value == null)
|
|
19489
|
+
return [];
|
|
19490
|
+
return [String(value)];
|
|
19491
|
+
}
|
|
19492
|
+
var PRIMITIVES = {
|
|
19493
|
+
/**
|
|
19494
|
+
* `coverage` — the values at `got` must exactly cover the set at `want`:
|
|
19495
|
+
* every wanted value present, nothing invented, none repeated. Params:
|
|
19496
|
+
* `{ got: <output path>, want: <ref>, subject?: <noun> }`.
|
|
19497
|
+
*/
|
|
19498
|
+
coverage(params, output, inputs) {
|
|
19499
|
+
const subject = typeof params.subject === "string" ? params.subject : "item";
|
|
19500
|
+
const got = asStrings(resolveRef(String(params.got), output, inputs));
|
|
19501
|
+
const want = asStrings(resolveRef(String(params.want), output, inputs));
|
|
19502
|
+
const gotSet = new Set(got);
|
|
19503
|
+
const wantSet = new Set(want);
|
|
19504
|
+
const missing = want.filter((v) => !gotSet.has(v));
|
|
19505
|
+
const unknown = [...new Set(got.filter((v) => !wantSet.has(v)))];
|
|
19506
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19507
|
+
const duplicated = [];
|
|
19508
|
+
for (const v of got) {
|
|
19509
|
+
if (seen.has(v))
|
|
19510
|
+
duplicated.push(v);
|
|
19511
|
+
else
|
|
19512
|
+
seen.add(v);
|
|
19513
|
+
}
|
|
19514
|
+
if (missing.length === 0 && unknown.length === 0 && duplicated.length === 0)
|
|
19515
|
+
return [];
|
|
19516
|
+
const parts = [];
|
|
19517
|
+
if (missing.length)
|
|
19518
|
+
parts.push(`missing ${subject}(s): [${missing.join(", ")}]`);
|
|
19519
|
+
if (unknown.length)
|
|
19520
|
+
parts.push(`not a known ${subject}: [${unknown.join(", ")}]`);
|
|
19521
|
+
if (duplicated.length)
|
|
19522
|
+
parts.push(`assessed more than once: [${[...new Set(duplicated)].join(", ")}]`);
|
|
19523
|
+
return [
|
|
19524
|
+
`every ${subject} must be covered exactly once \u2014 ${parts.join("; ")}. Return one entry per ${subject}, using its exact id.`
|
|
19525
|
+
];
|
|
19526
|
+
},
|
|
19527
|
+
/**
|
|
19528
|
+
* `non_empty_when` — the array at `path` must be non-empty, optionally only
|
|
19529
|
+
* when `when.path` equals `when.equals`. Params:
|
|
19530
|
+
* `{ path: <output path>, when?: { path: <output path>, equals: <value> } }`.
|
|
19531
|
+
*/
|
|
19532
|
+
non_empty_when(params, output) {
|
|
19533
|
+
const when = params.when;
|
|
19534
|
+
if (when && typeof when.path === "string") {
|
|
19535
|
+
if (resolvePath(output, when.path) !== when.equals)
|
|
19536
|
+
return [];
|
|
19537
|
+
}
|
|
19538
|
+
const path = String(params.path);
|
|
19539
|
+
const value = resolvePath(output, path);
|
|
19540
|
+
if (Array.isArray(value) && value.length > 0)
|
|
19541
|
+
return [];
|
|
19542
|
+
const cond = when && typeof when.path === "string" ? ` when ${when.path} is ${JSON.stringify(when.equals)}` : "";
|
|
19543
|
+
return [
|
|
19544
|
+
`'${path}' must be a non-empty array${cond}; it was ${Array.isArray(value) ? "empty" : "absent"}.`
|
|
19545
|
+
];
|
|
19546
|
+
}
|
|
19547
|
+
};
|
|
19548
|
+
function evaluateOutputGates(gates, output, inputs) {
|
|
19549
|
+
const findings = [];
|
|
19550
|
+
for (const gate of gates) {
|
|
19551
|
+
const primitive = PRIMITIVES[gate.primitive];
|
|
19552
|
+
if (!primitive) {
|
|
19553
|
+
findings.push({ gate: gate.name, message: `unknown gate primitive '${gate.primitive}'` });
|
|
19554
|
+
continue;
|
|
19555
|
+
}
|
|
19556
|
+
for (const message of primitive(gate.params, output, inputs)) {
|
|
19557
|
+
findings.push({ gate: gate.name, message });
|
|
19558
|
+
}
|
|
19559
|
+
}
|
|
19560
|
+
return { ok: findings.length === 0, findings };
|
|
19561
|
+
}
|
|
19562
|
+
function renderRepairPrompt(findings) {
|
|
19563
|
+
return [
|
|
19564
|
+
"Your previous answer did not satisfy these output checks. Return the corrected, complete answer that resolves every item below. Produce the same output shape with the problems fixed, and nothing else \u2014 no commentary, no explanation of the changes.",
|
|
19565
|
+
"",
|
|
19566
|
+
...findings.map((f) => `- [${f.gate}] ${f.message}`)
|
|
19567
|
+
].join("\n");
|
|
19568
|
+
}
|
|
19569
|
+
}
|
|
19570
|
+
});
|
|
19571
|
+
|
|
18928
19572
|
// ../../packages/shuttle/dist/logging/logger.js
|
|
18929
19573
|
var require_logger = __commonJS({
|
|
18930
19574
|
"../../packages/shuttle/dist/logging/logger.js"(exports2) {
|
|
@@ -18958,614 +19602,251 @@ var require_logger = __commonJS({
|
|
|
18958
19602
|
message,
|
|
18959
19603
|
...context
|
|
18960
19604
|
};
|
|
18961
|
-
return JSON.stringify(entry);
|
|
18962
|
-
}
|
|
18963
|
-
function createLogger(options) {
|
|
18964
|
-
const threshold = LEVEL_ORDER[options.level];
|
|
18965
|
-
const format = options.format === "json" ? formatJson : formatPretty;
|
|
18966
|
-
function emit(level, message, context) {
|
|
18967
|
-
if (LEVEL_ORDER[level] < threshold)
|
|
18968
|
-
return;
|
|
18969
|
-
const line = format(level, message, context);
|
|
18970
|
-
if (level === "error") {
|
|
18971
|
-
console.error(line);
|
|
18972
|
-
} else if (level === "warn") {
|
|
18973
|
-
console.warn(line);
|
|
18974
|
-
} else {
|
|
18975
|
-
console.log(line);
|
|
18976
|
-
}
|
|
18977
|
-
}
|
|
18978
|
-
return {
|
|
18979
|
-
debug: (message, context) => emit("debug", message, context),
|
|
18980
|
-
info: (message, context) => emit("info", message, context),
|
|
18981
|
-
warn: (message, context) => emit("warn", message, context),
|
|
18982
|
-
error: (message, context) => emit("error", message, context)
|
|
18983
|
-
};
|
|
18984
|
-
}
|
|
18985
|
-
var globalLogger = createLogger({ level: "info", format: "pretty" });
|
|
18986
|
-
function setGlobalLogger(logger) {
|
|
18987
|
-
globalLogger = logger;
|
|
18988
|
-
}
|
|
18989
|
-
function getLogger() {
|
|
18990
|
-
return globalLogger;
|
|
18991
|
-
}
|
|
18992
|
-
}
|
|
18993
|
-
});
|
|
18994
|
-
|
|
18995
|
-
// ../../packages/shuttle/dist/executors/registry.js
|
|
18996
|
-
var require_registry = __commonJS({
|
|
18997
|
-
"../../packages/shuttle/dist/executors/registry.js"(exports2) {
|
|
18998
|
-
"use strict";
|
|
18999
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19000
|
-
exports2.ExecutorRegistry = void 0;
|
|
19001
|
-
var ExecutorRegistry = class {
|
|
19002
|
-
factories = /* @__PURE__ */ new Map();
|
|
19003
|
-
allowlist;
|
|
19004
|
-
constructor(allowlist = ["*"]) {
|
|
19005
|
-
this.allowlist = allowlist;
|
|
19006
|
-
}
|
|
19007
|
-
register(name, factory) {
|
|
19008
|
-
if (!this.isAllowed(name)) {
|
|
19009
|
-
throw new Error(`Executor "${name}" is not enabled for this build. Allowed executors: ${this.allowlist.join(", ")}`);
|
|
19010
|
-
}
|
|
19011
|
-
this.factories.set(name, factory);
|
|
19012
|
-
}
|
|
19013
|
-
create(name, config) {
|
|
19014
|
-
const factory = this.factories.get(name);
|
|
19015
|
-
if (!factory) {
|
|
19016
|
-
throw new Error(`Executor not registered: ${name}`);
|
|
19017
|
-
}
|
|
19018
|
-
return factory(config, name);
|
|
19019
|
-
}
|
|
19020
|
-
has(name) {
|
|
19021
|
-
return this.factories.has(name);
|
|
19022
|
-
}
|
|
19023
|
-
get allowedExecutors() {
|
|
19024
|
-
return this.allowlist;
|
|
19025
|
-
}
|
|
19026
|
-
/** All registered factories as JobExecutors built with their default config. */
|
|
19027
|
-
build(configs) {
|
|
19028
|
-
const out = [];
|
|
19029
|
-
for (const [name, cfg] of Object.entries(configs)) {
|
|
19030
|
-
if (!this.factories.has(name)) {
|
|
19031
|
-
throw new Error(`Executor "${name}" in config is not registered or not enabled for this build.`);
|
|
19032
|
-
}
|
|
19033
|
-
out.push(this.create(name, cfg));
|
|
19034
|
-
}
|
|
19035
|
-
return out;
|
|
19036
|
-
}
|
|
19037
|
-
isAllowed(name) {
|
|
19038
|
-
return this.allowlist[0] === "*" || this.allowlist.includes(name);
|
|
19039
|
-
}
|
|
19040
|
-
};
|
|
19041
|
-
exports2.ExecutorRegistry = ExecutorRegistry;
|
|
19042
|
-
}
|
|
19043
|
-
});
|
|
19044
|
-
|
|
19045
|
-
// ../../packages/shuttle/dist/executors/validate-output.js
|
|
19046
|
-
var require_validate_output = __commonJS({
|
|
19047
|
-
"../../packages/shuttle/dist/executors/validate-output.js"(exports2) {
|
|
19048
|
-
"use strict";
|
|
19049
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19050
|
-
exports2.assertOutputValid = assertOutputValid;
|
|
19051
|
-
var loom_1 = require_dist3();
|
|
19052
|
-
function assertOutputValid(spec, parsed) {
|
|
19053
|
-
const { valid, errors } = (0, loom_1.validateAgainstSchema)(parsed ?? null, spec.outputSchema);
|
|
19054
|
-
if (!valid) {
|
|
19055
|
-
throw new Error(`prompt-execution output failed schema validation: ${errors.join("; ")}`);
|
|
19056
|
-
}
|
|
19057
|
-
}
|
|
19058
|
-
}
|
|
19059
|
-
});
|
|
19060
|
-
|
|
19061
|
-
// ../../packages/shuttle/dist/executors/progress.js
|
|
19062
|
-
var require_progress = __commonJS({
|
|
19063
|
-
"../../packages/shuttle/dist/executors/progress.js"(exports2) {
|
|
19064
|
-
"use strict";
|
|
19065
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19066
|
-
exports2.StreamJsonTap = exports2.ProgressReporter = void 0;
|
|
19067
|
-
var ProgressReporter = class {
|
|
19068
|
-
sink;
|
|
19069
|
-
seq = 0;
|
|
19070
|
-
pending = [];
|
|
19071
|
-
textBuf = "";
|
|
19072
|
-
timer = null;
|
|
19073
|
-
closed = false;
|
|
19074
|
-
flushMs;
|
|
19075
|
-
maxChars;
|
|
19076
|
-
constructor(sink, options = {}) {
|
|
19077
|
-
this.sink = sink;
|
|
19078
|
-
this.flushMs = options.flushMs ?? 400;
|
|
19079
|
-
this.maxChars = options.maxChars ?? 1200;
|
|
19080
|
-
}
|
|
19081
|
-
reasoning(text) {
|
|
19082
|
-
if (this.closed || !text)
|
|
19083
|
-
return;
|
|
19084
|
-
this.textBuf += text;
|
|
19085
|
-
if (this.textBuf.length >= this.maxChars) {
|
|
19086
|
-
this.flush();
|
|
19087
|
-
} else {
|
|
19088
|
-
this.arm();
|
|
19089
|
-
}
|
|
19090
|
-
}
|
|
19091
|
-
tool(name, target) {
|
|
19092
|
-
if (this.closed || !name)
|
|
19093
|
-
return;
|
|
19094
|
-
this.drainText();
|
|
19095
|
-
this.push({ kind: "tool", tool: target ? { name, target } : { name } });
|
|
19096
|
-
this.flush();
|
|
19097
|
-
}
|
|
19098
|
-
/** Flush pending text + frames now. */
|
|
19099
|
-
flush() {
|
|
19100
|
-
if (this.closed)
|
|
19101
|
-
return;
|
|
19102
|
-
this.drainText();
|
|
19103
|
-
this.clearTimer();
|
|
19104
|
-
if (this.pending.length === 0)
|
|
19105
|
-
return;
|
|
19106
|
-
const batch = this.pending;
|
|
19107
|
-
this.pending = [];
|
|
19108
|
-
try {
|
|
19109
|
-
this.sink(batch);
|
|
19110
|
-
} catch {
|
|
19111
|
-
}
|
|
19112
|
-
}
|
|
19113
|
-
/** Final flush; no further frames are emitted after this. */
|
|
19114
|
-
close() {
|
|
19115
|
-
this.flush();
|
|
19116
|
-
this.closed = true;
|
|
19117
|
-
this.clearTimer();
|
|
19118
|
-
}
|
|
19119
|
-
drainText() {
|
|
19120
|
-
if (this.textBuf.length === 0)
|
|
19121
|
-
return;
|
|
19122
|
-
this.push({ kind: "reasoning", text: this.textBuf });
|
|
19123
|
-
this.textBuf = "";
|
|
19124
|
-
}
|
|
19125
|
-
push(frame) {
|
|
19126
|
-
this.pending.push({ seq: this.seq++, at: (/* @__PURE__ */ new Date()).toISOString(), ...frame });
|
|
19127
|
-
}
|
|
19128
|
-
arm() {
|
|
19129
|
-
if (this.timer)
|
|
19130
|
-
return;
|
|
19131
|
-
this.timer = setTimeout(() => {
|
|
19132
|
-
this.timer = null;
|
|
19133
|
-
this.flush();
|
|
19134
|
-
}, this.flushMs);
|
|
19135
|
-
this.timer.unref?.();
|
|
19136
|
-
}
|
|
19137
|
-
clearTimer() {
|
|
19138
|
-
if (this.timer) {
|
|
19139
|
-
clearTimeout(this.timer);
|
|
19140
|
-
this.timer = null;
|
|
19141
|
-
}
|
|
19142
|
-
}
|
|
19143
|
-
};
|
|
19144
|
-
exports2.ProgressReporter = ProgressReporter;
|
|
19145
|
-
var StreamJsonTap = class {
|
|
19146
|
-
handlers;
|
|
19147
|
-
buf = "";
|
|
19148
|
-
constructor(handlers) {
|
|
19149
|
-
this.handlers = handlers;
|
|
19150
|
-
}
|
|
19151
|
-
/** Feed a raw stdout chunk. Emits handlers for every complete line parsed. */
|
|
19152
|
-
push(chunk) {
|
|
19153
|
-
this.buf += chunk;
|
|
19154
|
-
let nl;
|
|
19155
|
-
while ((nl = this.buf.indexOf("\n")) !== -1) {
|
|
19156
|
-
const line = this.buf.slice(0, nl);
|
|
19157
|
-
this.buf = this.buf.slice(nl + 1);
|
|
19158
|
-
this.consumeLine(line);
|
|
19159
|
-
}
|
|
19160
|
-
}
|
|
19161
|
-
/** Flush a trailing partial line (best-effort; usually empty at process close). */
|
|
19162
|
-
end() {
|
|
19163
|
-
if (this.buf.trim().length > 0)
|
|
19164
|
-
this.consumeLine(this.buf);
|
|
19165
|
-
this.buf = "";
|
|
19166
|
-
}
|
|
19167
|
-
consumeLine(line) {
|
|
19168
|
-
const trimmed = line.trim();
|
|
19169
|
-
if (!trimmed.startsWith("{"))
|
|
19170
|
-
return;
|
|
19171
|
-
let event;
|
|
19172
|
-
try {
|
|
19173
|
-
event = JSON.parse(trimmed);
|
|
19174
|
-
} catch {
|
|
19175
|
-
return;
|
|
19176
|
-
}
|
|
19177
|
-
if (event.type === "assistant") {
|
|
19178
|
-
const content = event.message?.content;
|
|
19179
|
-
if (!Array.isArray(content))
|
|
19180
|
-
return;
|
|
19181
|
-
for (const block of content) {
|
|
19182
|
-
const b = block;
|
|
19183
|
-
if (b?.type === "text" && typeof b.text === "string" && b.text.length > 0) {
|
|
19184
|
-
this.handlers.onReasoning?.(b.text);
|
|
19185
|
-
} else if (b?.type === "thinking" && typeof b.thinking === "string" && b.thinking.length > 0) {
|
|
19186
|
-
this.handlers.onReasoning?.(b.thinking);
|
|
19187
|
-
} else if (b?.type === "tool_use" && typeof b.name === "string") {
|
|
19188
|
-
this.handlers.onTool?.(b.name, toolTarget(b.input));
|
|
19189
|
-
}
|
|
19190
|
-
}
|
|
19191
|
-
} else if (event.type === "result") {
|
|
19192
|
-
this.handlers.onResult?.();
|
|
19605
|
+
return JSON.stringify(entry);
|
|
19606
|
+
}
|
|
19607
|
+
function createLogger(options) {
|
|
19608
|
+
const threshold = LEVEL_ORDER[options.level];
|
|
19609
|
+
const format = options.format === "json" ? formatJson : formatPretty;
|
|
19610
|
+
function emit(level, message, context) {
|
|
19611
|
+
if (LEVEL_ORDER[level] < threshold)
|
|
19612
|
+
return;
|
|
19613
|
+
const line = format(level, message, context);
|
|
19614
|
+
if (level === "error") {
|
|
19615
|
+
console.error(line);
|
|
19616
|
+
} else if (level === "warn") {
|
|
19617
|
+
console.warn(line);
|
|
19618
|
+
} else {
|
|
19619
|
+
console.log(line);
|
|
19193
19620
|
}
|
|
19194
19621
|
}
|
|
19195
|
-
|
|
19196
|
-
|
|
19197
|
-
|
|
19198
|
-
|
|
19199
|
-
|
|
19200
|
-
|
|
19201
|
-
|
|
19202
|
-
|
|
19203
|
-
|
|
19204
|
-
|
|
19205
|
-
|
|
19622
|
+
return {
|
|
19623
|
+
debug: (message, context) => emit("debug", message, context),
|
|
19624
|
+
info: (message, context) => emit("info", message, context),
|
|
19625
|
+
warn: (message, context) => emit("warn", message, context),
|
|
19626
|
+
error: (message, context) => emit("error", message, context)
|
|
19627
|
+
};
|
|
19628
|
+
}
|
|
19629
|
+
var globalLogger = createLogger({ level: "info", format: "pretty" });
|
|
19630
|
+
function setGlobalLogger(logger) {
|
|
19631
|
+
globalLogger = logger;
|
|
19632
|
+
}
|
|
19633
|
+
function getLogger() {
|
|
19634
|
+
return globalLogger;
|
|
19206
19635
|
}
|
|
19207
19636
|
}
|
|
19208
19637
|
});
|
|
19209
19638
|
|
|
19210
|
-
// ../../packages/shuttle/dist/executors/
|
|
19211
|
-
var
|
|
19212
|
-
"../../packages/shuttle/dist/executors/
|
|
19639
|
+
// ../../packages/shuttle/dist/executors/registry.js
|
|
19640
|
+
var require_registry2 = __commonJS({
|
|
19641
|
+
"../../packages/shuttle/dist/executors/registry.js"(exports2) {
|
|
19213
19642
|
"use strict";
|
|
19214
|
-
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
19215
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
19216
|
-
};
|
|
19217
19643
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19218
|
-
exports2.
|
|
19219
|
-
|
|
19220
|
-
|
|
19221
|
-
|
|
19222
|
-
|
|
19223
|
-
|
|
19224
|
-
var DEFAULT_GIT_TIMEOUT_MS = 12e4;
|
|
19225
|
-
var DEFAULT_GIT_PROTOCOLS = "https:ssh:git";
|
|
19226
|
-
async function prepareWorkspace(workspace, options = {}) {
|
|
19227
|
-
const tmpBase = options.tmpDir ?? os_1.default.tmpdir();
|
|
19228
|
-
const resolved = readGitWorkspace(workspace);
|
|
19229
|
-
if ("warning" in resolved) {
|
|
19230
|
-
return degradedWorkspace(resolved.warning, tmpBase);
|
|
19644
|
+
exports2.ExecutorRegistry = void 0;
|
|
19645
|
+
var ExecutorRegistry = class {
|
|
19646
|
+
factories = /* @__PURE__ */ new Map();
|
|
19647
|
+
allowlist;
|
|
19648
|
+
constructor(allowlist = ["*"]) {
|
|
19649
|
+
this.allowlist = allowlist;
|
|
19231
19650
|
}
|
|
19232
|
-
|
|
19233
|
-
|
|
19234
|
-
|
|
19235
|
-
const signal = options.signal;
|
|
19236
|
-
const authEnv = options.gitToken !== void 0 ? buildGitAuthEnv(gitWorkspace.remote, options.gitToken) : void 0;
|
|
19237
|
-
const git = (args, cwd) => runGit(args, cwd, { timeoutMs, signal, allowedProtocols, extraEnv: authEnv });
|
|
19238
|
-
const hasCommit = (dir) => git(["cat-file", "-e", `${gitWorkspace.commit}^{commit}`], dir).then((r) => r.code === 0);
|
|
19239
|
-
const fetchCommit = async (dir, opts) => {
|
|
19240
|
-
if (await hasCommit(dir))
|
|
19241
|
-
return [];
|
|
19242
|
-
const depth = opts.shallow ? ["--depth=1"] : [];
|
|
19243
|
-
const attempts = [];
|
|
19244
|
-
for (const ref of gitWorkspace.fetchRefs ?? []) {
|
|
19245
|
-
attempts.push({
|
|
19246
|
-
source: `${gitWorkspace.remote} ${ref}`,
|
|
19247
|
-
args: ["fetch", "--no-tags", ...depth, gitWorkspace.remote, ref]
|
|
19248
|
-
});
|
|
19249
|
-
}
|
|
19250
|
-
attempts.push({
|
|
19251
|
-
source: `${gitWorkspace.remote} commit`,
|
|
19252
|
-
args: ["fetch", "--no-tags", ...depth, gitWorkspace.remote, gitWorkspace.commit]
|
|
19253
|
-
});
|
|
19254
|
-
const failures2 = [];
|
|
19255
|
-
for (const { source, args } of attempts) {
|
|
19256
|
-
const result = await git(args, dir);
|
|
19257
|
-
if (result.code !== 0) {
|
|
19258
|
-
failures2.push(`${source}: ${firstLine(result.stderr)}`);
|
|
19259
|
-
continue;
|
|
19260
|
-
}
|
|
19261
|
-
if (await hasCommit(dir))
|
|
19262
|
-
return [];
|
|
19263
|
-
failures2.push(`${source}: fetch succeeded but ${gitWorkspace.commit} is still absent`);
|
|
19651
|
+
register(name, factory) {
|
|
19652
|
+
if (!this.isAllowed(name)) {
|
|
19653
|
+
throw new Error(`Executor "${name}" is not enabled for this build. Allowed executors: ${this.allowlist.join(", ")}`);
|
|
19264
19654
|
}
|
|
19265
|
-
|
|
19266
|
-
};
|
|
19267
|
-
const notMaterialized = (failures2) => `could not materialize ${gitWorkspace.commit} from ${gitWorkspace.remote}` + (failures2.length > 0 ? ` (${failures2.join("; ")})` : "");
|
|
19268
|
-
const tempRoot = await fs_1.promises.mkdtemp(path_1.default.join(tmpBase, "shuttle-ws-"));
|
|
19269
|
-
const checkoutDir = path_1.default.join(tempRoot, "checkout");
|
|
19270
|
-
const removeTempRoot = async () => {
|
|
19271
|
-
await fs_1.promises.rm(tempRoot, { recursive: true, force: true }).catch(() => {
|
|
19272
|
-
});
|
|
19273
|
-
};
|
|
19274
|
-
await fs_1.promises.mkdir(checkoutDir);
|
|
19275
|
-
const init = await git(["init", "-q"], checkoutDir);
|
|
19276
|
-
if (init.code !== 0) {
|
|
19277
|
-
await removeTempRoot();
|
|
19278
|
-
return degradedWorkspace(`git init failed: ${firstLine(init.stderr)}`, tmpBase);
|
|
19279
|
-
}
|
|
19280
|
-
const failures = await fetchCommit(checkoutDir, { shallow: true });
|
|
19281
|
-
if (!await hasCommit(checkoutDir)) {
|
|
19282
|
-
await removeTempRoot();
|
|
19283
|
-
return degradedWorkspace(notMaterialized(failures), tmpBase);
|
|
19284
|
-
}
|
|
19285
|
-
const checkout = await git(["checkout", "--detach", gitWorkspace.commit], checkoutDir);
|
|
19286
|
-
if (checkout.code !== 0) {
|
|
19287
|
-
await removeTempRoot();
|
|
19288
|
-
return degradedWorkspace(`git checkout failed: ${firstLine(checkout.stderr)}`, tmpBase);
|
|
19289
|
-
}
|
|
19290
|
-
return { cwd: checkoutDir, materialized: true, cleanup: removeTempRoot };
|
|
19291
|
-
}
|
|
19292
|
-
function buildGitAuthEnv(remote, token) {
|
|
19293
|
-
let origin;
|
|
19294
|
-
try {
|
|
19295
|
-
const url = new URL(remote);
|
|
19296
|
-
if (url.protocol !== "https:")
|
|
19297
|
-
return void 0;
|
|
19298
|
-
origin = `${url.protocol}//${url.host}/`;
|
|
19299
|
-
} catch {
|
|
19300
|
-
return void 0;
|
|
19301
|
-
}
|
|
19302
|
-
const basic = Buffer.from(`x-access-token:${token}`).toString("base64");
|
|
19303
|
-
return {
|
|
19304
|
-
GIT_CONFIG_COUNT: "1",
|
|
19305
|
-
GIT_CONFIG_KEY_0: `http.${origin}.extraheader`,
|
|
19306
|
-
GIT_CONFIG_VALUE_0: `Authorization: Basic ${basic}`
|
|
19307
|
-
};
|
|
19308
|
-
}
|
|
19309
|
-
var COMMIT_PATTERN = /^[0-9a-f]{40}$/i;
|
|
19310
|
-
var REF_PATTERN = /^[A-Za-z0-9][A-Za-z0-9/_.@^~+-]*$/;
|
|
19311
|
-
function readGitWorkspace(workspace) {
|
|
19312
|
-
const git = workspace.git;
|
|
19313
|
-
if (git === void 0) {
|
|
19314
|
-
const offered = Object.keys(workspace).join(", ") || "(none)";
|
|
19315
|
-
return {
|
|
19316
|
-
warning: `workspace carries no namespace this executor understands (offered: ${offered}; supported: git)`
|
|
19317
|
-
};
|
|
19655
|
+
this.factories.set(name, factory);
|
|
19318
19656
|
}
|
|
19319
|
-
|
|
19320
|
-
|
|
19657
|
+
create(name, config) {
|
|
19658
|
+
const factory = this.factories.get(name);
|
|
19659
|
+
if (!factory) {
|
|
19660
|
+
throw new Error(`Executor not registered: ${name}`);
|
|
19661
|
+
}
|
|
19662
|
+
return factory(config, name);
|
|
19321
19663
|
}
|
|
19322
|
-
|
|
19323
|
-
return
|
|
19664
|
+
has(name) {
|
|
19665
|
+
return this.factories.has(name);
|
|
19324
19666
|
}
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
return { warning: "workspace.git.fetchRefs must be an array of ref strings" };
|
|
19328
|
-
}
|
|
19329
|
-
const bad = git.fetchRefs.find((r) => !REF_PATTERN.test(r));
|
|
19330
|
-
if (bad !== void 0) {
|
|
19331
|
-
return { warning: `workspace.git.fetchRefs contains an invalid ref: ${JSON.stringify(bad)}` };
|
|
19332
|
-
}
|
|
19667
|
+
get allowedExecutors() {
|
|
19668
|
+
return this.allowlist;
|
|
19333
19669
|
}
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
19340
|
-
materialized: false,
|
|
19341
|
-
warning,
|
|
19342
|
-
cleanup: async () => {
|
|
19343
|
-
await fs_1.promises.rm(scratchDir, { recursive: true, force: true }).catch(() => {
|
|
19344
|
-
});
|
|
19345
|
-
}
|
|
19346
|
-
};
|
|
19347
|
-
}
|
|
19348
|
-
function runGit(args, cwd, opts) {
|
|
19349
|
-
return new Promise((resolve) => {
|
|
19350
|
-
const child = (0, child_process_1.spawn)("git", args, {
|
|
19351
|
-
cwd,
|
|
19352
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
19353
|
-
// `extraEnv` (e.g. the credential extraheader) goes first so the two
|
|
19354
|
-
// security-critical vars below always win and can't be overridden.
|
|
19355
|
-
env: {
|
|
19356
|
-
...process.env,
|
|
19357
|
-
...opts.extraEnv ?? {},
|
|
19358
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
19359
|
-
// Transport allowlist. `remote` passes readGitWorkspace as any
|
|
19360
|
-
// non-empty non-`-` string, which still admits `ext::sh -c …` and
|
|
19361
|
-
// `fd::` remotes that execute arbitrary commands on this host.
|
|
19362
|
-
// Restricting to the transports we actually use turns those into a
|
|
19363
|
-
// fast failure (→ degrade) rather than RCE across the boundary this
|
|
19364
|
-
// executor is meant to defend.
|
|
19365
|
-
GIT_ALLOW_PROTOCOL: opts.allowedProtocols
|
|
19670
|
+
/** All registered factories as JobExecutors built with their default config. */
|
|
19671
|
+
build(configs) {
|
|
19672
|
+
const out = [];
|
|
19673
|
+
for (const [name, cfg] of Object.entries(configs)) {
|
|
19674
|
+
if (!this.factories.has(name)) {
|
|
19675
|
+
throw new Error(`Executor "${name}" in config is not registered or not enabled for this build.`);
|
|
19366
19676
|
}
|
|
19367
|
-
|
|
19368
|
-
const stdout = [];
|
|
19369
|
-
const stderr = [];
|
|
19370
|
-
let settled = false;
|
|
19371
|
-
const settle = (result) => {
|
|
19372
|
-
if (settled)
|
|
19373
|
-
return;
|
|
19374
|
-
settled = true;
|
|
19375
|
-
clearTimeout(timer);
|
|
19376
|
-
opts.signal?.removeEventListener("abort", onAbort);
|
|
19377
|
-
resolve(result);
|
|
19378
|
-
};
|
|
19379
|
-
const killChild = () => {
|
|
19380
|
-
child.kill("SIGTERM");
|
|
19381
|
-
const escalate = setTimeout(() => child.kill("SIGKILL"), 5e3);
|
|
19382
|
-
escalate.unref();
|
|
19383
|
-
child.once("close", () => clearTimeout(escalate));
|
|
19384
|
-
};
|
|
19385
|
-
const timer = setTimeout(() => {
|
|
19386
|
-
killChild();
|
|
19387
|
-
settle({
|
|
19388
|
-
code: -1,
|
|
19389
|
-
stdout: Buffer.concat(stdout).toString("utf-8"),
|
|
19390
|
-
stderr: `git ${args[0]} timed out after ${opts.timeoutMs}ms`
|
|
19391
|
-
});
|
|
19392
|
-
}, opts.timeoutMs);
|
|
19393
|
-
const onAbort = () => {
|
|
19394
|
-
killChild();
|
|
19395
|
-
settle({
|
|
19396
|
-
code: -1,
|
|
19397
|
-
stdout: Buffer.concat(stdout).toString("utf-8"),
|
|
19398
|
-
stderr: "aborted"
|
|
19399
|
-
});
|
|
19400
|
-
};
|
|
19401
|
-
if (opts.signal?.aborted) {
|
|
19402
|
-
onAbort();
|
|
19403
|
-
return;
|
|
19677
|
+
out.push(this.create(name, cfg));
|
|
19404
19678
|
}
|
|
19405
|
-
|
|
19406
|
-
|
|
19407
|
-
|
|
19408
|
-
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
|
|
19413
|
-
|
|
19414
|
-
|
|
19415
|
-
|
|
19416
|
-
|
|
19417
|
-
|
|
19418
|
-
|
|
19679
|
+
return out;
|
|
19680
|
+
}
|
|
19681
|
+
isAllowed(name) {
|
|
19682
|
+
return this.allowlist[0] === "*" || this.allowlist.includes(name);
|
|
19683
|
+
}
|
|
19684
|
+
};
|
|
19685
|
+
exports2.ExecutorRegistry = ExecutorRegistry;
|
|
19686
|
+
}
|
|
19687
|
+
});
|
|
19688
|
+
|
|
19689
|
+
// ../../packages/shuttle/dist/executors/validate-output.js
|
|
19690
|
+
var require_validate_output = __commonJS({
|
|
19691
|
+
"../../packages/shuttle/dist/executors/validate-output.js"(exports2) {
|
|
19692
|
+
"use strict";
|
|
19693
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19694
|
+
exports2.assertOutputValid = assertOutputValid;
|
|
19695
|
+
var loom_1 = require_dist3();
|
|
19696
|
+
function assertOutputValid(spec, parsed) {
|
|
19697
|
+
const { valid, errors } = (0, loom_1.validateAgainstSchema)(parsed ?? null, spec.outputSchema);
|
|
19698
|
+
if (!valid) {
|
|
19699
|
+
throw new Error(`prompt-execution output failed schema validation: ${errors.join("; ")}`);
|
|
19700
|
+
}
|
|
19419
19701
|
}
|
|
19420
19702
|
}
|
|
19421
19703
|
});
|
|
19422
19704
|
|
|
19423
|
-
// ../../packages/shuttle/dist/executors/
|
|
19424
|
-
var
|
|
19425
|
-
"../../packages/shuttle/dist/executors/
|
|
19705
|
+
// ../../packages/shuttle/dist/executors/progress.js
|
|
19706
|
+
var require_progress = __commonJS({
|
|
19707
|
+
"../../packages/shuttle/dist/executors/progress.js"(exports2) {
|
|
19426
19708
|
"use strict";
|
|
19427
|
-
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
19428
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
19429
|
-
};
|
|
19430
19709
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19431
|
-
exports2.
|
|
19432
|
-
var
|
|
19433
|
-
|
|
19434
|
-
|
|
19435
|
-
|
|
19436
|
-
|
|
19437
|
-
|
|
19438
|
-
|
|
19439
|
-
|
|
19440
|
-
|
|
19441
|
-
|
|
19442
|
-
|
|
19443
|
-
|
|
19444
|
-
|
|
19445
|
-
|
|
19446
|
-
|
|
19447
|
-
|
|
19448
|
-
|
|
19449
|
-
|
|
19450
|
-
if (
|
|
19451
|
-
|
|
19452
|
-
|
|
19453
|
-
|
|
19454
|
-
let origin;
|
|
19455
|
-
try {
|
|
19456
|
-
const u = new URL(file.url);
|
|
19457
|
-
if (u.protocol !== "https:" && u.protocol !== "http:") {
|
|
19458
|
-
skip(`unsupported url protocol ${u.protocol}`);
|
|
19459
|
-
continue;
|
|
19460
|
-
}
|
|
19461
|
-
origin = normalizeOrigin(u.origin);
|
|
19462
|
-
} catch {
|
|
19463
|
-
skip("invalid url");
|
|
19464
|
-
continue;
|
|
19465
|
-
}
|
|
19466
|
-
if (!origin || !allowed.has(origin)) {
|
|
19467
|
-
skip(`origin not in the overlay allowlist (${origin})`);
|
|
19468
|
-
continue;
|
|
19710
|
+
exports2.StreamJsonTap = exports2.ProgressReporter = void 0;
|
|
19711
|
+
var ProgressReporter = class {
|
|
19712
|
+
sink;
|
|
19713
|
+
seq = 0;
|
|
19714
|
+
pending = [];
|
|
19715
|
+
textBuf = "";
|
|
19716
|
+
timer = null;
|
|
19717
|
+
closed = false;
|
|
19718
|
+
flushMs;
|
|
19719
|
+
maxChars;
|
|
19720
|
+
constructor(sink, options = {}) {
|
|
19721
|
+
this.sink = sink;
|
|
19722
|
+
this.flushMs = options.flushMs ?? 400;
|
|
19723
|
+
this.maxChars = options.maxChars ?? 1200;
|
|
19724
|
+
}
|
|
19725
|
+
reasoning(text) {
|
|
19726
|
+
if (this.closed || !text)
|
|
19727
|
+
return;
|
|
19728
|
+
this.textBuf += text;
|
|
19729
|
+
if (this.textBuf.length >= this.maxChars) {
|
|
19730
|
+
this.flush();
|
|
19731
|
+
} else {
|
|
19732
|
+
this.arm();
|
|
19469
19733
|
}
|
|
19734
|
+
}
|
|
19735
|
+
tool(name, target) {
|
|
19736
|
+
if (this.closed || !name)
|
|
19737
|
+
return;
|
|
19738
|
+
this.drainText();
|
|
19739
|
+
this.push({ kind: "tool", tool: target ? { name, target } : { name } });
|
|
19740
|
+
this.flush();
|
|
19741
|
+
}
|
|
19742
|
+
/** Flush pending text + frames now. */
|
|
19743
|
+
flush() {
|
|
19744
|
+
if (this.closed)
|
|
19745
|
+
return;
|
|
19746
|
+
this.drainText();
|
|
19747
|
+
this.clearTimer();
|
|
19748
|
+
if (this.pending.length === 0)
|
|
19749
|
+
return;
|
|
19750
|
+
const batch = this.pending;
|
|
19751
|
+
this.pending = [];
|
|
19470
19752
|
try {
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
await fs_1.promises.writeFile(dest, content);
|
|
19474
|
-
result.written += 1;
|
|
19475
|
-
result.writtenPaths.push(file.path);
|
|
19476
|
-
} catch (err) {
|
|
19477
|
-
skip(err instanceof Error ? err.message : String(err));
|
|
19753
|
+
this.sink(batch);
|
|
19754
|
+
} catch {
|
|
19478
19755
|
}
|
|
19479
19756
|
}
|
|
19480
|
-
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
|
|
19485
|
-
const resolved = path_1.default.resolve(rootReal, relPath);
|
|
19486
|
-
const rootWithSep = rootReal.endsWith(path_1.default.sep) ? rootReal : rootReal + path_1.default.sep;
|
|
19487
|
-
if (resolved !== rootReal && !resolved.startsWith(rootWithSep))
|
|
19488
|
-
return void 0;
|
|
19489
|
-
if (relPath.split(/[/\\]/).includes(".."))
|
|
19490
|
-
return void 0;
|
|
19491
|
-
return resolved;
|
|
19492
|
-
}
|
|
19493
|
-
function normalizeOrigin(value) {
|
|
19494
|
-
try {
|
|
19495
|
-
const u = new URL(value.includes("://") ? value : `https://${value}`);
|
|
19496
|
-
return u.origin;
|
|
19497
|
-
} catch {
|
|
19498
|
-
return void 0;
|
|
19757
|
+
/** Final flush; no further frames are emitted after this. */
|
|
19758
|
+
close() {
|
|
19759
|
+
this.flush();
|
|
19760
|
+
this.closed = true;
|
|
19761
|
+
this.clearTimer();
|
|
19499
19762
|
}
|
|
19500
|
-
|
|
19501
|
-
|
|
19502
|
-
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
19507
|
-
|
|
19508
|
-
|
|
19509
|
-
|
|
19510
|
-
|
|
19511
|
-
|
|
19512
|
-
|
|
19513
|
-
|
|
19514
|
-
|
|
19515
|
-
|
|
19763
|
+
drainText() {
|
|
19764
|
+
if (this.textBuf.length === 0)
|
|
19765
|
+
return;
|
|
19766
|
+
this.push({ kind: "reasoning", text: this.textBuf });
|
|
19767
|
+
this.textBuf = "";
|
|
19768
|
+
}
|
|
19769
|
+
push(frame) {
|
|
19770
|
+
this.pending.push({ seq: this.seq++, at: (/* @__PURE__ */ new Date()).toISOString(), ...frame });
|
|
19771
|
+
}
|
|
19772
|
+
arm() {
|
|
19773
|
+
if (this.timer)
|
|
19774
|
+
return;
|
|
19775
|
+
this.timer = setTimeout(() => {
|
|
19776
|
+
this.timer = null;
|
|
19777
|
+
this.flush();
|
|
19778
|
+
}, this.flushMs);
|
|
19779
|
+
this.timer.unref?.();
|
|
19780
|
+
}
|
|
19781
|
+
clearTimer() {
|
|
19782
|
+
if (this.timer) {
|
|
19783
|
+
clearTimeout(this.timer);
|
|
19784
|
+
this.timer = null;
|
|
19516
19785
|
}
|
|
19517
|
-
|
|
19518
|
-
|
|
19519
|
-
|
|
19520
|
-
|
|
19521
|
-
|
|
19522
|
-
|
|
19523
|
-
|
|
19524
|
-
|
|
19786
|
+
}
|
|
19787
|
+
};
|
|
19788
|
+
exports2.ProgressReporter = ProgressReporter;
|
|
19789
|
+
var StreamJsonTap = class {
|
|
19790
|
+
handlers;
|
|
19791
|
+
buf = "";
|
|
19792
|
+
constructor(handlers) {
|
|
19793
|
+
this.handlers = handlers;
|
|
19794
|
+
}
|
|
19795
|
+
/** Feed a raw stdout chunk. Emits handlers for every complete line parsed. */
|
|
19796
|
+
push(chunk) {
|
|
19797
|
+
this.buf += chunk;
|
|
19798
|
+
let nl;
|
|
19799
|
+
while ((nl = this.buf.indexOf("\n")) !== -1) {
|
|
19800
|
+
const line = this.buf.slice(0, nl);
|
|
19801
|
+
this.buf = this.buf.slice(nl + 1);
|
|
19802
|
+
this.consumeLine(line);
|
|
19525
19803
|
}
|
|
19526
|
-
return Buffer.concat(chunks);
|
|
19527
|
-
} finally {
|
|
19528
|
-
clearTimeout(timer);
|
|
19529
|
-
opts.signal?.removeEventListener("abort", onParentAbort);
|
|
19530
19804
|
}
|
|
19531
|
-
|
|
19532
|
-
|
|
19533
|
-
|
|
19534
|
-
|
|
19535
|
-
|
|
19536
|
-
var require_env_ref = __commonJS({
|
|
19537
|
-
"../../packages/shuttle/dist/config/env-ref.js"(exports2) {
|
|
19538
|
-
"use strict";
|
|
19539
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19540
|
-
exports2.resolveEnvRef = resolveEnvRef;
|
|
19541
|
-
exports2.isEnvRef = isEnvRef;
|
|
19542
|
-
exports2.resolveConfigStringOrEnv = resolveConfigStringOrEnv;
|
|
19543
|
-
var ENV_REF_PATTERN = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/;
|
|
19544
|
-
function resolveEnvRef(value, fieldName) {
|
|
19545
|
-
const match = ENV_REF_PATTERN.exec(value);
|
|
19546
|
-
if (!match)
|
|
19547
|
-
return value;
|
|
19548
|
-
const varName = match[1];
|
|
19549
|
-
const resolved = process.env[varName];
|
|
19550
|
-
if (resolved === void 0 || resolved === "") {
|
|
19551
|
-
const subject = fieldName ? `${fieldName} (${value})` : value;
|
|
19552
|
-
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.`);
|
|
19805
|
+
/** Flush a trailing partial line (best-effort; usually empty at process close). */
|
|
19806
|
+
end() {
|
|
19807
|
+
if (this.buf.trim().length > 0)
|
|
19808
|
+
this.consumeLine(this.buf);
|
|
19809
|
+
this.buf = "";
|
|
19553
19810
|
}
|
|
19554
|
-
|
|
19555
|
-
|
|
19556
|
-
|
|
19557
|
-
|
|
19558
|
-
|
|
19559
|
-
|
|
19560
|
-
|
|
19561
|
-
|
|
19562
|
-
|
|
19811
|
+
consumeLine(line) {
|
|
19812
|
+
const trimmed = line.trim();
|
|
19813
|
+
if (!trimmed.startsWith("{"))
|
|
19814
|
+
return;
|
|
19815
|
+
let event;
|
|
19816
|
+
try {
|
|
19817
|
+
event = JSON.parse(trimmed);
|
|
19818
|
+
} catch {
|
|
19819
|
+
return;
|
|
19820
|
+
}
|
|
19821
|
+
if (event.type === "assistant") {
|
|
19822
|
+
const content = event.message?.content;
|
|
19823
|
+
if (!Array.isArray(content))
|
|
19824
|
+
return;
|
|
19825
|
+
for (const block of content) {
|
|
19826
|
+
const b = block;
|
|
19827
|
+
if (b?.type === "text" && typeof b.text === "string" && b.text.length > 0) {
|
|
19828
|
+
this.handlers.onReasoning?.(b.text);
|
|
19829
|
+
} else if (b?.type === "thinking" && typeof b.thinking === "string" && b.thinking.length > 0) {
|
|
19830
|
+
this.handlers.onReasoning?.(b.thinking);
|
|
19831
|
+
} else if (b?.type === "tool_use" && typeof b.name === "string") {
|
|
19832
|
+
this.handlers.onTool?.(b.name, toolTarget(b.input));
|
|
19833
|
+
}
|
|
19834
|
+
}
|
|
19835
|
+
} else if (event.type === "result") {
|
|
19836
|
+
this.handlers.onResult?.();
|
|
19837
|
+
}
|
|
19563
19838
|
}
|
|
19564
|
-
|
|
19565
|
-
|
|
19566
|
-
|
|
19839
|
+
};
|
|
19840
|
+
exports2.StreamJsonTap = StreamJsonTap;
|
|
19841
|
+
function toolTarget(input) {
|
|
19842
|
+
if (!input)
|
|
19843
|
+
return void 0;
|
|
19844
|
+
for (const key of ["file_path", "path", "pattern", "command", "url", "query", "notebook_path"]) {
|
|
19845
|
+
const v = input[key];
|
|
19846
|
+
if (typeof v === "string" && v.length > 0)
|
|
19847
|
+
return v;
|
|
19567
19848
|
}
|
|
19568
|
-
|
|
19849
|
+
return void 0;
|
|
19569
19850
|
}
|
|
19570
19851
|
}
|
|
19571
19852
|
});
|
|
@@ -19588,12 +19869,8 @@ var require_claude_code = __commonJS({
|
|
|
19588
19869
|
var path_1 = __importDefault(require("path"));
|
|
19589
19870
|
var string_decoder_1 = require("string_decoder");
|
|
19590
19871
|
var loom_1 = require_dist3();
|
|
19591
|
-
var keep_1 = require_dist4();
|
|
19592
19872
|
var validate_output_1 = require_validate_output();
|
|
19593
19873
|
var progress_1 = require_progress();
|
|
19594
|
-
var workspace_1 = require_workspace();
|
|
19595
|
-
var overlay_1 = require_overlay();
|
|
19596
|
-
var env_ref_1 = require_env_ref();
|
|
19597
19874
|
var apply_1 = require_apply();
|
|
19598
19875
|
var DEFAULT_TIMEOUT = 6e5;
|
|
19599
19876
|
var ClaudeCodeExecutor = class {
|
|
@@ -19611,47 +19888,49 @@ var require_claude_code = __commonJS({
|
|
|
19611
19888
|
outputTokens: spec.costCapHint?.estimatedOutputTokens
|
|
19612
19889
|
};
|
|
19613
19890
|
}
|
|
19891
|
+
/**
|
|
19892
|
+
* Declare the run environment to the runner (ADR 0026): the opaque workspace
|
|
19893
|
+
* bag plus the materialization config (tmp dir, overlay allowlist). No
|
|
19894
|
+
* credentials here (ADR 0027) — the runner's broker reveals them on demand:
|
|
19895
|
+
* this executor pulls its model key from `ctx.credentials` in `execute()`,
|
|
19896
|
+
* and the git request references its own fetch credential. The executor
|
|
19897
|
+
* touches neither git nor Keep.
|
|
19898
|
+
*/
|
|
19899
|
+
async prepareEnvironment(dispatch) {
|
|
19900
|
+
const spec = (0, loom_1.readPromptExecutionSpec)(dispatch.inputs);
|
|
19901
|
+
const tmpBase = this.config.tmpDir ? (0, apply_1.expandHome)(this.config.tmpDir) : void 0;
|
|
19902
|
+
return {
|
|
19903
|
+
...spec.workspace ? { workspaceRequest: spec.workspace } : {},
|
|
19904
|
+
materializeOptions: {
|
|
19905
|
+
...tmpBase ? { tmpDir: tmpBase } : {},
|
|
19906
|
+
overlayAllowedHosts: this.config.overlayAllowedHosts ?? []
|
|
19907
|
+
}
|
|
19908
|
+
};
|
|
19909
|
+
}
|
|
19614
19910
|
async execute(dispatch, signal, ctx) {
|
|
19615
19911
|
const { prompt, spec } = (0, loom_1.renderPromptExecution)(dispatch.inputs);
|
|
19616
|
-
|
|
19617
|
-
|
|
19912
|
+
const workspace = ctx?.workspace ?? null;
|
|
19913
|
+
let modelKey = null;
|
|
19914
|
+
if (ctx?.credentials) {
|
|
19618
19915
|
const hints = (0, loom_1.pickProviderHints)(spec, "anthropic-api");
|
|
19619
19916
|
const customerId = typeof hints.customerId === "string" ? hints.customerId : void 0;
|
|
19620
19917
|
if (!customerId) {
|
|
19621
19918
|
throw new Error("claude-code dynamic-key mode requires providerHints.anthropicApi.customerId on every job");
|
|
19622
19919
|
}
|
|
19623
|
-
|
|
19624
|
-
customerId,
|
|
19625
|
-
needGithubToken: !!spec.workspace
|
|
19626
|
-
});
|
|
19920
|
+
modelKey = await ctx.credentials.reveal({ name: "managed-model-key", principal: customerId });
|
|
19627
19921
|
}
|
|
19628
19922
|
let jobScratch = null;
|
|
19629
19923
|
let configDir;
|
|
19630
|
-
if (
|
|
19924
|
+
if (modelKey) {
|
|
19631
19925
|
jobScratch = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "shuttle-cc-"));
|
|
19632
19926
|
configDir = path_1.default.join(jobScratch, "claude-config");
|
|
19633
19927
|
await fs_1.promises.mkdir(configDir);
|
|
19634
19928
|
}
|
|
19635
19929
|
const tmpBase = this.config.tmpDir ? (0, apply_1.expandHome)(this.config.tmpDir) : os_1.default.tmpdir();
|
|
19636
|
-
let workspace = null;
|
|
19637
19930
|
let promptOnlyScratch = null;
|
|
19638
|
-
let overlayResult = null;
|
|
19639
19931
|
const reporter = spec.progressChannel && ctx?.reportProgress ? new progress_1.ProgressReporter((frames) => ctx.reportProgress(spec.progressChannel, frames)) : null;
|
|
19640
19932
|
try {
|
|
19641
|
-
|
|
19642
|
-
workspace = await (0, workspace_1.prepareWorkspace)(spec.workspace, {
|
|
19643
|
-
signal,
|
|
19644
|
-
tmpDir: tmpBase,
|
|
19645
|
-
...credentials?.githubToken ? { gitToken: credentials.githubToken } : {}
|
|
19646
|
-
});
|
|
19647
|
-
if (spec.workspace.overlay) {
|
|
19648
|
-
overlayResult = await (0, overlay_1.applyOverlay)(workspace.cwd, spec.workspace.overlay, {
|
|
19649
|
-
allowedHosts: this.config.overlayAllowedHosts ?? [],
|
|
19650
|
-
signal
|
|
19651
|
-
});
|
|
19652
|
-
}
|
|
19653
|
-
}
|
|
19654
|
-
const childEnv = buildChildEnv(credentials, configDir);
|
|
19933
|
+
const childEnv = buildChildEnv(modelKey, configDir);
|
|
19655
19934
|
const claudeHints = (0, loom_1.pickProviderHints)(spec, "claude-code");
|
|
19656
19935
|
const hintedModel = typeof claudeHints.model === "string" && claudeHints.model.length > 0 ? claudeHints.model : void 0;
|
|
19657
19936
|
if (!workspace) {
|
|
@@ -19661,7 +19940,7 @@ var require_claude_code = __commonJS({
|
|
|
19661
19940
|
const { response, readPaths, toolText } = await this.runClaude(prompt, spec, runCwd, childEnv, signal, hintedModel, reporter);
|
|
19662
19941
|
if (workspace) {
|
|
19663
19942
|
const reads = [...readPaths];
|
|
19664
|
-
for (const rel of
|
|
19943
|
+
for (const rel of workspace.overlay?.writtenPaths ?? []) {
|
|
19665
19944
|
if (toolText.includes(rel))
|
|
19666
19945
|
reads.push(path_1.default.join(runCwd, rel));
|
|
19667
19946
|
}
|
|
@@ -19674,10 +19953,17 @@ var require_claude_code = __commonJS({
|
|
|
19674
19953
|
content: {
|
|
19675
19954
|
materialized: workspace.materialized,
|
|
19676
19955
|
...workspace.warning ? { warning: workspace.warning } : {},
|
|
19677
|
-
...
|
|
19956
|
+
...workspace.context.length > 0 ? {
|
|
19957
|
+
context: workspace.context.map((c) => ({
|
|
19958
|
+
name: c.requestedName,
|
|
19959
|
+
materialized: c.materialized,
|
|
19960
|
+
...c.warning ? { warning: c.warning } : {}
|
|
19961
|
+
}))
|
|
19962
|
+
} : {},
|
|
19963
|
+
...workspace.overlay ? {
|
|
19678
19964
|
overlay: {
|
|
19679
|
-
written:
|
|
19680
|
-
...
|
|
19965
|
+
written: workspace.overlay.written,
|
|
19966
|
+
...workspace.overlay.skipped.length > 0 ? { skipped: workspace.overlay.skipped } : {}
|
|
19681
19967
|
}
|
|
19682
19968
|
} : {}
|
|
19683
19969
|
}
|
|
@@ -19686,8 +19972,6 @@ var require_claude_code = __commonJS({
|
|
|
19686
19972
|
return response;
|
|
19687
19973
|
} finally {
|
|
19688
19974
|
reporter?.close();
|
|
19689
|
-
if (workspace)
|
|
19690
|
-
await workspace.cleanup();
|
|
19691
19975
|
if (promptOnlyScratch)
|
|
19692
19976
|
await fs_1.promises.rm(promptOnlyScratch, { recursive: true, force: true }).catch(() => {
|
|
19693
19977
|
});
|
|
@@ -19767,18 +20051,27 @@ var require_claude_code = __commonJS({
|
|
|
19767
20051
|
onResult: () => reporter.flush()
|
|
19768
20052
|
}) : null;
|
|
19769
20053
|
const decoder = tap ? new string_decoder_1.StringDecoder("utf8") : null;
|
|
20054
|
+
child.stdin.write(prompt);
|
|
20055
|
+
child.stdin.end();
|
|
20056
|
+
const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;
|
|
20057
|
+
let timer;
|
|
20058
|
+
const armIdleTimer = () => {
|
|
20059
|
+
if (settled)
|
|
20060
|
+
return;
|
|
20061
|
+
if (timer)
|
|
20062
|
+
clearTimeout(timer);
|
|
20063
|
+
timer = setTimeout(() => {
|
|
20064
|
+
child.kill("SIGTERM");
|
|
20065
|
+
settle(() => reject(new Error(`Claude Code produced no output for ${timeout}ms; killed as hung`)));
|
|
20066
|
+
}, timeout);
|
|
20067
|
+
};
|
|
20068
|
+
armIdleTimer();
|
|
19770
20069
|
child.stdout.on("data", (chunk) => {
|
|
19771
20070
|
stdoutChunks.push(chunk);
|
|
20071
|
+
armIdleTimer();
|
|
19772
20072
|
if (tap && decoder)
|
|
19773
20073
|
tap.push(decoder.write(chunk));
|
|
19774
20074
|
});
|
|
19775
|
-
child.stdin.write(prompt);
|
|
19776
|
-
child.stdin.end();
|
|
19777
|
-
const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;
|
|
19778
|
-
const timer = setTimeout(() => {
|
|
19779
|
-
child.kill("SIGTERM");
|
|
19780
|
-
settle(() => reject(new Error(`Claude Code timed out after ${timeout}ms`)));
|
|
19781
|
-
}, timeout);
|
|
19782
20075
|
function onAbort() {
|
|
19783
20076
|
child.kill("SIGTERM");
|
|
19784
20077
|
settle(() => reject(new Error("Dispatch aborted")));
|
|
@@ -19850,7 +20143,7 @@ var require_claude_code = __commonJS({
|
|
|
19850
20143
|
});
|
|
19851
20144
|
}
|
|
19852
20145
|
};
|
|
19853
|
-
function createClaudeCodeExecutor(config, capabilityId = "claude-code"
|
|
20146
|
+
function createClaudeCodeExecutor(config, capabilityId = "claude-code") {
|
|
19854
20147
|
const validated = { capabilityId };
|
|
19855
20148
|
if (config.tmpDir !== void 0) {
|
|
19856
20149
|
if (typeof config.tmpDir !== "string" || config.tmpDir.length === 0) {
|
|
@@ -19881,9 +20174,6 @@ var require_claude_code = __commonJS({
|
|
|
19881
20174
|
}
|
|
19882
20175
|
validated.timeout = config.timeout;
|
|
19883
20176
|
}
|
|
19884
|
-
if (config.apiKeyFrom !== void 0) {
|
|
19885
|
-
validated.resolveCredentials = buildCredentialResolver(config, deps);
|
|
19886
|
-
}
|
|
19887
20177
|
if (config.overlayAllowedHosts !== void 0) {
|
|
19888
20178
|
if (!Array.isArray(config.overlayAllowedHosts) || !config.overlayAllowedHosts.every((h) => typeof h === "string")) {
|
|
19889
20179
|
throw new Error('"overlayAllowedHosts" must be an array of strings');
|
|
@@ -19892,43 +20182,15 @@ var require_claude_code = __commonJS({
|
|
|
19892
20182
|
}
|
|
19893
20183
|
return new ClaudeCodeExecutor(validated);
|
|
19894
20184
|
}
|
|
19895
|
-
function buildChildEnv(
|
|
19896
|
-
if (!
|
|
20185
|
+
function buildChildEnv(modelKey, configDir) {
|
|
20186
|
+
if (!modelKey && !configDir)
|
|
19897
20187
|
return void 0;
|
|
19898
20188
|
return {
|
|
19899
20189
|
...process.env,
|
|
19900
|
-
...
|
|
20190
|
+
...modelKey ? { ANTHROPIC_API_KEY: modelKey } : {},
|
|
19901
20191
|
...configDir ? { CLAUDE_CONFIG_DIR: configDir } : {}
|
|
19902
20192
|
};
|
|
19903
20193
|
}
|
|
19904
|
-
function buildCredentialResolver(config, deps) {
|
|
19905
|
-
if (config.apiKeyFrom !== "job-metadata") {
|
|
19906
|
-
throw new Error(`Unsupported "apiKeyFrom" value: ${JSON.stringify(config.apiKeyFrom)}. Only "job-metadata" is supported.`);
|
|
19907
|
-
}
|
|
19908
|
-
const keepApiUrl = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepApiUrl", "KEEP_API_URL");
|
|
19909
|
-
const clientId = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepClientId", "KEEP_CLIENT_ID");
|
|
19910
|
-
const clientSecret = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepClientSecret", "KEEP_CLIENT_SECRET");
|
|
19911
|
-
const stmFactory = deps.buildServiceTokenManager ?? ((cfg) => new keep_1.ServiceTokenManager(cfg));
|
|
19912
|
-
const stm = stmFactory({ keepApiUrl, clientId, clientSecret });
|
|
19913
|
-
const keepClientFactory = deps.buildKeepClient ?? ((cfg) => new keep_1.KeepClient(cfg));
|
|
19914
|
-
return async ({ customerId, needGithubToken }) => {
|
|
19915
|
-
const serviceToken = await stm.getToken();
|
|
19916
|
-
if (!serviceToken) {
|
|
19917
|
-
throw new Error("claude-code dynamic-key mode failed to exchange a Keep service token. Check KEEP_CLIENT_ID / KEEP_CLIENT_SECRET.");
|
|
19918
|
-
}
|
|
19919
|
-
const keep = keepClientFactory({ baseUrl: keepApiUrl, serviceToken });
|
|
19920
|
-
const reveal = await keep.revealManagedSifterKey(customerId);
|
|
19921
|
-
const credentials = { anthropicApiKey: reveal.apiKey };
|
|
19922
|
-
if (needGithubToken) {
|
|
19923
|
-
try {
|
|
19924
|
-
const gh = await keep.getUserGithubToken(customerId);
|
|
19925
|
-
credentials.githubToken = gh.access_token;
|
|
19926
|
-
} catch {
|
|
19927
|
-
}
|
|
19928
|
-
}
|
|
19929
|
-
return credentials;
|
|
19930
|
-
};
|
|
19931
|
-
}
|
|
19932
20194
|
function parseStreamJson(stdout) {
|
|
19933
20195
|
let finalText = null;
|
|
19934
20196
|
const readPaths = [];
|
|
@@ -21173,12 +21435,14 @@ var require_shuttle = __commonJS({
|
|
|
21173
21435
|
var decommission_1 = require_decommission();
|
|
21174
21436
|
var loom_1 = require_dist3();
|
|
21175
21437
|
var chain_1 = require_chain();
|
|
21438
|
+
var workspace_1 = require_workspace();
|
|
21439
|
+
var credentials_1 = require_credentials();
|
|
21176
21440
|
var output_gates_1 = require_output_gates();
|
|
21177
21441
|
var keep_1 = require_dist4();
|
|
21178
21442
|
var apply_1 = require_apply();
|
|
21179
21443
|
var version_check_1 = require_version_check();
|
|
21180
21444
|
var logger_1 = require_logger();
|
|
21181
|
-
var registry_1 =
|
|
21445
|
+
var registry_1 = require_registry2();
|
|
21182
21446
|
var claude_code_1 = require_claude_code();
|
|
21183
21447
|
var http_api_1 = require_http_api();
|
|
21184
21448
|
var anthropic_api_1 = require_anthropic_api();
|
|
@@ -21223,12 +21487,20 @@ var require_shuttle = __commonJS({
|
|
|
21223
21487
|
brand;
|
|
21224
21488
|
config;
|
|
21225
21489
|
profile;
|
|
21490
|
+
credentialDeps;
|
|
21226
21491
|
subscription = null;
|
|
21227
21492
|
spendTracker = null;
|
|
21493
|
+
/**
|
|
21494
|
+
* The runner-owned credential broker (ADR 0027), built once from the
|
|
21495
|
+
* `credentials` config (the reveal service identity). Null in local mode
|
|
21496
|
+
* (no such config) → consumers reveal nothing and use ambient credentials.
|
|
21497
|
+
*/
|
|
21498
|
+
broker = null;
|
|
21228
21499
|
constructor(options) {
|
|
21229
21500
|
this.brand = options.brand;
|
|
21230
21501
|
this.config = options.config;
|
|
21231
21502
|
this.profile = options.profile ?? apply_1.DEFAULT_PROFILE;
|
|
21503
|
+
this.credentialDeps = options.credentialDeps ?? {};
|
|
21232
21504
|
}
|
|
21233
21505
|
/** Exposed so the CLI's `caps set` can reload after a write. */
|
|
21234
21506
|
get spendTrackerHandle() {
|
|
@@ -21242,6 +21514,7 @@ var require_shuttle = __commonJS({
|
|
|
21242
21514
|
const logger = (0, logger_1.getLogger)();
|
|
21243
21515
|
logger.info(`${this.brand.product.title} starting...`);
|
|
21244
21516
|
const paths = (0, apply_1.resolvePaths)(this.brand, this.profile);
|
|
21517
|
+
this.broker = (0, credentials_1.createCredentialBroker)(this.config.credentials, this.credentialDeps);
|
|
21245
21518
|
const enforceMinVersion = (response) => {
|
|
21246
21519
|
(0, version_check_1.checkResponseMinVersion)({
|
|
21247
21520
|
response,
|
|
@@ -21315,11 +21588,30 @@ var require_shuttle = __commonJS({
|
|
|
21315
21588
|
}
|
|
21316
21589
|
await observerChain.notify({ kind: "executor.dispatch.started", jobId, attendanceId, executor: executor.capability.id, strategy }, onObserverError);
|
|
21317
21590
|
const dispatchStartedAt = Date.now();
|
|
21591
|
+
const environment = executor.prepareEnvironment ? await executor.prepareEnvironment(dispatch) : void 0;
|
|
21592
|
+
let preparedWorkspace = null;
|
|
21593
|
+
if (environment?.workspaceRequest) {
|
|
21594
|
+
try {
|
|
21595
|
+
preparedWorkspace = await (0, workspace_1.materializeWorkspace)(environment.workspaceRequest, {
|
|
21596
|
+
...environment.materializeOptions ?? {},
|
|
21597
|
+
credentials: this.broker,
|
|
21598
|
+
signal
|
|
21599
|
+
});
|
|
21600
|
+
} catch (err) {
|
|
21601
|
+
(0, logger_1.getLogger)().warn("workspace materialization failed; running prompt-only", {
|
|
21602
|
+
jobId,
|
|
21603
|
+
error: err instanceof Error ? err.message : String(err)
|
|
21604
|
+
});
|
|
21605
|
+
preparedWorkspace = null;
|
|
21606
|
+
}
|
|
21607
|
+
}
|
|
21318
21608
|
const executionCtx = {
|
|
21319
21609
|
reportProgress: (channel, frames) => {
|
|
21320
21610
|
void jobsClient.postProgress(jobId, { channel, frames }).then(() => activity.record()).catch(() => {
|
|
21321
21611
|
});
|
|
21322
|
-
}
|
|
21612
|
+
},
|
|
21613
|
+
workspace: preparedWorkspace,
|
|
21614
|
+
credentials: this.broker
|
|
21323
21615
|
};
|
|
21324
21616
|
let response;
|
|
21325
21617
|
try {
|
|
@@ -21328,6 +21620,9 @@ var require_shuttle = __commonJS({
|
|
|
21328
21620
|
const reason = `executor failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
21329
21621
|
await observerChain.notify({ kind: "attendance.closed", jobId, attendanceId, status: "failed", reason, totalDurationMs: Date.now() - startedAt }, onObserverError);
|
|
21330
21622
|
return { status: "failed", reason };
|
|
21623
|
+
} finally {
|
|
21624
|
+
if (preparedWorkspace)
|
|
21625
|
+
await preparedWorkspace.cleanup();
|
|
21331
21626
|
}
|
|
21332
21627
|
const gated = await runOutputGates(dispatch, response, executor, signal, executionCtx);
|
|
21333
21628
|
if (!gated.ok) {
|
|
@@ -21911,7 +22206,7 @@ var require_dist5 = __commonJS({
|
|
|
21911
22206
|
Object.defineProperty(exports2, "AgentCredentialRevokedError", { enumerable: true, get: function() {
|
|
21912
22207
|
return keep_1.AgentCredentialRevokedError;
|
|
21913
22208
|
} });
|
|
21914
|
-
var registry_1 =
|
|
22209
|
+
var registry_1 = require_registry2();
|
|
21915
22210
|
Object.defineProperty(exports2, "ExecutorRegistry", { enumerable: true, get: function() {
|
|
21916
22211
|
return registry_1.ExecutorRegistry;
|
|
21917
22212
|
} });
|
|
@@ -22007,7 +22302,7 @@ var import_path = require("path");
|
|
|
22007
22302
|
var import_promises = require("fs/promises");
|
|
22008
22303
|
var import_yaml = __toESM(require_dist());
|
|
22009
22304
|
var import_shuttle = __toESM(require_dist5());
|
|
22010
|
-
var buildVersion = true ? "0.
|
|
22305
|
+
var buildVersion = true ? "0.22.0" : pkg.version;
|
|
22011
22306
|
var sifterBrand = {
|
|
22012
22307
|
product: {
|
|
22013
22308
|
id: "sifter",
|