aglio-worker 0.10.0 → 0.11.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/agent.js +184 -54
- package/package.json +1 -1
package/agent.js
CHANGED
|
@@ -7302,8 +7302,8 @@ var require_content_type = __commonJS({
|
|
|
7302
7302
|
});
|
|
7303
7303
|
|
|
7304
7304
|
// src/remote-agent.ts
|
|
7305
|
-
import { homedir as
|
|
7306
|
-
import { join as
|
|
7305
|
+
import { homedir as homedir3, hostname as hostname3 } from "node:os";
|
|
7306
|
+
import { join as join8 } from "node:path";
|
|
7307
7307
|
|
|
7308
7308
|
// ../../packages/shared/src/enums.ts
|
|
7309
7309
|
var CARD_COLUMNS = ["backlog", "todo", "doing", "review", "done"];
|
|
@@ -28248,8 +28248,110 @@ var cardOpenModeSchema = external_exports.enum(CARD_OPEN_MODES);
|
|
|
28248
28248
|
// src/remote-docker.ts
|
|
28249
28249
|
import { existsSync } from "node:fs";
|
|
28250
28250
|
import { PassThrough } from "node:stream";
|
|
28251
|
-
import { join, posix, relative, resolve } from "node:path";
|
|
28251
|
+
import { join as join2, posix, relative, resolve } from "node:path";
|
|
28252
28252
|
import Dockerode from "dockerode";
|
|
28253
|
+
|
|
28254
|
+
// src/docker-images.ts
|
|
28255
|
+
import { readFileSync } from "node:fs";
|
|
28256
|
+
import { homedir } from "node:os";
|
|
28257
|
+
import { join } from "node:path";
|
|
28258
|
+
function isRegistryImage(image) {
|
|
28259
|
+
return image.includes("/");
|
|
28260
|
+
}
|
|
28261
|
+
function registryHost(image) {
|
|
28262
|
+
const first = image.split("/")[0] ?? "";
|
|
28263
|
+
if (first.includes(".") || first.includes(":") || first === "localhost") return first;
|
|
28264
|
+
return "https://index.docker.io/v1/";
|
|
28265
|
+
}
|
|
28266
|
+
function dockerAuthFor(image) {
|
|
28267
|
+
const configDir = process.env.DOCKER_CONFIG || join(homedir(), ".docker");
|
|
28268
|
+
let config2;
|
|
28269
|
+
try {
|
|
28270
|
+
config2 = JSON.parse(readFileSync(join(configDir, "config.json"), "utf8"));
|
|
28271
|
+
} catch {
|
|
28272
|
+
return void 0;
|
|
28273
|
+
}
|
|
28274
|
+
const host = registryHost(image);
|
|
28275
|
+
const entry = config2.auths?.[host] ?? config2.auths?.[`https://${host}`] ?? config2.auths?.[`http://${host}`];
|
|
28276
|
+
if (!entry) return void 0;
|
|
28277
|
+
if (entry.auth) {
|
|
28278
|
+
const decoded = Buffer.from(entry.auth, "base64").toString("utf8");
|
|
28279
|
+
const separator = decoded.indexOf(":");
|
|
28280
|
+
if (separator > 0) {
|
|
28281
|
+
return {
|
|
28282
|
+
username: decoded.slice(0, separator),
|
|
28283
|
+
password: decoded.slice(separator + 1)
|
|
28284
|
+
};
|
|
28285
|
+
}
|
|
28286
|
+
}
|
|
28287
|
+
if (entry.username && entry.password) {
|
|
28288
|
+
return { username: entry.username, password: entry.password };
|
|
28289
|
+
}
|
|
28290
|
+
if (entry.identitytoken) return { identitytoken: entry.identitytoken };
|
|
28291
|
+
if (entry.registrytoken) return { registrytoken: entry.registrytoken };
|
|
28292
|
+
return void 0;
|
|
28293
|
+
}
|
|
28294
|
+
var inFlight = /* @__PURE__ */ new WeakMap();
|
|
28295
|
+
async function pullDockerImage(client, image) {
|
|
28296
|
+
if (!isRegistryImage(image)) {
|
|
28297
|
+
throw new Error(
|
|
28298
|
+
`Runner image ${image} is not registry-qualified. Build it with: pnpm runner:build`
|
|
28299
|
+
);
|
|
28300
|
+
}
|
|
28301
|
+
let pending = inFlight.get(client);
|
|
28302
|
+
if (!pending) {
|
|
28303
|
+
pending = /* @__PURE__ */ new Map();
|
|
28304
|
+
inFlight.set(client, pending);
|
|
28305
|
+
}
|
|
28306
|
+
const existing = pending.get(image);
|
|
28307
|
+
if (existing) return existing;
|
|
28308
|
+
const pull = (async () => {
|
|
28309
|
+
const stream = await client.pull(image, { authconfig: dockerAuthFor(image) });
|
|
28310
|
+
await new Promise((resolve6, reject) => {
|
|
28311
|
+
stream.on("data", () => void 0);
|
|
28312
|
+
stream.on("end", () => resolve6());
|
|
28313
|
+
stream.on("error", (error62) => reject(error62));
|
|
28314
|
+
});
|
|
28315
|
+
})();
|
|
28316
|
+
pending.set(image, pull);
|
|
28317
|
+
try {
|
|
28318
|
+
await pull;
|
|
28319
|
+
} finally {
|
|
28320
|
+
pending.delete(image);
|
|
28321
|
+
}
|
|
28322
|
+
}
|
|
28323
|
+
async function ensureDockerImage(input2) {
|
|
28324
|
+
try {
|
|
28325
|
+
await input2.client.getImage(input2.image).inspect();
|
|
28326
|
+
return "present";
|
|
28327
|
+
} catch {
|
|
28328
|
+
}
|
|
28329
|
+
if (!isRegistryImage(input2.image)) {
|
|
28330
|
+
throw new Error(
|
|
28331
|
+
`Runner image ${input2.image} not found on this machine. Build it with: pnpm runner:build`
|
|
28332
|
+
);
|
|
28333
|
+
}
|
|
28334
|
+
input2.log?.("runner image missing, pulling", { image: input2.image });
|
|
28335
|
+
try {
|
|
28336
|
+
await pullDockerImage(input2.client, input2.image);
|
|
28337
|
+
} catch (error62) {
|
|
28338
|
+
const reason = error62 instanceof Error ? error62.message : String(error62);
|
|
28339
|
+
throw new Error(
|
|
28340
|
+
`Runner image ${input2.image} not found and could not be pulled: ${reason}. Check registry access or docker login, then retry: docker pull ${input2.image}`
|
|
28341
|
+
);
|
|
28342
|
+
}
|
|
28343
|
+
try {
|
|
28344
|
+
await input2.client.getImage(input2.image).inspect();
|
|
28345
|
+
} catch {
|
|
28346
|
+
throw new Error(
|
|
28347
|
+
`Runner image ${input2.image} pull finished but the image is still missing locally. Retry: docker pull ${input2.image}`
|
|
28348
|
+
);
|
|
28349
|
+
}
|
|
28350
|
+
input2.log?.("runner image pulled", { image: input2.image });
|
|
28351
|
+
return "pulled";
|
|
28352
|
+
}
|
|
28353
|
+
|
|
28354
|
+
// src/remote-docker.ts
|
|
28253
28355
|
async function runnerCpuCount(client) {
|
|
28254
28356
|
const info = await client.info().catch(() => null);
|
|
28255
28357
|
return Math.max(1, Math.min(2, Math.floor(info?.NCPU ?? 1)));
|
|
@@ -28291,25 +28393,24 @@ function mapDockerWorkspace(input2) {
|
|
|
28291
28393
|
throw new Error(`Path escapes the job workspace: ${input2.target}`);
|
|
28292
28394
|
}
|
|
28293
28395
|
return {
|
|
28294
|
-
hostPath: resolve(
|
|
28396
|
+
hostPath: resolve(join2(input2.hostWorkspaceRoot, rel)),
|
|
28295
28397
|
containerPath: containerRel ? posix.join(CONTAINER_WORKSPACE, containerRel.split(/[\\/]+/).join("/")) : CONTAINER_WORKSPACE
|
|
28296
28398
|
};
|
|
28297
28399
|
}
|
|
28298
28400
|
function sleep(ms) {
|
|
28299
28401
|
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
28300
28402
|
}
|
|
28403
|
+
var defaultClients = /* @__PURE__ */ new Map();
|
|
28301
28404
|
function defaultClient(socketPath) {
|
|
28302
|
-
|
|
28405
|
+
const existing = defaultClients.get(socketPath);
|
|
28406
|
+
if (existing) return existing;
|
|
28407
|
+
const client = new Dockerode({ socketPath });
|
|
28408
|
+
defaultClients.set(socketPath, client);
|
|
28409
|
+
return client;
|
|
28303
28410
|
}
|
|
28304
28411
|
async function createDockerJobExecutor(input2) {
|
|
28305
28412
|
const client = input2.client ?? defaultClient(input2.socketPath ?? "/var/run/docker.sock");
|
|
28306
|
-
|
|
28307
|
-
await client.getImage(input2.image).inspect();
|
|
28308
|
-
} catch {
|
|
28309
|
-
throw new Error(
|
|
28310
|
-
`Runner image ${input2.image} not found on this machine. Pull it with: docker pull ${input2.image}`
|
|
28311
|
-
);
|
|
28312
|
-
}
|
|
28413
|
+
await ensureDockerImage({ client, image: input2.image, log: input2.log });
|
|
28313
28414
|
const { hostPath } = mapDockerWorkspace({
|
|
28314
28415
|
workspaceRoot: input2.workspaceRoot,
|
|
28315
28416
|
hostWorkspaceRoot: input2.hostWorkspaceRoot,
|
|
@@ -28421,13 +28522,35 @@ async function createDockerJobExecutor(input2) {
|
|
|
28421
28522
|
}
|
|
28422
28523
|
};
|
|
28423
28524
|
}
|
|
28424
|
-
async function
|
|
28425
|
-
const client =
|
|
28525
|
+
async function prefetchRunnerImage(input2) {
|
|
28526
|
+
const client = input2.client ?? defaultClient(input2.socketPath ?? "/var/run/docker.sock");
|
|
28426
28527
|
try {
|
|
28427
|
-
|
|
28428
|
-
|
|
28429
|
-
|
|
28528
|
+
if (isRegistryImage(input2.image)) {
|
|
28529
|
+
await pullDockerImage(client, input2.image);
|
|
28530
|
+
input2.log?.("runner image refreshed", { image: input2.image });
|
|
28531
|
+
return true;
|
|
28532
|
+
}
|
|
28533
|
+
const result = await ensureDockerImage({
|
|
28534
|
+
client,
|
|
28535
|
+
image: input2.image,
|
|
28536
|
+
log: input2.log
|
|
28537
|
+
});
|
|
28538
|
+
if (result === "present") input2.log?.("runner image ready", { image: input2.image });
|
|
28539
|
+
return true;
|
|
28540
|
+
} catch (error62) {
|
|
28541
|
+
input2.log?.("runner image unavailable", {
|
|
28542
|
+
image: input2.image,
|
|
28543
|
+
error: error62 instanceof Error ? error62.message : String(error62)
|
|
28544
|
+
});
|
|
28545
|
+
return false;
|
|
28430
28546
|
}
|
|
28547
|
+
}
|
|
28548
|
+
async function createDockerLoginContainer(input2) {
|
|
28549
|
+
const client = new Dockerode({ socketPath: input2.socketPath ?? "/var/run/docker.sock" });
|
|
28550
|
+
await ensureDockerImage({
|
|
28551
|
+
client,
|
|
28552
|
+
image: input2.image
|
|
28553
|
+
});
|
|
28431
28554
|
const { hostPath } = mapDockerWorkspace({
|
|
28432
28555
|
workspaceRoot: input2.workspaceRoot,
|
|
28433
28556
|
hostWorkspaceRoot: input2.hostWorkspaceRoot,
|
|
@@ -28554,8 +28677,8 @@ async function verifyHostRuntime(input2, run = defaultRunner) {
|
|
|
28554
28677
|
// src/job-runner.ts
|
|
28555
28678
|
import { spawn } from "node:child_process";
|
|
28556
28679
|
import { access, mkdir, readFile, stat } from "node:fs/promises";
|
|
28557
|
-
import { homedir } from "node:os";
|
|
28558
|
-
import { isAbsolute, join as
|
|
28680
|
+
import { homedir as homedir2 } from "node:os";
|
|
28681
|
+
import { isAbsolute, join as join3, relative as relative2, resolve as resolve2 } from "node:path";
|
|
28559
28682
|
|
|
28560
28683
|
// src/job-log-batcher.ts
|
|
28561
28684
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -28762,7 +28885,7 @@ var JobAbortedError = class extends Error {
|
|
|
28762
28885
|
}
|
|
28763
28886
|
};
|
|
28764
28887
|
function defaultWorkspaceRoot() {
|
|
28765
|
-
return process.env.AGLIO_WORKER_WORKSPACE?.trim() ||
|
|
28888
|
+
return process.env.AGLIO_WORKER_WORKSPACE?.trim() || join3(homedir2(), ".aglio", "workspaces");
|
|
28766
28889
|
}
|
|
28767
28890
|
function safeSegment(value) {
|
|
28768
28891
|
return value.replace(/[^A-Za-z0-9._-]+/g, "-").slice(0, 120) || "job";
|
|
@@ -28782,7 +28905,7 @@ function inside(root, target) {
|
|
|
28782
28905
|
async function executeRemoteJob(job, deps) {
|
|
28783
28906
|
if (deps.signal?.aborted) throw new JobAbortedError();
|
|
28784
28907
|
const root = deps.workspaceRoot ?? defaultWorkspaceRoot();
|
|
28785
|
-
const workspace =
|
|
28908
|
+
const workspace = join3(
|
|
28786
28909
|
root,
|
|
28787
28910
|
safeSegment(job.workspaceId || "workspace"),
|
|
28788
28911
|
safeSegment(job.project?.id || "workspace"),
|
|
@@ -28891,7 +29014,7 @@ ${job.instructions.trim()}
|
|
|
28891
29014
|
for (const step of job.steps) {
|
|
28892
29015
|
if (deps.signal?.aborted) throw new JobAbortedError();
|
|
28893
29016
|
if (leaseAbort.signal.aborted) throw new JobLeaseLostError("Lease was lost before the next step.");
|
|
28894
|
-
const cwd = step.cwd ? inside(workspace,
|
|
29017
|
+
const cwd = step.cwd ? inside(workspace, join3(workspace, step.cwd)) : workspace;
|
|
28895
29018
|
write(`[aglio] step: ${step.name}
|
|
28896
29019
|
$ ${step.command}
|
|
28897
29020
|
`);
|
|
@@ -28950,8 +29073,8 @@ async function finish(deps, job, state, error62) {
|
|
|
28950
29073
|
}
|
|
28951
29074
|
async function checkoutRepos(job, workspace, write, signal) {
|
|
28952
29075
|
for (const repo of job.project?.repos ?? []) {
|
|
28953
|
-
const target = inside(workspace,
|
|
28954
|
-
const hasGit = await access(
|
|
29076
|
+
const target = inside(workspace, join3(workspace, repo.name));
|
|
29077
|
+
const hasGit = await access(join3(target, ".git")).then(
|
|
28955
29078
|
() => true,
|
|
28956
29079
|
() => false
|
|
28957
29080
|
);
|
|
@@ -29033,7 +29156,7 @@ async function uploadJobArtifacts(job, workspace, deps, write) {
|
|
|
29033
29156
|
`);
|
|
29034
29157
|
continue;
|
|
29035
29158
|
}
|
|
29036
|
-
const path = inside(workspace,
|
|
29159
|
+
const path = inside(workspace, join3(workspace, artifact.name));
|
|
29037
29160
|
const info = await stat(path).catch(() => null);
|
|
29038
29161
|
if (!info?.isFile()) {
|
|
29039
29162
|
write(`[aglio] artifact missing: ${artifact.name}
|
|
@@ -29082,7 +29205,7 @@ import { dirname as dirname2 } from "node:path";
|
|
|
29082
29205
|
|
|
29083
29206
|
// src/remote-card-run.ts
|
|
29084
29207
|
import { mkdir as mkdir2, readFile as readFile2, rm as rm2, stat as stat3, writeFile } from "node:fs/promises";
|
|
29085
|
-
import { dirname, isAbsolute as isAbsolute2, join as
|
|
29208
|
+
import { dirname, isAbsolute as isAbsolute2, join as join5, relative as relative3, resolve as resolve3 } from "node:path";
|
|
29086
29209
|
|
|
29087
29210
|
// ../../packages/agents/src/drivers.ts
|
|
29088
29211
|
function withSystemPrompt(prompt, append) {
|
|
@@ -29736,13 +29859,13 @@ function credentialFiles(payload) {
|
|
|
29736
29859
|
|
|
29737
29860
|
// src/remote-cleanup.ts
|
|
29738
29861
|
import { access as access2, readdir, rm, stat as stat2 } from "node:fs/promises";
|
|
29739
|
-
import { join as
|
|
29862
|
+
import { join as join4 } from "node:path";
|
|
29740
29863
|
import Dockerode2 from "dockerode";
|
|
29741
29864
|
var JOB_WORKSPACE_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
29742
29865
|
var STALE_CONTAINER_RETENTION_SECONDS = 24 * 60 * 60;
|
|
29743
29866
|
var SESSION_RESUME_MARKER = "resume-pending";
|
|
29744
29867
|
function sessionResumeMarkerPath(workspaceDir) {
|
|
29745
|
-
return
|
|
29868
|
+
return join4(workspaceDir, ".aglio", SESSION_RESUME_MARKER);
|
|
29746
29869
|
}
|
|
29747
29870
|
async function workspaceHasResumeMarker(workspaceDir) {
|
|
29748
29871
|
return access2(sessionResumeMarkerPath(workspaceDir)).then(
|
|
@@ -29765,16 +29888,16 @@ async function cleanupRemoteWorkspaces(input2) {
|
|
|
29765
29888
|
const rootEntries = await readdir(input2.workspaceRoot, { withFileTypes: true }).catch(() => []);
|
|
29766
29889
|
for (const workspace of rootEntries) {
|
|
29767
29890
|
if (!workspace.isDirectory()) continue;
|
|
29768
|
-
const workspaceDir =
|
|
29891
|
+
const workspaceDir = join4(input2.workspaceRoot, workspace.name);
|
|
29769
29892
|
const projectEntries = await readdir(workspaceDir, { withFileTypes: true }).catch(() => []);
|
|
29770
29893
|
for (const project of projectEntries) {
|
|
29771
29894
|
if (!project.isDirectory()) continue;
|
|
29772
|
-
const projectDir =
|
|
29895
|
+
const projectDir = join4(workspaceDir, project.name);
|
|
29773
29896
|
const jobEntries = await readdir(projectDir, { withFileTypes: true }).catch(() => []);
|
|
29774
29897
|
for (const job of jobEntries) {
|
|
29775
29898
|
if (!job.isDirectory()) continue;
|
|
29776
29899
|
scanned += 1;
|
|
29777
|
-
const jobDir =
|
|
29900
|
+
const jobDir = join4(projectDir, job.name);
|
|
29778
29901
|
const info = await stat2(jobDir).catch(() => null);
|
|
29779
29902
|
if (!info) continue;
|
|
29780
29903
|
if (!shouldRemoveWorkspace({
|
|
@@ -29936,12 +30059,12 @@ async function runRemoteCardJob(deps) {
|
|
|
29936
30059
|
const lease = { jobId: job.id, leaseId: job.lease.id, token: job.lease.token };
|
|
29937
30060
|
let context;
|
|
29938
30061
|
try {
|
|
29939
|
-
const workspaceReady = await stat3(
|
|
30062
|
+
const workspaceReady = await stat3(join5(workspace, ".aglio")).then(
|
|
29940
30063
|
(info) => info.isDirectory(),
|
|
29941
30064
|
() => false
|
|
29942
30065
|
);
|
|
29943
30066
|
context = await deps.api.fetchContext({ ...lease, workspaceRoot: runRoot, workspaceReady });
|
|
29944
|
-
await rm2(
|
|
30067
|
+
await rm2(join5(workspace, ".aglio", SESSION_RESUME_MARKER), { force: true }).catch(() => void 0);
|
|
29945
30068
|
} catch (error62) {
|
|
29946
30069
|
return { state: "failed", error: `Could not load run context: ${errorText2(error62)}` };
|
|
29947
30070
|
}
|
|
@@ -29950,9 +30073,9 @@ async function runRemoteCardJob(deps) {
|
|
|
29950
30073
|
write("[aglio] waiting for a free run slot\n");
|
|
29951
30074
|
return { state: "succeeded", error: null };
|
|
29952
30075
|
}
|
|
29953
|
-
const toolDir =
|
|
29954
|
-
const homeHost =
|
|
29955
|
-
const gitConfigPath =
|
|
30076
|
+
const toolDir = join5(workspace, ".aglio", context.agent.driver);
|
|
30077
|
+
const homeHost = join5(toolDir, "home");
|
|
30078
|
+
const gitConfigPath = join5(workspace, ".aglio", "gitconfig");
|
|
29956
30079
|
const mcpRelativePath = mcpConfigRelativePath(context.agent.driver);
|
|
29957
30080
|
const mcpUrl = deps.mcp ? `${deps.mcp.baseUrl.replace(/\/+$/, "")}/mcp/${context.run.id}` : "";
|
|
29958
30081
|
const extraServers = context.mcp.servers;
|
|
@@ -30000,7 +30123,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30000
30123
|
options: context.agent.options,
|
|
30001
30124
|
sessionId: context.run.sessionId,
|
|
30002
30125
|
resume: Boolean(context.run.sessionId),
|
|
30003
|
-
mcpConfigPath: mcpRelativePath ? containerPath(runRoot, workspace,
|
|
30126
|
+
mcpConfigPath: mcpRelativePath ? containerPath(runRoot, workspace, join5(toolDir, mcpRelativePath)) : "",
|
|
30004
30127
|
piHome: containerPath(runRoot, workspace, toolDir),
|
|
30005
30128
|
workingDirectory: repoPaths.length === 1 ? containerPath(runRoot, workspace, repoPaths[0]) : runRoot,
|
|
30006
30129
|
systemPromptAppend: context.systemPromptAppend
|
|
@@ -30020,14 +30143,14 @@ async function runRemoteCardJob(deps) {
|
|
|
30020
30143
|
deps.mcp?.stopRun(context.run.id);
|
|
30021
30144
|
await rm2(homeHost, { recursive: true, force: true }).catch(() => void 0);
|
|
30022
30145
|
await rm2(gitConfigPath, { force: true }).catch(() => void 0);
|
|
30023
|
-
await rm2(
|
|
30146
|
+
await rm2(join5(toolDir, "models.json"), { force: true }).catch(() => void 0);
|
|
30024
30147
|
if (mcpRelativePath) {
|
|
30025
|
-
await rm2(
|
|
30148
|
+
await rm2(join5(toolDir, mcpRelativePath), { force: true }).catch(() => void 0);
|
|
30026
30149
|
}
|
|
30027
30150
|
}
|
|
30028
30151
|
async function prepareWorkspace() {
|
|
30029
30152
|
await mkdir2(homeHost, { recursive: true, mode: 448 });
|
|
30030
|
-
await mkdir2(
|
|
30153
|
+
await mkdir2(join5(workspace, ".aglio", "attachments"), { recursive: true });
|
|
30031
30154
|
const files = credentialFiles(context.agent.credential.payload);
|
|
30032
30155
|
if (context.agent.credential.authType === "session_bundle" && Object.keys(files).length === 0) {
|
|
30033
30156
|
throw new Error(`Credential "${context.agent.credential.label}" has no session files.`);
|
|
@@ -30048,7 +30171,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30048
30171
|
throw new Error(`Credential "${context.agent.credential.label}" has no base URL.`);
|
|
30049
30172
|
}
|
|
30050
30173
|
await writeFile(
|
|
30051
|
-
|
|
30174
|
+
join5(toolDir, "models.json"),
|
|
30052
30175
|
JSON.stringify(
|
|
30053
30176
|
{
|
|
30054
30177
|
providers: {
|
|
@@ -30067,7 +30190,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30067
30190
|
);
|
|
30068
30191
|
}
|
|
30069
30192
|
if (mcpRelativePath && mcpUrl) {
|
|
30070
|
-
const hostConfigPath =
|
|
30193
|
+
const hostConfigPath = join5(toolDir, mcpRelativePath);
|
|
30071
30194
|
await mkdir2(dirname(hostConfigPath), { recursive: true });
|
|
30072
30195
|
await writeFile(
|
|
30073
30196
|
hostConfigPath,
|
|
@@ -30097,7 +30220,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30097
30220
|
try {
|
|
30098
30221
|
const data = await deps.api.fetchAttachment({ ...lease, artifactId: attachment.id });
|
|
30099
30222
|
await writeFile(
|
|
30100
|
-
|
|
30223
|
+
join5(workspace, ".aglio", "attachments", artifactFileName(attachment.id, attachment.label)),
|
|
30101
30224
|
data
|
|
30102
30225
|
);
|
|
30103
30226
|
} catch {
|
|
@@ -30109,10 +30232,10 @@ async function runRemoteCardJob(deps) {
|
|
|
30109
30232
|
async function cloneRepos() {
|
|
30110
30233
|
const repoPaths = [];
|
|
30111
30234
|
for (const repo of context.repos) {
|
|
30112
|
-
const target = inside2(workspace,
|
|
30235
|
+
const target = inside2(workspace, join5(workspace, repo.name));
|
|
30113
30236
|
const targetRun = containerPath(runRoot, workspace, target);
|
|
30114
30237
|
repoPaths.push(target);
|
|
30115
|
-
const hasGit = await readFile2(
|
|
30238
|
+
const hasGit = await readFile2(join5(target, ".git", "HEAD"), "utf8").then(
|
|
30116
30239
|
() => true,
|
|
30117
30240
|
() => false
|
|
30118
30241
|
);
|
|
@@ -30216,7 +30339,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30216
30339
|
};
|
|
30217
30340
|
}
|
|
30218
30341
|
async function pushBranch(repo, branch) {
|
|
30219
|
-
const target = inside2(workspace,
|
|
30342
|
+
const target = inside2(workspace, join5(workspace, repo.name));
|
|
30220
30343
|
const targetRun = containerPath(runRoot, workspace, target);
|
|
30221
30344
|
write(`[aglio] pushing ${repo.name} ${branch}
|
|
30222
30345
|
`);
|
|
@@ -30477,7 +30600,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30477
30600
|
|
|
30478
30601
|
// src/remote-provider-login.ts
|
|
30479
30602
|
import { mkdir as mkdir3, readFile as readFile3, rm as rm3 } from "node:fs/promises";
|
|
30480
|
-
import { join as
|
|
30603
|
+
import { join as join6, resolve as resolve4, sep } from "node:path";
|
|
30481
30604
|
var FINAL = ["completed", "failed", "cancelled", "expired"];
|
|
30482
30605
|
var POLL_MS = 700;
|
|
30483
30606
|
var KICKOFF_MS = 4e3;
|
|
@@ -30490,8 +30613,8 @@ function errorText3(error62) {
|
|
|
30490
30613
|
async function runRemoteProviderLogin(input2) {
|
|
30491
30614
|
let session = await input2.api.fetch();
|
|
30492
30615
|
const method = session.command;
|
|
30493
|
-
const root =
|
|
30494
|
-
const home =
|
|
30616
|
+
const root = join6(input2.workspace, "login");
|
|
30617
|
+
const home = join6(root, "home");
|
|
30495
30618
|
const target = resolve4(home, session.bundlePath);
|
|
30496
30619
|
if (!target.startsWith(resolve4(home) + sep)) {
|
|
30497
30620
|
return { state: "failed", error: "Unsafe credential bundle path." };
|
|
@@ -31320,7 +31443,7 @@ async function runRemoteWorker(config2, deps = {}) {
|
|
|
31320
31443
|
// src/remote-mcp.ts
|
|
31321
31444
|
import { createServer as createServer2 } from "node:http";
|
|
31322
31445
|
import { mkdir as mkdir5, readFile as readFile5, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
|
|
31323
|
-
import { basename, isAbsolute as isAbsolute3, join as
|
|
31446
|
+
import { basename, isAbsolute as isAbsolute3, join as join7, relative as relative4, resolve as resolve5 } from "node:path";
|
|
31324
31447
|
|
|
31325
31448
|
// ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/util.js
|
|
31326
31449
|
var util;
|
|
@@ -43028,7 +43151,7 @@ function fail(message) {
|
|
|
43028
43151
|
function workspaceHostPath(workspace, input2) {
|
|
43029
43152
|
const root = resolve5(workspace);
|
|
43030
43153
|
const trimmed = input2.trim();
|
|
43031
|
-
const mapped = !trimmed.startsWith("/") ?
|
|
43154
|
+
const mapped = !trimmed.startsWith("/") ? join7(root, trimmed) : trimmed === root || trimmed.startsWith(`${root}/`) ? trimmed : join7(root, relative4("/workspace", trimmed));
|
|
43032
43155
|
const normalized = resolve5(mapped);
|
|
43033
43156
|
const rel = relative4(root, normalized);
|
|
43034
43157
|
if (rel.startsWith("..") || isAbsolute3(rel)) throw new Error("Artifact path escapes the workspace");
|
|
@@ -43138,9 +43261,9 @@ function createServerForRun(run) {
|
|
|
43138
43261
|
try {
|
|
43139
43262
|
const result = await run.callTool({ tool, input: input2 });
|
|
43140
43263
|
if (run.kind !== "planning" && (tool === "question_ask" || tool === "spec_propose") && result && typeof result === "object" && ["awaiting_input", "pending"].includes(String(result.status ?? ""))) {
|
|
43141
|
-
await mkdir5(
|
|
43264
|
+
await mkdir5(join7(run.workspace, ".aglio"), { recursive: true }).catch(() => void 0);
|
|
43142
43265
|
await writeFile3(
|
|
43143
|
-
|
|
43266
|
+
join7(run.workspace, ".aglio", SESSION_RESUME_MARKER),
|
|
43144
43267
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
43145
43268
|
"utf8"
|
|
43146
43269
|
).catch((error62) => run.log(`resume marker write failed: ${String(error62)}`));
|
|
@@ -43420,7 +43543,7 @@ async function main() {
|
|
|
43420
43543
|
const mode = normalizeWorkerMode(process.env.AGLIO_WORKER_MODE?.trim() || "docker");
|
|
43421
43544
|
const os = normalizeWorkerOs(process.env.AGLIO_WORKER_OS?.trim() || defaultOs());
|
|
43422
43545
|
const labels = parseWorkerLabels(process.env.AGLIO_WORKER_LABELS ?? "");
|
|
43423
|
-
const stateFile = process.env.AGLIO_WORKER_STATE_FILE?.trim() ||
|
|
43546
|
+
const stateFile = process.env.AGLIO_WORKER_STATE_FILE?.trim() || join8(homedir3(), ".aglio", "worker.json");
|
|
43424
43547
|
const workspaceRoot = process.env.AGLIO_WORKER_WORKSPACE?.trim() || defaultWorkspaceRoot();
|
|
43425
43548
|
const hostWorkspaceRoot = process.env.AGLIO_WORKER_HOST_WORKSPACE?.trim() || workspaceRoot;
|
|
43426
43549
|
const runnerImage = defaultRunnerImage();
|
|
@@ -43476,6 +43599,13 @@ async function main() {
|
|
|
43476
43599
|
);
|
|
43477
43600
|
const hostTools = await verifyHostRuntime({ mode, labels, platform: process.platform });
|
|
43478
43601
|
if (hostTools.length > 0) console.log(`[worker-agent] host tools ${hostTools.join(" | ")}`);
|
|
43602
|
+
if (mode === "docker") {
|
|
43603
|
+
void prefetchRunnerImage({
|
|
43604
|
+
image: runnerImage,
|
|
43605
|
+
socketPath: dockerSocket,
|
|
43606
|
+
log: (message, extra) => console.log(`[worker-agent] ${message}`, extra ?? "")
|
|
43607
|
+
});
|
|
43608
|
+
}
|
|
43479
43609
|
try {
|
|
43480
43610
|
process.exitCode = await runRemoteWorker(config2, {
|
|
43481
43611
|
signal: controller.signal,
|