@whittlelabs/sifter 0.19.1 → 0.21.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 +1314 -1016
- 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),
|
|
@@ -15639,26 +15653,38 @@ var require_subscribe = __commonJS({
|
|
|
15639
15653
|
return;
|
|
15640
15654
|
}
|
|
15641
15655
|
const controller = new AbortController();
|
|
15642
|
-
let
|
|
15643
|
-
|
|
15644
|
-
|
|
15656
|
+
let quietTimer = null;
|
|
15657
|
+
const quietWindowMs = dispatch.deadline ? new Date(dispatch.deadline).getTime() - Date.now() : null;
|
|
15658
|
+
const armQuietTimer = () => {
|
|
15659
|
+
if (quietWindowMs === null)
|
|
15660
|
+
return;
|
|
15661
|
+
if (quietTimer !== null)
|
|
15662
|
+
clearTimeout(quietTimer);
|
|
15663
|
+
const ms = quietWindowMs - 3e4;
|
|
15645
15664
|
if (ms > 0) {
|
|
15646
|
-
|
|
15665
|
+
quietTimer = setTimeout(() => controller.abort(), ms);
|
|
15647
15666
|
} else {
|
|
15648
15667
|
controller.abort();
|
|
15649
15668
|
}
|
|
15650
|
-
}
|
|
15669
|
+
};
|
|
15670
|
+
armQuietTimer();
|
|
15671
|
+
const activity = {
|
|
15672
|
+
record: () => {
|
|
15673
|
+
if (!controller.signal.aborted)
|
|
15674
|
+
armQuietTimer();
|
|
15675
|
+
}
|
|
15676
|
+
};
|
|
15651
15677
|
let result;
|
|
15652
15678
|
try {
|
|
15653
|
-
result = await opts.onJob(dispatch, controller.signal);
|
|
15679
|
+
result = await opts.onJob(dispatch, controller.signal, activity);
|
|
15654
15680
|
} catch (err) {
|
|
15655
15681
|
result = {
|
|
15656
15682
|
status: "failed",
|
|
15657
15683
|
reason: `onJob threw: ${err instanceof Error ? err.message : String(err)}`
|
|
15658
15684
|
};
|
|
15659
15685
|
} finally {
|
|
15660
|
-
if (
|
|
15661
|
-
clearTimeout(
|
|
15686
|
+
if (quietTimer !== null)
|
|
15687
|
+
clearTimeout(quietTimer);
|
|
15662
15688
|
}
|
|
15663
15689
|
try {
|
|
15664
15690
|
for (const output of result.outputs ?? []) {
|
|
@@ -18423,222 +18449,606 @@ var require_chain = __commonJS({
|
|
|
18423
18449
|
}
|
|
18424
18450
|
});
|
|
18425
18451
|
|
|
18426
|
-
// ../../packages/shuttle/dist/
|
|
18427
|
-
var
|
|
18428
|
-
"../../packages/shuttle/dist/
|
|
18452
|
+
// ../../packages/shuttle/dist/workspace/git.js
|
|
18453
|
+
var require_git = __commonJS({
|
|
18454
|
+
"../../packages/shuttle/dist/workspace/git.js"(exports2) {
|
|
18429
18455
|
"use strict";
|
|
18456
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
18457
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
18458
|
+
};
|
|
18430
18459
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18431
|
-
exports2.
|
|
18432
|
-
exports2.
|
|
18433
|
-
|
|
18434
|
-
var
|
|
18435
|
-
|
|
18436
|
-
|
|
18437
|
-
|
|
18438
|
-
|
|
18439
|
-
|
|
18440
|
-
|
|
18441
|
-
|
|
18442
|
-
|
|
18443
|
-
|
|
18444
|
-
|
|
18445
|
-
|
|
18446
|
-
|
|
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;
|
|
18447
18492
|
}
|
|
18448
18493
|
}
|
|
18449
|
-
|
|
18450
|
-
|
|
18451
|
-
|
|
18452
|
-
if (
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
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
|
+
}
|
|
18458
18522
|
}
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18462
|
-
|
|
18463
|
-
|
|
18464
|
-
if (
|
|
18465
|
-
return
|
|
18466
|
-
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
|
|
18470
|
-
|
|
18471
|
-
|
|
18472
|
-
|
|
18473
|
-
|
|
18474
|
-
|
|
18475
|
-
|
|
18476
|
-
const got = asStrings(resolveRef(String(params.got), output, inputs));
|
|
18477
|
-
const want = asStrings(resolveRef(String(params.want), output, inputs));
|
|
18478
|
-
const gotSet = new Set(got);
|
|
18479
|
-
const wantSet = new Set(want);
|
|
18480
|
-
const missing = want.filter((v) => !gotSet.has(v));
|
|
18481
|
-
const unknown = [...new Set(got.filter((v) => !wantSet.has(v)))];
|
|
18482
|
-
const seen = /* @__PURE__ */ new Set();
|
|
18483
|
-
const duplicated = [];
|
|
18484
|
-
for (const v of got) {
|
|
18485
|
-
if (seen.has(v))
|
|
18486
|
-
duplicated.push(v);
|
|
18487
|
-
else
|
|
18488
|
-
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;
|
|
18489
18540
|
}
|
|
18490
|
-
if (
|
|
18491
|
-
|
|
18492
|
-
|
|
18493
|
-
|
|
18494
|
-
|
|
18495
|
-
|
|
18496
|
-
|
|
18497
|
-
if (duplicated.length)
|
|
18498
|
-
parts.push(`assessed more than once: [${[...new Set(duplicated)].join(", ")}]`);
|
|
18499
|
-
return [
|
|
18500
|
-
`every ${subject} must be covered exactly once \u2014 ${parts.join("; ")}. Return one entry per ${subject}, using its exact id.`
|
|
18501
|
-
];
|
|
18502
|
-
},
|
|
18503
|
-
/**
|
|
18504
|
-
* `non_empty_when` — the array at `path` must be non-empty, optionally only
|
|
18505
|
-
* when `when.path` equals `when.equals`. Params:
|
|
18506
|
-
* `{ path: <output path>, when?: { path: <output path>, equals: <value> } }`.
|
|
18507
|
-
*/
|
|
18508
|
-
non_empty_when(params, output) {
|
|
18509
|
-
const when = params.when;
|
|
18510
|
-
if (when && typeof when.path === "string") {
|
|
18511
|
-
if (resolvePath(output, when.path) !== when.equals)
|
|
18512
|
-
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;
|
|
18513
18548
|
}
|
|
18514
|
-
|
|
18515
|
-
|
|
18516
|
-
|
|
18517
|
-
|
|
18518
|
-
|
|
18519
|
-
|
|
18520
|
-
|
|
18521
|
-
|
|
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
|
+
});
|
|
18522
18571
|
}
|
|
18523
|
-
|
|
18524
|
-
|
|
18525
|
-
|
|
18526
|
-
|
|
18527
|
-
|
|
18528
|
-
|
|
18529
|
-
|
|
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)}`);
|
|
18530
18584
|
continue;
|
|
18531
18585
|
}
|
|
18532
|
-
|
|
18533
|
-
|
|
18534
|
-
|
|
18586
|
+
present = await hasCommit();
|
|
18587
|
+
if (!present)
|
|
18588
|
+
failures.push(`${source}: fetch succeeded but ${gitWorkspace.commit} is still absent`);
|
|
18535
18589
|
}
|
|
18536
|
-
|
|
18537
|
-
|
|
18538
|
-
|
|
18539
|
-
|
|
18540
|
-
|
|
18541
|
-
|
|
18542
|
-
|
|
18543
|
-
|
|
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 };
|
|
18544
18600
|
}
|
|
18545
|
-
|
|
18546
|
-
|
|
18547
|
-
|
|
18548
|
-
|
|
18549
|
-
|
|
18550
|
-
|
|
18551
|
-
|
|
18552
|
-
|
|
18553
|
-
|
|
18554
|
-
|
|
18555
|
-
|
|
18556
|
-
|
|
18557
|
-
constructor(config) {
|
|
18558
|
-
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
18559
|
-
this.headers = { "Content-Type": "application/json" };
|
|
18560
|
-
if (config.serviceToken) {
|
|
18561
|
-
this.headers["Authorization"] = `Bearer ${config.serviceToken}`;
|
|
18562
|
-
} else if (config.accessToken) {
|
|
18563
|
-
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;
|
|
18564
18613
|
}
|
|
18565
18614
|
}
|
|
18566
|
-
|
|
18567
|
-
|
|
18568
|
-
|
|
18569
|
-
|
|
18570
|
-
|
|
18571
|
-
|
|
18572
|
-
|
|
18573
|
-
|
|
18574
|
-
|
|
18575
|
-
|
|
18576
|
-
|
|
18577
|
-
|
|
18578
|
-
|
|
18579
|
-
|
|
18580
|
-
return
|
|
18581
|
-
}
|
|
18582
|
-
async destroySession(refreshToken) {
|
|
18583
|
-
await this.request("DELETE", "/api/sessions", refreshToken ? { refreshToken } : void 0);
|
|
18584
|
-
}
|
|
18585
|
-
async refreshSession(refreshToken) {
|
|
18586
|
-
return this.request("POST", "/api/sessions/renewal", { refreshToken });
|
|
18587
|
-
}
|
|
18588
|
-
async getCurrentUser() {
|
|
18589
|
-
return this.request("GET", "/api/accounts/me");
|
|
18590
|
-
}
|
|
18591
|
-
async updateProfile(updates) {
|
|
18592
|
-
return this.request("PATCH", "/api/accounts/me", updates);
|
|
18593
|
-
}
|
|
18594
|
-
async confirmEmail(email, code) {
|
|
18595
|
-
await this.request("POST", "/api/email-confirmations", { email, code });
|
|
18596
|
-
}
|
|
18597
|
-
async requestVerificationCode(email) {
|
|
18598
|
-
await this.request("POST", "/api/verification-codes", { email });
|
|
18599
|
-
}
|
|
18600
|
-
async requestPasswordReset(email) {
|
|
18601
|
-
await this.request("POST", "/api/password-resets", { email });
|
|
18602
|
-
}
|
|
18603
|
-
async resetPassword(email, code, newPassword) {
|
|
18604
|
-
await this.request("PUT", `/api/password-resets/${code}`, { email, newPassword });
|
|
18605
|
-
}
|
|
18606
|
-
async changePassword(currentPassword, newPassword) {
|
|
18607
|
-
await this.request("PUT", "/api/passwords", { currentPassword, newPassword });
|
|
18608
|
-
}
|
|
18609
|
-
// ── Organizations ───────────────────────────────────────────────
|
|
18610
|
-
async listOrganizations() {
|
|
18611
|
-
const { organizations } = await this.request("GET", "/api/organizations");
|
|
18612
|
-
return organizations;
|
|
18613
|
-
}
|
|
18614
|
-
async getOrganization(orgId) {
|
|
18615
|
-
return this.request("GET", `/api/organizations/${orgId}`);
|
|
18616
|
-
}
|
|
18617
|
-
async createOrganization(name, description) {
|
|
18618
|
-
return this.request("POST", "/api/organizations", { name, description });
|
|
18619
|
-
}
|
|
18620
|
-
async updateOrganization(orgId, updates) {
|
|
18621
|
-
return this.request("PATCH", `/api/organizations/${orgId}`, updates);
|
|
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" };
|
|
18622
18630
|
}
|
|
18623
|
-
|
|
18624
|
-
|
|
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` };
|
|
18625
18636
|
}
|
|
18626
|
-
|
|
18627
|
-
|
|
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` };
|
|
18628
18640
|
}
|
|
18629
|
-
|
|
18630
|
-
|
|
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" };
|
|
18631
18649
|
}
|
|
18632
|
-
|
|
18633
|
-
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" };
|
|
18634
18652
|
}
|
|
18635
|
-
|
|
18636
|
-
|
|
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
|
+
}
|
|
18637
18661
|
}
|
|
18638
|
-
|
|
18639
|
-
|
|
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;
|
|
18640
18673
|
}
|
|
18641
|
-
|
|
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) {
|
|
18642
19052
|
return this.request("POST", `/api/organizations/${orgId}/teams/${teamId}/members`, { userId });
|
|
18643
19053
|
}
|
|
18644
19054
|
async createInvitation(orgId, email, roleGrants) {
|
|
@@ -18745,815 +19155,698 @@ var require_client2 = __commonJS({
|
|
|
18745
19155
|
}
|
|
18746
19156
|
});
|
|
18747
19157
|
|
|
18748
|
-
// ../../packages/keep/dist/service-token.js
|
|
18749
|
-
var require_service_token = __commonJS({
|
|
18750
|
-
"../../packages/keep/dist/service-token.js"(exports2) {
|
|
18751
|
-
"use strict";
|
|
18752
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18753
|
-
exports2.ServiceTokenManager = void 0;
|
|
18754
|
-
var ServiceTokenManager = class {
|
|
18755
|
-
token = null;
|
|
18756
|
-
expiresAt = 0;
|
|
18757
|
-
pendingExchange = null;
|
|
18758
|
-
keepApiUrl;
|
|
18759
|
-
clientId;
|
|
18760
|
-
clientSecret;
|
|
18761
|
-
enabled;
|
|
18762
|
-
onResponse;
|
|
18763
|
-
constructor(config) {
|
|
18764
|
-
this.keepApiUrl = config.keepApiUrl.replace(/\/$/, "");
|
|
18765
|
-
this.clientId = config.clientId;
|
|
18766
|
-
this.clientSecret = config.clientSecret;
|
|
18767
|
-
this.enabled = !!(config.clientId && config.clientSecret && config.keepApiUrl);
|
|
18768
|
-
this.onResponse = config.onResponse;
|
|
18769
|
-
if (!this.enabled) {
|
|
18770
|
-
console.warn("[ServiceTokenManager] Keep credentials not configured (KEEP_CLIENT_ID, KEEP_CLIENT_SECRET, KEEP_API_URL). Service-to-service auth is disabled.");
|
|
18771
|
-
}
|
|
18772
|
-
}
|
|
18773
|
-
/**
|
|
18774
|
-
* Returns a valid service token, refreshing if needed.
|
|
18775
|
-
* Returns null if Keep credentials are not configured (graceful degradation for local dev).
|
|
18776
|
-
*/
|
|
18777
|
-
async getToken() {
|
|
18778
|
-
if (!this.enabled)
|
|
18779
|
-
return null;
|
|
18780
|
-
if (this.token && Date.now() < this.expiresAt - 3e4) {
|
|
18781
|
-
return this.token;
|
|
18782
|
-
}
|
|
18783
|
-
if (this.pendingExchange) {
|
|
18784
|
-
return this.pendingExchange;
|
|
18785
|
-
}
|
|
18786
|
-
this.pendingExchange = this.exchange();
|
|
18787
|
-
try {
|
|
18788
|
-
return await this.pendingExchange;
|
|
18789
|
-
} finally {
|
|
18790
|
-
this.pendingExchange = null;
|
|
18791
|
-
}
|
|
18792
|
-
}
|
|
18793
|
-
async exchange() {
|
|
18794
|
-
const url = `${this.keepApiUrl}/api/service-tokens`;
|
|
18795
|
-
const response = await fetch(url, {
|
|
18796
|
-
method: "POST",
|
|
18797
|
-
headers: { "Content-Type": "application/json" },
|
|
18798
|
-
body: JSON.stringify({
|
|
18799
|
-
clientId: this.clientId,
|
|
18800
|
-
clientSecret: this.clientSecret
|
|
18801
|
-
})
|
|
18802
|
-
});
|
|
18803
|
-
this.onResponse?.(response);
|
|
18804
|
-
const json = await response.json();
|
|
18805
|
-
if (!response.ok || !json.success) {
|
|
18806
|
-
const msg = json.error?.message ?? `Token exchange failed with status ${response.status}`;
|
|
18807
|
-
throw new Error(`[ServiceTokenManager] ${msg}`);
|
|
18808
|
-
}
|
|
18809
|
-
this.token = json.data.token;
|
|
18810
|
-
this.expiresAt = Date.now() + json.data.expiresIn * 1e3;
|
|
18811
|
-
return this.token;
|
|
18812
|
-
}
|
|
18813
|
-
};
|
|
18814
|
-
exports2.ServiceTokenManager = ServiceTokenManager;
|
|
18815
|
-
}
|
|
18816
|
-
});
|
|
18817
|
-
|
|
18818
|
-
// ../../packages/keep/dist/agent-token.js
|
|
18819
|
-
var require_agent_token = __commonJS({
|
|
18820
|
-
"../../packages/keep/dist/agent-token.js"(exports2) {
|
|
18821
|
-
"use strict";
|
|
18822
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18823
|
-
exports2.AgentTokenManager = exports2.AgentCredentialRevokedError = void 0;
|
|
18824
|
-
var AgentCredentialRevokedError2 = class extends Error {
|
|
18825
|
-
code = "PAIRING_REVOKED";
|
|
18826
|
-
constructor(message) {
|
|
18827
|
-
super(message);
|
|
18828
|
-
this.name = "AgentCredentialRevokedError";
|
|
18829
|
-
}
|
|
18830
|
-
};
|
|
18831
|
-
exports2.AgentCredentialRevokedError = AgentCredentialRevokedError2;
|
|
18832
|
-
var AgentTokenManager = class {
|
|
18833
|
-
token = null;
|
|
18834
|
-
expiresAt = 0;
|
|
18835
|
-
pendingExchange = null;
|
|
18836
|
-
keepApiUrl;
|
|
18837
|
-
clientId;
|
|
18838
|
-
clientSecret;
|
|
18839
|
-
enabled;
|
|
18840
|
-
onResponse;
|
|
18841
|
-
constructor(config) {
|
|
18842
|
-
this.keepApiUrl = config.keepApiUrl.replace(/\/$/, "");
|
|
18843
|
-
this.clientId = config.clientId;
|
|
18844
|
-
this.clientSecret = config.clientSecret;
|
|
18845
|
-
this.enabled = !!(config.clientId && config.clientSecret && config.keepApiUrl);
|
|
18846
|
-
this.onResponse = config.onResponse;
|
|
18847
|
-
}
|
|
18848
|
-
async getToken(forceRefresh = false) {
|
|
18849
|
-
if (!this.enabled)
|
|
18850
|
-
return null;
|
|
18851
|
-
if (!forceRefresh && this.token && Date.now() < this.expiresAt - 3e4) {
|
|
18852
|
-
return this.token;
|
|
18853
|
-
}
|
|
18854
|
-
if (this.pendingExchange) {
|
|
18855
|
-
return this.pendingExchange;
|
|
18856
|
-
}
|
|
18857
|
-
this.pendingExchange = this.exchange();
|
|
18858
|
-
try {
|
|
18859
|
-
return await this.pendingExchange;
|
|
18860
|
-
} finally {
|
|
18861
|
-
this.pendingExchange = null;
|
|
18862
|
-
}
|
|
18863
|
-
}
|
|
18864
|
-
async exchange() {
|
|
18865
|
-
const url = `${this.keepApiUrl}/api/agent-tokens`;
|
|
18866
|
-
const response = await fetch(url, {
|
|
18867
|
-
method: "POST",
|
|
18868
|
-
headers: { "Content-Type": "application/json" },
|
|
18869
|
-
body: JSON.stringify({
|
|
18870
|
-
clientId: this.clientId,
|
|
18871
|
-
clientSecret: this.clientSecret
|
|
18872
|
-
})
|
|
18873
|
-
});
|
|
18874
|
-
this.onResponse?.(response);
|
|
18875
|
-
const json = await response.json();
|
|
18876
|
-
if (!response.ok || !json.success) {
|
|
18877
|
-
const msg = json.error?.message ?? `Agent token exchange failed with status ${response.status}`;
|
|
18878
|
-
if (response.status === 401) {
|
|
18879
|
-
throw new AgentCredentialRevokedError2(`[AgentTokenManager] Keep refused this agent's credentials (401): the pairing was revoked or the secret rotated. Re-pair to continue.`);
|
|
18880
|
-
}
|
|
18881
|
-
throw new Error(`[AgentTokenManager] ${msg}`);
|
|
18882
|
-
}
|
|
18883
|
-
this.token = json.data.token;
|
|
18884
|
-
this.expiresAt = Date.now() + json.data.expiresIn * 1e3;
|
|
18885
|
-
return this.token;
|
|
18886
|
-
}
|
|
18887
|
-
};
|
|
18888
|
-
exports2.AgentTokenManager = AgentTokenManager;
|
|
18889
|
-
}
|
|
18890
|
-
});
|
|
18891
|
-
|
|
18892
|
-
// ../../packages/keep/dist/index.js
|
|
18893
|
-
var require_dist4 = __commonJS({
|
|
18894
|
-
"../../packages/keep/dist/index.js"(exports2) {
|
|
18895
|
-
"use strict";
|
|
18896
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18897
|
-
exports2.AgentCredentialRevokedError = exports2.AgentTokenManager = exports2.ServiceTokenManager = exports2.KeepClient = void 0;
|
|
18898
|
-
var client_1 = require_client2();
|
|
18899
|
-
Object.defineProperty(exports2, "KeepClient", { enumerable: true, get: function() {
|
|
18900
|
-
return client_1.KeepClient;
|
|
18901
|
-
} });
|
|
18902
|
-
var service_token_1 = require_service_token();
|
|
18903
|
-
Object.defineProperty(exports2, "ServiceTokenManager", { enumerable: true, get: function() {
|
|
18904
|
-
return service_token_1.ServiceTokenManager;
|
|
18905
|
-
} });
|
|
18906
|
-
var agent_token_1 = require_agent_token();
|
|
18907
|
-
Object.defineProperty(exports2, "AgentTokenManager", { enumerable: true, get: function() {
|
|
18908
|
-
return agent_token_1.AgentTokenManager;
|
|
18909
|
-
} });
|
|
18910
|
-
Object.defineProperty(exports2, "AgentCredentialRevokedError", { enumerable: true, get: function() {
|
|
18911
|
-
return agent_token_1.AgentCredentialRevokedError;
|
|
18912
|
-
} });
|
|
18913
|
-
}
|
|
18914
|
-
});
|
|
18915
|
-
|
|
18916
|
-
// ../../packages/shuttle/dist/logging/logger.js
|
|
18917
|
-
var require_logger = __commonJS({
|
|
18918
|
-
"../../packages/shuttle/dist/logging/logger.js"(exports2) {
|
|
18919
|
-
"use strict";
|
|
18920
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18921
|
-
exports2.createLogger = createLogger;
|
|
18922
|
-
exports2.setGlobalLogger = setGlobalLogger;
|
|
18923
|
-
exports2.getLogger = getLogger;
|
|
18924
|
-
var LEVEL_ORDER = {
|
|
18925
|
-
debug: 0,
|
|
18926
|
-
info: 1,
|
|
18927
|
-
warn: 2,
|
|
18928
|
-
error: 3
|
|
18929
|
-
};
|
|
18930
|
-
function formatContext(context) {
|
|
18931
|
-
return Object.entries(context).map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`).join(" ");
|
|
18932
|
-
}
|
|
18933
|
-
function formatPretty(level, message, context) {
|
|
18934
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
18935
|
-
const tag = level.toUpperCase().padEnd(5);
|
|
18936
|
-
const base = `[${timestamp}] ${tag} ${message}`;
|
|
18937
|
-
if (context && Object.keys(context).length > 0) {
|
|
18938
|
-
return `${base} ${formatContext(context)}`;
|
|
18939
|
-
}
|
|
18940
|
-
return base;
|
|
18941
|
-
}
|
|
18942
|
-
function formatJson(level, message, context) {
|
|
18943
|
-
const entry = {
|
|
18944
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18945
|
-
level,
|
|
18946
|
-
message,
|
|
18947
|
-
...context
|
|
18948
|
-
};
|
|
18949
|
-
return JSON.stringify(entry);
|
|
18950
|
-
}
|
|
18951
|
-
function createLogger(options) {
|
|
18952
|
-
const threshold = LEVEL_ORDER[options.level];
|
|
18953
|
-
const format = options.format === "json" ? formatJson : formatPretty;
|
|
18954
|
-
function emit(level, message, context) {
|
|
18955
|
-
if (LEVEL_ORDER[level] < threshold)
|
|
18956
|
-
return;
|
|
18957
|
-
const line = format(level, message, context);
|
|
18958
|
-
if (level === "error") {
|
|
18959
|
-
console.error(line);
|
|
18960
|
-
} else if (level === "warn") {
|
|
18961
|
-
console.warn(line);
|
|
18962
|
-
} else {
|
|
18963
|
-
console.log(line);
|
|
18964
|
-
}
|
|
18965
|
-
}
|
|
18966
|
-
return {
|
|
18967
|
-
debug: (message, context) => emit("debug", message, context),
|
|
18968
|
-
info: (message, context) => emit("info", message, context),
|
|
18969
|
-
warn: (message, context) => emit("warn", message, context),
|
|
18970
|
-
error: (message, context) => emit("error", message, context)
|
|
18971
|
-
};
|
|
18972
|
-
}
|
|
18973
|
-
var globalLogger = createLogger({ level: "info", format: "pretty" });
|
|
18974
|
-
function setGlobalLogger(logger) {
|
|
18975
|
-
globalLogger = logger;
|
|
18976
|
-
}
|
|
18977
|
-
function getLogger() {
|
|
18978
|
-
return globalLogger;
|
|
18979
|
-
}
|
|
18980
|
-
}
|
|
18981
|
-
});
|
|
18982
|
-
|
|
18983
|
-
// ../../packages/shuttle/dist/executors/registry.js
|
|
18984
|
-
var require_registry = __commonJS({
|
|
18985
|
-
"../../packages/shuttle/dist/executors/registry.js"(exports2) {
|
|
19158
|
+
// ../../packages/keep/dist/service-token.js
|
|
19159
|
+
var require_service_token = __commonJS({
|
|
19160
|
+
"../../packages/keep/dist/service-token.js"(exports2) {
|
|
18986
19161
|
"use strict";
|
|
18987
19162
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18988
|
-
exports2.
|
|
18989
|
-
var
|
|
18990
|
-
|
|
18991
|
-
|
|
18992
|
-
|
|
18993
|
-
|
|
18994
|
-
|
|
18995
|
-
|
|
18996
|
-
|
|
18997
|
-
|
|
19163
|
+
exports2.ServiceTokenManager = void 0;
|
|
19164
|
+
var ServiceTokenManager = class {
|
|
19165
|
+
token = null;
|
|
19166
|
+
expiresAt = 0;
|
|
19167
|
+
pendingExchange = null;
|
|
19168
|
+
keepApiUrl;
|
|
19169
|
+
clientId;
|
|
19170
|
+
clientSecret;
|
|
19171
|
+
enabled;
|
|
19172
|
+
onResponse;
|
|
19173
|
+
constructor(config) {
|
|
19174
|
+
this.keepApiUrl = config.keepApiUrl.replace(/\/$/, "");
|
|
19175
|
+
this.clientId = config.clientId;
|
|
19176
|
+
this.clientSecret = config.clientSecret;
|
|
19177
|
+
this.enabled = !!(config.clientId && config.clientSecret && config.keepApiUrl);
|
|
19178
|
+
this.onResponse = config.onResponse;
|
|
19179
|
+
if (!this.enabled) {
|
|
19180
|
+
console.warn("[ServiceTokenManager] Keep credentials not configured (KEEP_CLIENT_ID, KEEP_CLIENT_SECRET, KEEP_API_URL). Service-to-service auth is disabled.");
|
|
18998
19181
|
}
|
|
18999
|
-
this.factories.set(name, factory);
|
|
19000
19182
|
}
|
|
19001
|
-
|
|
19002
|
-
|
|
19003
|
-
|
|
19004
|
-
|
|
19183
|
+
/**
|
|
19184
|
+
* Returns a valid service token, refreshing if needed.
|
|
19185
|
+
* Returns null if Keep credentials are not configured (graceful degradation for local dev).
|
|
19186
|
+
*/
|
|
19187
|
+
async getToken() {
|
|
19188
|
+
if (!this.enabled)
|
|
19189
|
+
return null;
|
|
19190
|
+
if (this.token && Date.now() < this.expiresAt - 3e4) {
|
|
19191
|
+
return this.token;
|
|
19005
19192
|
}
|
|
19006
|
-
|
|
19007
|
-
|
|
19008
|
-
|
|
19009
|
-
|
|
19010
|
-
|
|
19011
|
-
|
|
19012
|
-
|
|
19013
|
-
|
|
19014
|
-
/** All registered factories as JobExecutors built with their default config. */
|
|
19015
|
-
build(configs) {
|
|
19016
|
-
const out = [];
|
|
19017
|
-
for (const [name, cfg] of Object.entries(configs)) {
|
|
19018
|
-
if (!this.factories.has(name)) {
|
|
19019
|
-
throw new Error(`Executor "${name}" in config is not registered or not enabled for this build.`);
|
|
19020
|
-
}
|
|
19021
|
-
out.push(this.create(name, cfg));
|
|
19193
|
+
if (this.pendingExchange) {
|
|
19194
|
+
return this.pendingExchange;
|
|
19195
|
+
}
|
|
19196
|
+
this.pendingExchange = this.exchange();
|
|
19197
|
+
try {
|
|
19198
|
+
return await this.pendingExchange;
|
|
19199
|
+
} finally {
|
|
19200
|
+
this.pendingExchange = null;
|
|
19022
19201
|
}
|
|
19023
|
-
return out;
|
|
19024
19202
|
}
|
|
19025
|
-
|
|
19026
|
-
|
|
19203
|
+
async exchange() {
|
|
19204
|
+
const url = `${this.keepApiUrl}/api/service-tokens`;
|
|
19205
|
+
const response = await fetch(url, {
|
|
19206
|
+
method: "POST",
|
|
19207
|
+
headers: { "Content-Type": "application/json" },
|
|
19208
|
+
body: JSON.stringify({
|
|
19209
|
+
clientId: this.clientId,
|
|
19210
|
+
clientSecret: this.clientSecret
|
|
19211
|
+
})
|
|
19212
|
+
});
|
|
19213
|
+
this.onResponse?.(response);
|
|
19214
|
+
const json = await response.json();
|
|
19215
|
+
if (!response.ok || !json.success) {
|
|
19216
|
+
const msg = json.error?.message ?? `Token exchange failed with status ${response.status}`;
|
|
19217
|
+
throw new Error(`[ServiceTokenManager] ${msg}`);
|
|
19218
|
+
}
|
|
19219
|
+
this.token = json.data.token;
|
|
19220
|
+
this.expiresAt = Date.now() + json.data.expiresIn * 1e3;
|
|
19221
|
+
return this.token;
|
|
19027
19222
|
}
|
|
19028
19223
|
};
|
|
19029
|
-
exports2.
|
|
19030
|
-
}
|
|
19031
|
-
});
|
|
19032
|
-
|
|
19033
|
-
// ../../packages/shuttle/dist/executors/validate-output.js
|
|
19034
|
-
var require_validate_output = __commonJS({
|
|
19035
|
-
"../../packages/shuttle/dist/executors/validate-output.js"(exports2) {
|
|
19036
|
-
"use strict";
|
|
19037
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19038
|
-
exports2.assertOutputValid = assertOutputValid;
|
|
19039
|
-
var loom_1 = require_dist3();
|
|
19040
|
-
function assertOutputValid(spec, parsed) {
|
|
19041
|
-
const { valid, errors } = (0, loom_1.validateAgainstSchema)(parsed ?? null, spec.outputSchema);
|
|
19042
|
-
if (!valid) {
|
|
19043
|
-
throw new Error(`prompt-execution output failed schema validation: ${errors.join("; ")}`);
|
|
19044
|
-
}
|
|
19045
|
-
}
|
|
19224
|
+
exports2.ServiceTokenManager = ServiceTokenManager;
|
|
19046
19225
|
}
|
|
19047
19226
|
});
|
|
19048
19227
|
|
|
19049
|
-
// ../../packages/
|
|
19050
|
-
var
|
|
19051
|
-
"../../packages/
|
|
19228
|
+
// ../../packages/keep/dist/agent-token.js
|
|
19229
|
+
var require_agent_token = __commonJS({
|
|
19230
|
+
"../../packages/keep/dist/agent-token.js"(exports2) {
|
|
19052
19231
|
"use strict";
|
|
19053
19232
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19054
|
-
exports2.
|
|
19055
|
-
var
|
|
19056
|
-
|
|
19057
|
-
|
|
19058
|
-
|
|
19059
|
-
|
|
19060
|
-
timer = null;
|
|
19061
|
-
closed = false;
|
|
19062
|
-
flushMs;
|
|
19063
|
-
maxChars;
|
|
19064
|
-
constructor(sink, options = {}) {
|
|
19065
|
-
this.sink = sink;
|
|
19066
|
-
this.flushMs = options.flushMs ?? 400;
|
|
19067
|
-
this.maxChars = options.maxChars ?? 1200;
|
|
19068
|
-
}
|
|
19069
|
-
reasoning(text) {
|
|
19070
|
-
if (this.closed || !text)
|
|
19071
|
-
return;
|
|
19072
|
-
this.textBuf += text;
|
|
19073
|
-
if (this.textBuf.length >= this.maxChars) {
|
|
19074
|
-
this.flush();
|
|
19075
|
-
} else {
|
|
19076
|
-
this.arm();
|
|
19077
|
-
}
|
|
19078
|
-
}
|
|
19079
|
-
tool(name, target) {
|
|
19080
|
-
if (this.closed || !name)
|
|
19081
|
-
return;
|
|
19082
|
-
this.drainText();
|
|
19083
|
-
this.push({ kind: "tool", tool: target ? { name, target } : { name } });
|
|
19084
|
-
this.flush();
|
|
19085
|
-
}
|
|
19086
|
-
/** Flush pending text + frames now. */
|
|
19087
|
-
flush() {
|
|
19088
|
-
if (this.closed)
|
|
19089
|
-
return;
|
|
19090
|
-
this.drainText();
|
|
19091
|
-
this.clearTimer();
|
|
19092
|
-
if (this.pending.length === 0)
|
|
19093
|
-
return;
|
|
19094
|
-
const batch = this.pending;
|
|
19095
|
-
this.pending = [];
|
|
19096
|
-
try {
|
|
19097
|
-
this.sink(batch);
|
|
19098
|
-
} catch {
|
|
19099
|
-
}
|
|
19100
|
-
}
|
|
19101
|
-
/** Final flush; no further frames are emitted after this. */
|
|
19102
|
-
close() {
|
|
19103
|
-
this.flush();
|
|
19104
|
-
this.closed = true;
|
|
19105
|
-
this.clearTimer();
|
|
19106
|
-
}
|
|
19107
|
-
drainText() {
|
|
19108
|
-
if (this.textBuf.length === 0)
|
|
19109
|
-
return;
|
|
19110
|
-
this.push({ kind: "reasoning", text: this.textBuf });
|
|
19111
|
-
this.textBuf = "";
|
|
19112
|
-
}
|
|
19113
|
-
push(frame) {
|
|
19114
|
-
this.pending.push({ seq: this.seq++, at: (/* @__PURE__ */ new Date()).toISOString(), ...frame });
|
|
19115
|
-
}
|
|
19116
|
-
arm() {
|
|
19117
|
-
if (this.timer)
|
|
19118
|
-
return;
|
|
19119
|
-
this.timer = setTimeout(() => {
|
|
19120
|
-
this.timer = null;
|
|
19121
|
-
this.flush();
|
|
19122
|
-
}, this.flushMs);
|
|
19123
|
-
this.timer.unref?.();
|
|
19124
|
-
}
|
|
19125
|
-
clearTimer() {
|
|
19126
|
-
if (this.timer) {
|
|
19127
|
-
clearTimeout(this.timer);
|
|
19128
|
-
this.timer = null;
|
|
19129
|
-
}
|
|
19233
|
+
exports2.AgentTokenManager = exports2.AgentCredentialRevokedError = void 0;
|
|
19234
|
+
var AgentCredentialRevokedError2 = class extends Error {
|
|
19235
|
+
code = "PAIRING_REVOKED";
|
|
19236
|
+
constructor(message) {
|
|
19237
|
+
super(message);
|
|
19238
|
+
this.name = "AgentCredentialRevokedError";
|
|
19130
19239
|
}
|
|
19131
19240
|
};
|
|
19132
|
-
exports2.
|
|
19133
|
-
var
|
|
19134
|
-
|
|
19135
|
-
|
|
19136
|
-
|
|
19137
|
-
|
|
19241
|
+
exports2.AgentCredentialRevokedError = AgentCredentialRevokedError2;
|
|
19242
|
+
var AgentTokenManager = class {
|
|
19243
|
+
token = null;
|
|
19244
|
+
expiresAt = 0;
|
|
19245
|
+
pendingExchange = null;
|
|
19246
|
+
keepApiUrl;
|
|
19247
|
+
clientId;
|
|
19248
|
+
clientSecret;
|
|
19249
|
+
enabled;
|
|
19250
|
+
onResponse;
|
|
19251
|
+
constructor(config) {
|
|
19252
|
+
this.keepApiUrl = config.keepApiUrl.replace(/\/$/, "");
|
|
19253
|
+
this.clientId = config.clientId;
|
|
19254
|
+
this.clientSecret = config.clientSecret;
|
|
19255
|
+
this.enabled = !!(config.clientId && config.clientSecret && config.keepApiUrl);
|
|
19256
|
+
this.onResponse = config.onResponse;
|
|
19138
19257
|
}
|
|
19139
|
-
|
|
19140
|
-
|
|
19141
|
-
|
|
19142
|
-
|
|
19143
|
-
|
|
19144
|
-
const line = this.buf.slice(0, nl);
|
|
19145
|
-
this.buf = this.buf.slice(nl + 1);
|
|
19146
|
-
this.consumeLine(line);
|
|
19258
|
+
async getToken(forceRefresh = false) {
|
|
19259
|
+
if (!this.enabled)
|
|
19260
|
+
return null;
|
|
19261
|
+
if (!forceRefresh && this.token && Date.now() < this.expiresAt - 3e4) {
|
|
19262
|
+
return this.token;
|
|
19147
19263
|
}
|
|
19148
|
-
|
|
19149
|
-
|
|
19150
|
-
|
|
19151
|
-
|
|
19152
|
-
this.consumeLine(this.buf);
|
|
19153
|
-
this.buf = "";
|
|
19154
|
-
}
|
|
19155
|
-
consumeLine(line) {
|
|
19156
|
-
const trimmed = line.trim();
|
|
19157
|
-
if (!trimmed.startsWith("{"))
|
|
19158
|
-
return;
|
|
19159
|
-
let event;
|
|
19264
|
+
if (this.pendingExchange) {
|
|
19265
|
+
return this.pendingExchange;
|
|
19266
|
+
}
|
|
19267
|
+
this.pendingExchange = this.exchange();
|
|
19160
19268
|
try {
|
|
19161
|
-
|
|
19162
|
-
}
|
|
19163
|
-
|
|
19269
|
+
return await this.pendingExchange;
|
|
19270
|
+
} finally {
|
|
19271
|
+
this.pendingExchange = null;
|
|
19164
19272
|
}
|
|
19165
|
-
|
|
19166
|
-
|
|
19167
|
-
|
|
19168
|
-
|
|
19169
|
-
|
|
19170
|
-
|
|
19171
|
-
|
|
19172
|
-
|
|
19173
|
-
|
|
19174
|
-
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19273
|
+
}
|
|
19274
|
+
async exchange() {
|
|
19275
|
+
const url = `${this.keepApiUrl}/api/agent-tokens`;
|
|
19276
|
+
const response = await fetch(url, {
|
|
19277
|
+
method: "POST",
|
|
19278
|
+
headers: { "Content-Type": "application/json" },
|
|
19279
|
+
body: JSON.stringify({
|
|
19280
|
+
clientId: this.clientId,
|
|
19281
|
+
clientSecret: this.clientSecret
|
|
19282
|
+
})
|
|
19283
|
+
});
|
|
19284
|
+
this.onResponse?.(response);
|
|
19285
|
+
const json = await response.json();
|
|
19286
|
+
if (!response.ok || !json.success) {
|
|
19287
|
+
const msg = json.error?.message ?? `Agent token exchange failed with status ${response.status}`;
|
|
19288
|
+
if (response.status === 401) {
|
|
19289
|
+
throw new AgentCredentialRevokedError2(`[AgentTokenManager] Keep refused this agent's credentials (401): the pairing was revoked or the secret rotated. Re-pair to continue.`);
|
|
19178
19290
|
}
|
|
19179
|
-
|
|
19180
|
-
this.handlers.onResult?.();
|
|
19291
|
+
throw new Error(`[AgentTokenManager] ${msg}`);
|
|
19181
19292
|
}
|
|
19293
|
+
this.token = json.data.token;
|
|
19294
|
+
this.expiresAt = Date.now() + json.data.expiresIn * 1e3;
|
|
19295
|
+
return this.token;
|
|
19182
19296
|
}
|
|
19183
19297
|
};
|
|
19184
|
-
exports2.
|
|
19185
|
-
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
|
|
19189
|
-
|
|
19190
|
-
|
|
19191
|
-
|
|
19298
|
+
exports2.AgentTokenManager = AgentTokenManager;
|
|
19299
|
+
}
|
|
19300
|
+
});
|
|
19301
|
+
|
|
19302
|
+
// ../../packages/keep/dist/index.js
|
|
19303
|
+
var require_dist4 = __commonJS({
|
|
19304
|
+
"../../packages/keep/dist/index.js"(exports2) {
|
|
19305
|
+
"use strict";
|
|
19306
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19307
|
+
exports2.AgentCredentialRevokedError = exports2.AgentTokenManager = exports2.ServiceTokenManager = exports2.KeepClient = void 0;
|
|
19308
|
+
var client_1 = require_client2();
|
|
19309
|
+
Object.defineProperty(exports2, "KeepClient", { enumerable: true, get: function() {
|
|
19310
|
+
return client_1.KeepClient;
|
|
19311
|
+
} });
|
|
19312
|
+
var service_token_1 = require_service_token();
|
|
19313
|
+
Object.defineProperty(exports2, "ServiceTokenManager", { enumerable: true, get: function() {
|
|
19314
|
+
return service_token_1.ServiceTokenManager;
|
|
19315
|
+
} });
|
|
19316
|
+
var agent_token_1 = require_agent_token();
|
|
19317
|
+
Object.defineProperty(exports2, "AgentTokenManager", { enumerable: true, get: function() {
|
|
19318
|
+
return agent_token_1.AgentTokenManager;
|
|
19319
|
+
} });
|
|
19320
|
+
Object.defineProperty(exports2, "AgentCredentialRevokedError", { enumerable: true, get: function() {
|
|
19321
|
+
return agent_token_1.AgentCredentialRevokedError;
|
|
19322
|
+
} });
|
|
19323
|
+
}
|
|
19324
|
+
});
|
|
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.`);
|
|
19192
19344
|
}
|
|
19193
|
-
return
|
|
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`);
|
|
19194
19360
|
}
|
|
19195
19361
|
}
|
|
19196
19362
|
});
|
|
19197
19363
|
|
|
19198
|
-
// ../../packages/shuttle/dist/
|
|
19199
|
-
var
|
|
19200
|
-
"../../packages/shuttle/dist/
|
|
19364
|
+
// ../../packages/shuttle/dist/credentials/registry.js
|
|
19365
|
+
var require_registry = __commonJS({
|
|
19366
|
+
"../../packages/shuttle/dist/credentials/registry.js"(exports2) {
|
|
19201
19367
|
"use strict";
|
|
19202
|
-
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
19203
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
19204
|
-
};
|
|
19205
19368
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19206
|
-
exports2.
|
|
19207
|
-
exports2.
|
|
19208
|
-
var
|
|
19209
|
-
|
|
19210
|
-
|
|
19211
|
-
|
|
19212
|
-
|
|
19213
|
-
|
|
19214
|
-
|
|
19215
|
-
|
|
19216
|
-
const
|
|
19217
|
-
|
|
19218
|
-
|
|
19219
|
-
|
|
19220
|
-
const gitWorkspace = resolved.git;
|
|
19221
|
-
const timeoutMs = options.commandTimeoutMs ?? DEFAULT_GIT_TIMEOUT_MS;
|
|
19222
|
-
const allowedProtocols = options.allowedProtocols ?? DEFAULT_GIT_PROTOCOLS;
|
|
19223
|
-
const signal = options.signal;
|
|
19224
|
-
const authEnv = options.gitToken !== void 0 ? buildGitAuthEnv(gitWorkspace.remote, options.gitToken) : void 0;
|
|
19225
|
-
const git = (args, cwd) => runGit(args, cwd, { timeoutMs, signal, allowedProtocols, extraEnv: authEnv });
|
|
19226
|
-
const hasCommit = (dir) => git(["cat-file", "-e", `${gitWorkspace.commit}^{commit}`], dir).then((r) => r.code === 0);
|
|
19227
|
-
const fetchCommit = async (dir, opts) => {
|
|
19228
|
-
if (await hasCommit(dir))
|
|
19229
|
-
return [];
|
|
19230
|
-
const depth = opts.shallow ? ["--depth=1"] : [];
|
|
19231
|
-
const attempts = [];
|
|
19232
|
-
for (const ref of gitWorkspace.fetchRefs ?? []) {
|
|
19233
|
-
attempts.push({
|
|
19234
|
-
source: `${gitWorkspace.remote} ${ref}`,
|
|
19235
|
-
args: ["fetch", "--no-tags", ...depth, gitWorkspace.remote, ref]
|
|
19236
|
-
});
|
|
19237
|
-
}
|
|
19238
|
-
attempts.push({
|
|
19239
|
-
source: `${gitWorkspace.remote} commit`,
|
|
19240
|
-
args: ["fetch", "--no-tags", ...depth, gitWorkspace.remote, gitWorkspace.commit]
|
|
19241
|
-
});
|
|
19242
|
-
const failures2 = [];
|
|
19243
|
-
for (const { source, args } of attempts) {
|
|
19244
|
-
const result = await git(args, dir);
|
|
19245
|
-
if (result.code !== 0) {
|
|
19246
|
-
failures2.push(`${source}: ${firstLine(result.stderr)}`);
|
|
19247
|
-
continue;
|
|
19248
|
-
}
|
|
19249
|
-
if (await hasCommit(dir))
|
|
19250
|
-
return [];
|
|
19251
|
-
failures2.push(`${source}: fetch succeeded but ${gitWorkspace.commit} is still absent`);
|
|
19252
|
-
}
|
|
19253
|
-
return failures2;
|
|
19254
|
-
};
|
|
19255
|
-
const notMaterialized = (failures2) => `could not materialize ${gitWorkspace.commit} from ${gitWorkspace.remote}` + (failures2.length > 0 ? ` (${failures2.join("; ")})` : "");
|
|
19256
|
-
const tempRoot = await fs_1.promises.mkdtemp(path_1.default.join(tmpBase, "shuttle-ws-"));
|
|
19257
|
-
const checkoutDir = path_1.default.join(tempRoot, "checkout");
|
|
19258
|
-
const removeTempRoot = async () => {
|
|
19259
|
-
await fs_1.promises.rm(tempRoot, { recursive: true, force: true }).catch(() => {
|
|
19260
|
-
});
|
|
19261
|
-
};
|
|
19262
|
-
await fs_1.promises.mkdir(checkoutDir);
|
|
19263
|
-
const init = await git(["init", "-q"], checkoutDir);
|
|
19264
|
-
if (init.code !== 0) {
|
|
19265
|
-
await removeTempRoot();
|
|
19266
|
-
return degradedWorkspace(`git init failed: ${firstLine(init.stderr)}`, tmpBase);
|
|
19267
|
-
}
|
|
19268
|
-
const failures = await fetchCommit(checkoutDir, { shallow: true });
|
|
19269
|
-
if (!await hasCommit(checkoutDir)) {
|
|
19270
|
-
await removeTempRoot();
|
|
19271
|
-
return degradedWorkspace(notMaterialized(failures), tmpBase);
|
|
19272
|
-
}
|
|
19273
|
-
const checkout = await git(["checkout", "--detach", gitWorkspace.commit], checkoutDir);
|
|
19274
|
-
if (checkout.code !== 0) {
|
|
19275
|
-
await removeTempRoot();
|
|
19276
|
-
return degradedWorkspace(`git checkout failed: ${firstLine(checkout.stderr)}`, tmpBase);
|
|
19277
|
-
}
|
|
19278
|
-
return { cwd: checkoutDir, materialized: true, cleanup: removeTempRoot };
|
|
19279
|
-
}
|
|
19280
|
-
function buildGitAuthEnv(remote, token) {
|
|
19281
|
-
let origin;
|
|
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) => {
|
|
19282
19383
|
try {
|
|
19283
|
-
const
|
|
19284
|
-
|
|
19285
|
-
return void 0;
|
|
19286
|
-
origin = `${url.protocol}//${url.host}/`;
|
|
19384
|
+
const reveal = await keep.getUserGithubToken(principal);
|
|
19385
|
+
return reveal.access_token;
|
|
19287
19386
|
} catch {
|
|
19288
|
-
return
|
|
19387
|
+
return null;
|
|
19289
19388
|
}
|
|
19290
|
-
|
|
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));
|
|
19291
19413
|
return {
|
|
19292
|
-
|
|
19293
|
-
|
|
19294
|
-
|
|
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
|
+
}
|
|
19295
19425
|
};
|
|
19296
19426
|
}
|
|
19297
|
-
|
|
19298
|
-
|
|
19299
|
-
|
|
19300
|
-
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
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
|
+
}
|
|
19306
19472
|
}
|
|
19307
|
-
|
|
19308
|
-
|
|
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;
|
|
19309
19482
|
}
|
|
19310
|
-
|
|
19311
|
-
|
|
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
|
+
];
|
|
19312
19546
|
}
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
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;
|
|
19316
19555
|
}
|
|
19317
|
-
const
|
|
19318
|
-
|
|
19319
|
-
return { warning: `workspace.git.fetchRefs contains an invalid ref: ${JSON.stringify(bad)}` };
|
|
19556
|
+
for (const message of primitive(gate.params, output, inputs)) {
|
|
19557
|
+
findings.push({ gate: gate.name, message });
|
|
19320
19558
|
}
|
|
19321
19559
|
}
|
|
19322
|
-
return {
|
|
19560
|
+
return { ok: findings.length === 0, findings };
|
|
19323
19561
|
}
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
|
|
19328
|
-
|
|
19329
|
-
|
|
19330
|
-
|
|
19331
|
-
|
|
19332
|
-
|
|
19333
|
-
|
|
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
|
+
|
|
19572
|
+
// ../../packages/shuttle/dist/logging/logger.js
|
|
19573
|
+
var require_logger = __commonJS({
|
|
19574
|
+
"../../packages/shuttle/dist/logging/logger.js"(exports2) {
|
|
19575
|
+
"use strict";
|
|
19576
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19577
|
+
exports2.createLogger = createLogger;
|
|
19578
|
+
exports2.setGlobalLogger = setGlobalLogger;
|
|
19579
|
+
exports2.getLogger = getLogger;
|
|
19580
|
+
var LEVEL_ORDER = {
|
|
19581
|
+
debug: 0,
|
|
19582
|
+
info: 1,
|
|
19583
|
+
warn: 2,
|
|
19584
|
+
error: 3
|
|
19585
|
+
};
|
|
19586
|
+
function formatContext(context) {
|
|
19587
|
+
return Object.entries(context).map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`).join(" ");
|
|
19588
|
+
}
|
|
19589
|
+
function formatPretty(level, message, context) {
|
|
19590
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
19591
|
+
const tag = level.toUpperCase().padEnd(5);
|
|
19592
|
+
const base = `[${timestamp}] ${tag} ${message}`;
|
|
19593
|
+
if (context && Object.keys(context).length > 0) {
|
|
19594
|
+
return `${base} ${formatContext(context)}`;
|
|
19595
|
+
}
|
|
19596
|
+
return base;
|
|
19597
|
+
}
|
|
19598
|
+
function formatJson(level, message, context) {
|
|
19599
|
+
const entry = {
|
|
19600
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19601
|
+
level,
|
|
19602
|
+
message,
|
|
19603
|
+
...context
|
|
19334
19604
|
};
|
|
19605
|
+
return JSON.stringify(entry);
|
|
19335
19606
|
}
|
|
19336
|
-
function
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
// `extraEnv` (e.g. the credential extraheader) goes first so the two
|
|
19342
|
-
// security-critical vars below always win and can't be overridden.
|
|
19343
|
-
env: {
|
|
19344
|
-
...process.env,
|
|
19345
|
-
...opts.extraEnv ?? {},
|
|
19346
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
19347
|
-
// Transport allowlist. `remote` passes readGitWorkspace as any
|
|
19348
|
-
// non-empty non-`-` string, which still admits `ext::sh -c …` and
|
|
19349
|
-
// `fd::` remotes that execute arbitrary commands on this host.
|
|
19350
|
-
// Restricting to the transports we actually use turns those into a
|
|
19351
|
-
// fast failure (→ degrade) rather than RCE across the boundary this
|
|
19352
|
-
// executor is meant to defend.
|
|
19353
|
-
GIT_ALLOW_PROTOCOL: opts.allowedProtocols
|
|
19354
|
-
}
|
|
19355
|
-
});
|
|
19356
|
-
const stdout = [];
|
|
19357
|
-
const stderr = [];
|
|
19358
|
-
let settled = false;
|
|
19359
|
-
const settle = (result) => {
|
|
19360
|
-
if (settled)
|
|
19361
|
-
return;
|
|
19362
|
-
settled = true;
|
|
19363
|
-
clearTimeout(timer);
|
|
19364
|
-
opts.signal?.removeEventListener("abort", onAbort);
|
|
19365
|
-
resolve(result);
|
|
19366
|
-
};
|
|
19367
|
-
const killChild = () => {
|
|
19368
|
-
child.kill("SIGTERM");
|
|
19369
|
-
const escalate = setTimeout(() => child.kill("SIGKILL"), 5e3);
|
|
19370
|
-
escalate.unref();
|
|
19371
|
-
child.once("close", () => clearTimeout(escalate));
|
|
19372
|
-
};
|
|
19373
|
-
const timer = setTimeout(() => {
|
|
19374
|
-
killChild();
|
|
19375
|
-
settle({
|
|
19376
|
-
code: -1,
|
|
19377
|
-
stdout: Buffer.concat(stdout).toString("utf-8"),
|
|
19378
|
-
stderr: `git ${args[0]} timed out after ${opts.timeoutMs}ms`
|
|
19379
|
-
});
|
|
19380
|
-
}, opts.timeoutMs);
|
|
19381
|
-
const onAbort = () => {
|
|
19382
|
-
killChild();
|
|
19383
|
-
settle({
|
|
19384
|
-
code: -1,
|
|
19385
|
-
stdout: Buffer.concat(stdout).toString("utf-8"),
|
|
19386
|
-
stderr: "aborted"
|
|
19387
|
-
});
|
|
19388
|
-
};
|
|
19389
|
-
if (opts.signal?.aborted) {
|
|
19390
|
-
onAbort();
|
|
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)
|
|
19391
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);
|
|
19392
19620
|
}
|
|
19393
|
-
|
|
19394
|
-
|
|
19395
|
-
|
|
19396
|
-
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
stderr: Buffer.concat(stderr).toString("utf-8")
|
|
19401
|
-
}));
|
|
19402
|
-
});
|
|
19621
|
+
}
|
|
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
|
+
};
|
|
19403
19628
|
}
|
|
19404
|
-
|
|
19405
|
-
|
|
19406
|
-
|
|
19629
|
+
var globalLogger = createLogger({ level: "info", format: "pretty" });
|
|
19630
|
+
function setGlobalLogger(logger) {
|
|
19631
|
+
globalLogger = logger;
|
|
19632
|
+
}
|
|
19633
|
+
function getLogger() {
|
|
19634
|
+
return globalLogger;
|
|
19407
19635
|
}
|
|
19408
19636
|
}
|
|
19409
19637
|
});
|
|
19410
19638
|
|
|
19411
|
-
// ../../packages/shuttle/dist/executors/
|
|
19412
|
-
var
|
|
19413
|
-
"../../packages/shuttle/dist/executors/
|
|
19639
|
+
// ../../packages/shuttle/dist/executors/registry.js
|
|
19640
|
+
var require_registry2 = __commonJS({
|
|
19641
|
+
"../../packages/shuttle/dist/executors/registry.js"(exports2) {
|
|
19414
19642
|
"use strict";
|
|
19415
|
-
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
19416
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
19417
|
-
};
|
|
19418
19643
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19419
|
-
exports2.
|
|
19420
|
-
var
|
|
19421
|
-
|
|
19422
|
-
|
|
19423
|
-
|
|
19424
|
-
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
|
|
19429
|
-
const result = { written: 0, writtenPaths: [], skipped: [] };
|
|
19430
|
-
const files = Array.isArray(overlay.files) ? overlay.files : [];
|
|
19431
|
-
for (const file of files) {
|
|
19432
|
-
const skip = (reason) => result.skipped.push({ path: String(file?.path ?? "?"), reason });
|
|
19433
|
-
if (typeof file?.path !== "string" || typeof file?.url !== "string") {
|
|
19434
|
-
skip("malformed overlay entry (path/url must be strings)");
|
|
19435
|
-
continue;
|
|
19436
|
-
}
|
|
19437
|
-
const dest = safeDestination(rootReal, file.path);
|
|
19438
|
-
if (!dest) {
|
|
19439
|
-
skip("path escapes the workspace root");
|
|
19440
|
-
continue;
|
|
19441
|
-
}
|
|
19442
|
-
let origin;
|
|
19443
|
-
try {
|
|
19444
|
-
const u = new URL(file.url);
|
|
19445
|
-
if (u.protocol !== "https:" && u.protocol !== "http:") {
|
|
19446
|
-
skip(`unsupported url protocol ${u.protocol}`);
|
|
19447
|
-
continue;
|
|
19448
|
-
}
|
|
19449
|
-
origin = normalizeOrigin(u.origin);
|
|
19450
|
-
} catch {
|
|
19451
|
-
skip("invalid url");
|
|
19452
|
-
continue;
|
|
19453
|
-
}
|
|
19454
|
-
if (!origin || !allowed.has(origin)) {
|
|
19455
|
-
skip(`origin not in the overlay allowlist (${origin})`);
|
|
19456
|
-
continue;
|
|
19644
|
+
exports2.ExecutorRegistry = void 0;
|
|
19645
|
+
var ExecutorRegistry = class {
|
|
19646
|
+
factories = /* @__PURE__ */ new Map();
|
|
19647
|
+
allowlist;
|
|
19648
|
+
constructor(allowlist = ["*"]) {
|
|
19649
|
+
this.allowlist = allowlist;
|
|
19650
|
+
}
|
|
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(", ")}`);
|
|
19457
19654
|
}
|
|
19458
|
-
|
|
19459
|
-
|
|
19460
|
-
|
|
19461
|
-
|
|
19462
|
-
|
|
19463
|
-
|
|
19464
|
-
} catch (err) {
|
|
19465
|
-
skip(err instanceof Error ? err.message : String(err));
|
|
19655
|
+
this.factories.set(name, factory);
|
|
19656
|
+
}
|
|
19657
|
+
create(name, config) {
|
|
19658
|
+
const factory = this.factories.get(name);
|
|
19659
|
+
if (!factory) {
|
|
19660
|
+
throw new Error(`Executor not registered: ${name}`);
|
|
19466
19661
|
}
|
|
19662
|
+
return factory(config, name);
|
|
19467
19663
|
}
|
|
19468
|
-
|
|
19469
|
-
|
|
19470
|
-
function safeDestination(rootReal, relPath) {
|
|
19471
|
-
if (relPath.length === 0 || path_1.default.isAbsolute(relPath))
|
|
19472
|
-
return void 0;
|
|
19473
|
-
const resolved = path_1.default.resolve(rootReal, relPath);
|
|
19474
|
-
const rootWithSep = rootReal.endsWith(path_1.default.sep) ? rootReal : rootReal + path_1.default.sep;
|
|
19475
|
-
if (resolved !== rootReal && !resolved.startsWith(rootWithSep))
|
|
19476
|
-
return void 0;
|
|
19477
|
-
if (relPath.split(/[/\\]/).includes(".."))
|
|
19478
|
-
return void 0;
|
|
19479
|
-
return resolved;
|
|
19480
|
-
}
|
|
19481
|
-
function normalizeOrigin(value) {
|
|
19482
|
-
try {
|
|
19483
|
-
const u = new URL(value.includes("://") ? value : `https://${value}`);
|
|
19484
|
-
return u.origin;
|
|
19485
|
-
} catch {
|
|
19486
|
-
return void 0;
|
|
19664
|
+
has(name) {
|
|
19665
|
+
return this.factories.has(name);
|
|
19487
19666
|
}
|
|
19488
|
-
|
|
19489
|
-
|
|
19490
|
-
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
|
|
19494
|
-
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
|
|
19498
|
-
|
|
19499
|
-
if (!response.body) {
|
|
19500
|
-
const buf = Buffer.from(await response.arrayBuffer());
|
|
19501
|
-
if (buf.byteLength > opts.maxBytes)
|
|
19502
|
-
throw new Error("overlay file exceeds size cap");
|
|
19503
|
-
return buf;
|
|
19504
|
-
}
|
|
19505
|
-
const chunks = [];
|
|
19506
|
-
let total = 0;
|
|
19507
|
-
for await (const chunk of response.body) {
|
|
19508
|
-
const buf = Buffer.from(chunk);
|
|
19509
|
-
total += buf.byteLength;
|
|
19510
|
-
if (total > opts.maxBytes)
|
|
19511
|
-
throw new Error("overlay file exceeds size cap");
|
|
19512
|
-
chunks.push(buf);
|
|
19667
|
+
get allowedExecutors() {
|
|
19668
|
+
return this.allowlist;
|
|
19669
|
+
}
|
|
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.`);
|
|
19676
|
+
}
|
|
19677
|
+
out.push(this.create(name, cfg));
|
|
19513
19678
|
}
|
|
19514
|
-
return
|
|
19515
|
-
} finally {
|
|
19516
|
-
clearTimeout(timer);
|
|
19517
|
-
opts.signal?.removeEventListener("abort", onParentAbort);
|
|
19679
|
+
return out;
|
|
19518
19680
|
}
|
|
19519
|
-
|
|
19681
|
+
isAllowed(name) {
|
|
19682
|
+
return this.allowlist[0] === "*" || this.allowlist.includes(name);
|
|
19683
|
+
}
|
|
19684
|
+
};
|
|
19685
|
+
exports2.ExecutorRegistry = ExecutorRegistry;
|
|
19520
19686
|
}
|
|
19521
19687
|
});
|
|
19522
19688
|
|
|
19523
|
-
// ../../packages/shuttle/dist/
|
|
19524
|
-
var
|
|
19525
|
-
"../../packages/shuttle/dist/
|
|
19689
|
+
// ../../packages/shuttle/dist/executors/validate-output.js
|
|
19690
|
+
var require_validate_output = __commonJS({
|
|
19691
|
+
"../../packages/shuttle/dist/executors/validate-output.js"(exports2) {
|
|
19526
19692
|
"use strict";
|
|
19527
19693
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
19528
|
-
exports2.
|
|
19529
|
-
|
|
19530
|
-
|
|
19531
|
-
|
|
19532
|
-
|
|
19533
|
-
|
|
19534
|
-
if (!match)
|
|
19535
|
-
return value;
|
|
19536
|
-
const varName = match[1];
|
|
19537
|
-
const resolved = process.env[varName];
|
|
19538
|
-
if (resolved === void 0 || resolved === "") {
|
|
19539
|
-
const subject = fieldName ? `${fieldName} (${value})` : value;
|
|
19540
|
-
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.`);
|
|
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("; ")}`);
|
|
19541
19700
|
}
|
|
19542
|
-
return resolved;
|
|
19543
|
-
}
|
|
19544
|
-
function isEnvRef(value) {
|
|
19545
|
-
return ENV_REF_PATTERN.test(value);
|
|
19546
19701
|
}
|
|
19547
|
-
|
|
19548
|
-
|
|
19549
|
-
|
|
19550
|
-
|
|
19702
|
+
}
|
|
19703
|
+
});
|
|
19704
|
+
|
|
19705
|
+
// ../../packages/shuttle/dist/executors/progress.js
|
|
19706
|
+
var require_progress = __commonJS({
|
|
19707
|
+
"../../packages/shuttle/dist/executors/progress.js"(exports2) {
|
|
19708
|
+
"use strict";
|
|
19709
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
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;
|
|
19551
19724
|
}
|
|
19552
|
-
|
|
19553
|
-
|
|
19554
|
-
|
|
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();
|
|
19733
|
+
}
|
|
19555
19734
|
}
|
|
19556
|
-
|
|
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 = [];
|
|
19752
|
+
try {
|
|
19753
|
+
this.sink(batch);
|
|
19754
|
+
} catch {
|
|
19755
|
+
}
|
|
19756
|
+
}
|
|
19757
|
+
/** Final flush; no further frames are emitted after this. */
|
|
19758
|
+
close() {
|
|
19759
|
+
this.flush();
|
|
19760
|
+
this.closed = true;
|
|
19761
|
+
this.clearTimer();
|
|
19762
|
+
}
|
|
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;
|
|
19785
|
+
}
|
|
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);
|
|
19803
|
+
}
|
|
19804
|
+
}
|
|
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 = "";
|
|
19810
|
+
}
|
|
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
|
+
}
|
|
19838
|
+
}
|
|
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;
|
|
19848
|
+
}
|
|
19849
|
+
return void 0;
|
|
19557
19850
|
}
|
|
19558
19851
|
}
|
|
19559
19852
|
});
|
|
@@ -19576,12 +19869,8 @@ var require_claude_code = __commonJS({
|
|
|
19576
19869
|
var path_1 = __importDefault(require("path"));
|
|
19577
19870
|
var string_decoder_1 = require("string_decoder");
|
|
19578
19871
|
var loom_1 = require_dist3();
|
|
19579
|
-
var keep_1 = require_dist4();
|
|
19580
19872
|
var validate_output_1 = require_validate_output();
|
|
19581
19873
|
var progress_1 = require_progress();
|
|
19582
|
-
var workspace_1 = require_workspace();
|
|
19583
|
-
var overlay_1 = require_overlay();
|
|
19584
|
-
var env_ref_1 = require_env_ref();
|
|
19585
19874
|
var apply_1 = require_apply();
|
|
19586
19875
|
var DEFAULT_TIMEOUT = 6e5;
|
|
19587
19876
|
var ClaudeCodeExecutor = class {
|
|
@@ -19599,47 +19888,49 @@ var require_claude_code = __commonJS({
|
|
|
19599
19888
|
outputTokens: spec.costCapHint?.estimatedOutputTokens
|
|
19600
19889
|
};
|
|
19601
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
|
+
}
|
|
19602
19910
|
async execute(dispatch, signal, ctx) {
|
|
19603
19911
|
const { prompt, spec } = (0, loom_1.renderPromptExecution)(dispatch.inputs);
|
|
19604
|
-
|
|
19605
|
-
|
|
19912
|
+
const workspace = ctx?.workspace ?? null;
|
|
19913
|
+
let modelKey = null;
|
|
19914
|
+
if (ctx?.credentials) {
|
|
19606
19915
|
const hints = (0, loom_1.pickProviderHints)(spec, "anthropic-api");
|
|
19607
19916
|
const customerId = typeof hints.customerId === "string" ? hints.customerId : void 0;
|
|
19608
19917
|
if (!customerId) {
|
|
19609
19918
|
throw new Error("claude-code dynamic-key mode requires providerHints.anthropicApi.customerId on every job");
|
|
19610
19919
|
}
|
|
19611
|
-
|
|
19612
|
-
customerId,
|
|
19613
|
-
needGithubToken: !!spec.workspace
|
|
19614
|
-
});
|
|
19920
|
+
modelKey = await ctx.credentials.reveal({ name: "managed-model-key", principal: customerId });
|
|
19615
19921
|
}
|
|
19616
19922
|
let jobScratch = null;
|
|
19617
19923
|
let configDir;
|
|
19618
|
-
if (
|
|
19924
|
+
if (modelKey) {
|
|
19619
19925
|
jobScratch = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "shuttle-cc-"));
|
|
19620
19926
|
configDir = path_1.default.join(jobScratch, "claude-config");
|
|
19621
19927
|
await fs_1.promises.mkdir(configDir);
|
|
19622
19928
|
}
|
|
19623
19929
|
const tmpBase = this.config.tmpDir ? (0, apply_1.expandHome)(this.config.tmpDir) : os_1.default.tmpdir();
|
|
19624
|
-
let workspace = null;
|
|
19625
19930
|
let promptOnlyScratch = null;
|
|
19626
|
-
let overlayResult = null;
|
|
19627
19931
|
const reporter = spec.progressChannel && ctx?.reportProgress ? new progress_1.ProgressReporter((frames) => ctx.reportProgress(spec.progressChannel, frames)) : null;
|
|
19628
19932
|
try {
|
|
19629
|
-
|
|
19630
|
-
workspace = await (0, workspace_1.prepareWorkspace)(spec.workspace, {
|
|
19631
|
-
signal,
|
|
19632
|
-
tmpDir: tmpBase,
|
|
19633
|
-
...credentials?.githubToken ? { gitToken: credentials.githubToken } : {}
|
|
19634
|
-
});
|
|
19635
|
-
if (spec.workspace.overlay) {
|
|
19636
|
-
overlayResult = await (0, overlay_1.applyOverlay)(workspace.cwd, spec.workspace.overlay, {
|
|
19637
|
-
allowedHosts: this.config.overlayAllowedHosts ?? [],
|
|
19638
|
-
signal
|
|
19639
|
-
});
|
|
19640
|
-
}
|
|
19641
|
-
}
|
|
19642
|
-
const childEnv = buildChildEnv(credentials, configDir);
|
|
19933
|
+
const childEnv = buildChildEnv(modelKey, configDir);
|
|
19643
19934
|
const claudeHints = (0, loom_1.pickProviderHints)(spec, "claude-code");
|
|
19644
19935
|
const hintedModel = typeof claudeHints.model === "string" && claudeHints.model.length > 0 ? claudeHints.model : void 0;
|
|
19645
19936
|
if (!workspace) {
|
|
@@ -19649,7 +19940,7 @@ var require_claude_code = __commonJS({
|
|
|
19649
19940
|
const { response, readPaths, toolText } = await this.runClaude(prompt, spec, runCwd, childEnv, signal, hintedModel, reporter);
|
|
19650
19941
|
if (workspace) {
|
|
19651
19942
|
const reads = [...readPaths];
|
|
19652
|
-
for (const rel of
|
|
19943
|
+
for (const rel of workspace.overlay?.writtenPaths ?? []) {
|
|
19653
19944
|
if (toolText.includes(rel))
|
|
19654
19945
|
reads.push(path_1.default.join(runCwd, rel));
|
|
19655
19946
|
}
|
|
@@ -19662,10 +19953,17 @@ var require_claude_code = __commonJS({
|
|
|
19662
19953
|
content: {
|
|
19663
19954
|
materialized: workspace.materialized,
|
|
19664
19955
|
...workspace.warning ? { warning: workspace.warning } : {},
|
|
19665
|
-
...
|
|
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 ? {
|
|
19666
19964
|
overlay: {
|
|
19667
|
-
written:
|
|
19668
|
-
...
|
|
19965
|
+
written: workspace.overlay.written,
|
|
19966
|
+
...workspace.overlay.skipped.length > 0 ? { skipped: workspace.overlay.skipped } : {}
|
|
19669
19967
|
}
|
|
19670
19968
|
} : {}
|
|
19671
19969
|
}
|
|
@@ -19674,8 +19972,6 @@ var require_claude_code = __commonJS({
|
|
|
19674
19972
|
return response;
|
|
19675
19973
|
} finally {
|
|
19676
19974
|
reporter?.close();
|
|
19677
|
-
if (workspace)
|
|
19678
|
-
await workspace.cleanup();
|
|
19679
19975
|
if (promptOnlyScratch)
|
|
19680
19976
|
await fs_1.promises.rm(promptOnlyScratch, { recursive: true, force: true }).catch(() => {
|
|
19681
19977
|
});
|
|
@@ -19838,7 +20134,7 @@ var require_claude_code = __commonJS({
|
|
|
19838
20134
|
});
|
|
19839
20135
|
}
|
|
19840
20136
|
};
|
|
19841
|
-
function createClaudeCodeExecutor(config, capabilityId = "claude-code"
|
|
20137
|
+
function createClaudeCodeExecutor(config, capabilityId = "claude-code") {
|
|
19842
20138
|
const validated = { capabilityId };
|
|
19843
20139
|
if (config.tmpDir !== void 0) {
|
|
19844
20140
|
if (typeof config.tmpDir !== "string" || config.tmpDir.length === 0) {
|
|
@@ -19869,9 +20165,6 @@ var require_claude_code = __commonJS({
|
|
|
19869
20165
|
}
|
|
19870
20166
|
validated.timeout = config.timeout;
|
|
19871
20167
|
}
|
|
19872
|
-
if (config.apiKeyFrom !== void 0) {
|
|
19873
|
-
validated.resolveCredentials = buildCredentialResolver(config, deps);
|
|
19874
|
-
}
|
|
19875
20168
|
if (config.overlayAllowedHosts !== void 0) {
|
|
19876
20169
|
if (!Array.isArray(config.overlayAllowedHosts) || !config.overlayAllowedHosts.every((h) => typeof h === "string")) {
|
|
19877
20170
|
throw new Error('"overlayAllowedHosts" must be an array of strings');
|
|
@@ -19880,43 +20173,15 @@ var require_claude_code = __commonJS({
|
|
|
19880
20173
|
}
|
|
19881
20174
|
return new ClaudeCodeExecutor(validated);
|
|
19882
20175
|
}
|
|
19883
|
-
function buildChildEnv(
|
|
19884
|
-
if (!
|
|
20176
|
+
function buildChildEnv(modelKey, configDir) {
|
|
20177
|
+
if (!modelKey && !configDir)
|
|
19885
20178
|
return void 0;
|
|
19886
20179
|
return {
|
|
19887
20180
|
...process.env,
|
|
19888
|
-
...
|
|
20181
|
+
...modelKey ? { ANTHROPIC_API_KEY: modelKey } : {},
|
|
19889
20182
|
...configDir ? { CLAUDE_CONFIG_DIR: configDir } : {}
|
|
19890
20183
|
};
|
|
19891
20184
|
}
|
|
19892
|
-
function buildCredentialResolver(config, deps) {
|
|
19893
|
-
if (config.apiKeyFrom !== "job-metadata") {
|
|
19894
|
-
throw new Error(`Unsupported "apiKeyFrom" value: ${JSON.stringify(config.apiKeyFrom)}. Only "job-metadata" is supported.`);
|
|
19895
|
-
}
|
|
19896
|
-
const keepApiUrl = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepApiUrl", "KEEP_API_URL");
|
|
19897
|
-
const clientId = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepClientId", "KEEP_CLIENT_ID");
|
|
19898
|
-
const clientSecret = (0, env_ref_1.resolveConfigStringOrEnv)(config, "keepClientSecret", "KEEP_CLIENT_SECRET");
|
|
19899
|
-
const stmFactory = deps.buildServiceTokenManager ?? ((cfg) => new keep_1.ServiceTokenManager(cfg));
|
|
19900
|
-
const stm = stmFactory({ keepApiUrl, clientId, clientSecret });
|
|
19901
|
-
const keepClientFactory = deps.buildKeepClient ?? ((cfg) => new keep_1.KeepClient(cfg));
|
|
19902
|
-
return async ({ customerId, needGithubToken }) => {
|
|
19903
|
-
const serviceToken = await stm.getToken();
|
|
19904
|
-
if (!serviceToken) {
|
|
19905
|
-
throw new Error("claude-code dynamic-key mode failed to exchange a Keep service token. Check KEEP_CLIENT_ID / KEEP_CLIENT_SECRET.");
|
|
19906
|
-
}
|
|
19907
|
-
const keep = keepClientFactory({ baseUrl: keepApiUrl, serviceToken });
|
|
19908
|
-
const reveal = await keep.revealManagedSifterKey(customerId);
|
|
19909
|
-
const credentials = { anthropicApiKey: reveal.apiKey };
|
|
19910
|
-
if (needGithubToken) {
|
|
19911
|
-
try {
|
|
19912
|
-
const gh = await keep.getUserGithubToken(customerId);
|
|
19913
|
-
credentials.githubToken = gh.access_token;
|
|
19914
|
-
} catch {
|
|
19915
|
-
}
|
|
19916
|
-
}
|
|
19917
|
-
return credentials;
|
|
19918
|
-
};
|
|
19919
|
-
}
|
|
19920
20185
|
function parseStreamJson(stdout) {
|
|
19921
20186
|
let finalText = null;
|
|
19922
20187
|
const readPaths = [];
|
|
@@ -21161,12 +21426,14 @@ var require_shuttle = __commonJS({
|
|
|
21161
21426
|
var decommission_1 = require_decommission();
|
|
21162
21427
|
var loom_1 = require_dist3();
|
|
21163
21428
|
var chain_1 = require_chain();
|
|
21429
|
+
var workspace_1 = require_workspace();
|
|
21430
|
+
var credentials_1 = require_credentials();
|
|
21164
21431
|
var output_gates_1 = require_output_gates();
|
|
21165
21432
|
var keep_1 = require_dist4();
|
|
21166
21433
|
var apply_1 = require_apply();
|
|
21167
21434
|
var version_check_1 = require_version_check();
|
|
21168
21435
|
var logger_1 = require_logger();
|
|
21169
|
-
var registry_1 =
|
|
21436
|
+
var registry_1 = require_registry2();
|
|
21170
21437
|
var claude_code_1 = require_claude_code();
|
|
21171
21438
|
var http_api_1 = require_http_api();
|
|
21172
21439
|
var anthropic_api_1 = require_anthropic_api();
|
|
@@ -21211,12 +21478,20 @@ var require_shuttle = __commonJS({
|
|
|
21211
21478
|
brand;
|
|
21212
21479
|
config;
|
|
21213
21480
|
profile;
|
|
21481
|
+
credentialDeps;
|
|
21214
21482
|
subscription = null;
|
|
21215
21483
|
spendTracker = null;
|
|
21484
|
+
/**
|
|
21485
|
+
* The runner-owned credential broker (ADR 0027), built once from the
|
|
21486
|
+
* `credentials` config (the reveal service identity). Null in local mode
|
|
21487
|
+
* (no such config) → consumers reveal nothing and use ambient credentials.
|
|
21488
|
+
*/
|
|
21489
|
+
broker = null;
|
|
21216
21490
|
constructor(options) {
|
|
21217
21491
|
this.brand = options.brand;
|
|
21218
21492
|
this.config = options.config;
|
|
21219
21493
|
this.profile = options.profile ?? apply_1.DEFAULT_PROFILE;
|
|
21494
|
+
this.credentialDeps = options.credentialDeps ?? {};
|
|
21220
21495
|
}
|
|
21221
21496
|
/** Exposed so the CLI's `caps set` can reload after a write. */
|
|
21222
21497
|
get spendTrackerHandle() {
|
|
@@ -21230,6 +21505,7 @@ var require_shuttle = __commonJS({
|
|
|
21230
21505
|
const logger = (0, logger_1.getLogger)();
|
|
21231
21506
|
logger.info(`${this.brand.product.title} starting...`);
|
|
21232
21507
|
const paths = (0, apply_1.resolvePaths)(this.brand, this.profile);
|
|
21508
|
+
this.broker = (0, credentials_1.createCredentialBroker)(this.config.credentials, this.credentialDeps);
|
|
21233
21509
|
const enforceMinVersion = (response) => {
|
|
21234
21510
|
(0, version_check_1.checkResponseMinVersion)({
|
|
21235
21511
|
response,
|
|
@@ -21267,7 +21543,7 @@ var require_shuttle = __commonJS({
|
|
|
21267
21543
|
error: err instanceof Error ? err.message : String(err)
|
|
21268
21544
|
});
|
|
21269
21545
|
};
|
|
21270
|
-
const onJob = async (dispatch, signal) => {
|
|
21546
|
+
const onJob = async (dispatch, signal, activity) => {
|
|
21271
21547
|
const jobId = dispatch.job.id;
|
|
21272
21548
|
const attendanceId = dispatch.attendanceId;
|
|
21273
21549
|
const startedAt = Date.now();
|
|
@@ -21303,11 +21579,30 @@ var require_shuttle = __commonJS({
|
|
|
21303
21579
|
}
|
|
21304
21580
|
await observerChain.notify({ kind: "executor.dispatch.started", jobId, attendanceId, executor: executor.capability.id, strategy }, onObserverError);
|
|
21305
21581
|
const dispatchStartedAt = Date.now();
|
|
21582
|
+
const environment = executor.prepareEnvironment ? await executor.prepareEnvironment(dispatch) : void 0;
|
|
21583
|
+
let preparedWorkspace = null;
|
|
21584
|
+
if (environment?.workspaceRequest) {
|
|
21585
|
+
try {
|
|
21586
|
+
preparedWorkspace = await (0, workspace_1.materializeWorkspace)(environment.workspaceRequest, {
|
|
21587
|
+
...environment.materializeOptions ?? {},
|
|
21588
|
+
credentials: this.broker,
|
|
21589
|
+
signal
|
|
21590
|
+
});
|
|
21591
|
+
} catch (err) {
|
|
21592
|
+
(0, logger_1.getLogger)().warn("workspace materialization failed; running prompt-only", {
|
|
21593
|
+
jobId,
|
|
21594
|
+
error: err instanceof Error ? err.message : String(err)
|
|
21595
|
+
});
|
|
21596
|
+
preparedWorkspace = null;
|
|
21597
|
+
}
|
|
21598
|
+
}
|
|
21306
21599
|
const executionCtx = {
|
|
21307
21600
|
reportProgress: (channel, frames) => {
|
|
21308
|
-
void jobsClient.postProgress(jobId, { channel, frames }).catch(() => {
|
|
21601
|
+
void jobsClient.postProgress(jobId, { channel, frames }).then(() => activity.record()).catch(() => {
|
|
21309
21602
|
});
|
|
21310
|
-
}
|
|
21603
|
+
},
|
|
21604
|
+
workspace: preparedWorkspace,
|
|
21605
|
+
credentials: this.broker
|
|
21311
21606
|
};
|
|
21312
21607
|
let response;
|
|
21313
21608
|
try {
|
|
@@ -21316,6 +21611,9 @@ var require_shuttle = __commonJS({
|
|
|
21316
21611
|
const reason = `executor failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
21317
21612
|
await observerChain.notify({ kind: "attendance.closed", jobId, attendanceId, status: "failed", reason, totalDurationMs: Date.now() - startedAt }, onObserverError);
|
|
21318
21613
|
return { status: "failed", reason };
|
|
21614
|
+
} finally {
|
|
21615
|
+
if (preparedWorkspace)
|
|
21616
|
+
await preparedWorkspace.cleanup();
|
|
21319
21617
|
}
|
|
21320
21618
|
const gated = await runOutputGates(dispatch, response, executor, signal, executionCtx);
|
|
21321
21619
|
if (!gated.ok) {
|
|
@@ -21899,7 +22197,7 @@ var require_dist5 = __commonJS({
|
|
|
21899
22197
|
Object.defineProperty(exports2, "AgentCredentialRevokedError", { enumerable: true, get: function() {
|
|
21900
22198
|
return keep_1.AgentCredentialRevokedError;
|
|
21901
22199
|
} });
|
|
21902
|
-
var registry_1 =
|
|
22200
|
+
var registry_1 = require_registry2();
|
|
21903
22201
|
Object.defineProperty(exports2, "ExecutorRegistry", { enumerable: true, get: function() {
|
|
21904
22202
|
return registry_1.ExecutorRegistry;
|
|
21905
22203
|
} });
|
|
@@ -21995,7 +22293,7 @@ var import_path = require("path");
|
|
|
21995
22293
|
var import_promises = require("fs/promises");
|
|
21996
22294
|
var import_yaml = __toESM(require_dist());
|
|
21997
22295
|
var import_shuttle = __toESM(require_dist5());
|
|
21998
|
-
var buildVersion = true ? "0.
|
|
22296
|
+
var buildVersion = true ? "0.21.0" : pkg.version;
|
|
21999
22297
|
var sifterBrand = {
|
|
22000
22298
|
product: {
|
|
22001
22299
|
id: "sifter",
|