aglio-worker 0.9.2 → 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 +188 -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"];
|
|
@@ -28241,11 +28241,117 @@ var boardListMoveSchema = external_exports.object({
|
|
|
28241
28241
|
// ../../packages/shared/src/password-reset.ts
|
|
28242
28242
|
var PASSWORD_RESET_TTL_SECONDS = 60 * 60;
|
|
28243
28243
|
|
|
28244
|
+
// ../../packages/shared/src/card-open-mode.ts
|
|
28245
|
+
var CARD_OPEN_MODES = ["popup", "page"];
|
|
28246
|
+
var cardOpenModeSchema = external_exports.enum(CARD_OPEN_MODES);
|
|
28247
|
+
|
|
28244
28248
|
// src/remote-docker.ts
|
|
28245
28249
|
import { existsSync } from "node:fs";
|
|
28246
28250
|
import { PassThrough } from "node:stream";
|
|
28247
|
-
import { join, posix, relative, resolve } from "node:path";
|
|
28251
|
+
import { join as join2, posix, relative, resolve } from "node:path";
|
|
28248
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
|
|
28249
28355
|
async function runnerCpuCount(client) {
|
|
28250
28356
|
const info = await client.info().catch(() => null);
|
|
28251
28357
|
return Math.max(1, Math.min(2, Math.floor(info?.NCPU ?? 1)));
|
|
@@ -28287,25 +28393,24 @@ function mapDockerWorkspace(input2) {
|
|
|
28287
28393
|
throw new Error(`Path escapes the job workspace: ${input2.target}`);
|
|
28288
28394
|
}
|
|
28289
28395
|
return {
|
|
28290
|
-
hostPath: resolve(
|
|
28396
|
+
hostPath: resolve(join2(input2.hostWorkspaceRoot, rel)),
|
|
28291
28397
|
containerPath: containerRel ? posix.join(CONTAINER_WORKSPACE, containerRel.split(/[\\/]+/).join("/")) : CONTAINER_WORKSPACE
|
|
28292
28398
|
};
|
|
28293
28399
|
}
|
|
28294
28400
|
function sleep(ms) {
|
|
28295
28401
|
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
28296
28402
|
}
|
|
28403
|
+
var defaultClients = /* @__PURE__ */ new Map();
|
|
28297
28404
|
function defaultClient(socketPath) {
|
|
28298
|
-
|
|
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;
|
|
28299
28410
|
}
|
|
28300
28411
|
async function createDockerJobExecutor(input2) {
|
|
28301
28412
|
const client = input2.client ?? defaultClient(input2.socketPath ?? "/var/run/docker.sock");
|
|
28302
|
-
|
|
28303
|
-
await client.getImage(input2.image).inspect();
|
|
28304
|
-
} catch {
|
|
28305
|
-
throw new Error(
|
|
28306
|
-
`Runner image ${input2.image} not found on this machine. Pull it with: docker pull ${input2.image}`
|
|
28307
|
-
);
|
|
28308
|
-
}
|
|
28413
|
+
await ensureDockerImage({ client, image: input2.image, log: input2.log });
|
|
28309
28414
|
const { hostPath } = mapDockerWorkspace({
|
|
28310
28415
|
workspaceRoot: input2.workspaceRoot,
|
|
28311
28416
|
hostWorkspaceRoot: input2.hostWorkspaceRoot,
|
|
@@ -28417,13 +28522,35 @@ async function createDockerJobExecutor(input2) {
|
|
|
28417
28522
|
}
|
|
28418
28523
|
};
|
|
28419
28524
|
}
|
|
28420
|
-
async function
|
|
28421
|
-
const client =
|
|
28525
|
+
async function prefetchRunnerImage(input2) {
|
|
28526
|
+
const client = input2.client ?? defaultClient(input2.socketPath ?? "/var/run/docker.sock");
|
|
28422
28527
|
try {
|
|
28423
|
-
|
|
28424
|
-
|
|
28425
|
-
|
|
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;
|
|
28426
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
|
+
});
|
|
28427
28554
|
const { hostPath } = mapDockerWorkspace({
|
|
28428
28555
|
workspaceRoot: input2.workspaceRoot,
|
|
28429
28556
|
hostWorkspaceRoot: input2.hostWorkspaceRoot,
|
|
@@ -28550,8 +28677,8 @@ async function verifyHostRuntime(input2, run = defaultRunner) {
|
|
|
28550
28677
|
// src/job-runner.ts
|
|
28551
28678
|
import { spawn } from "node:child_process";
|
|
28552
28679
|
import { access, mkdir, readFile, stat } from "node:fs/promises";
|
|
28553
|
-
import { homedir } from "node:os";
|
|
28554
|
-
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";
|
|
28555
28682
|
|
|
28556
28683
|
// src/job-log-batcher.ts
|
|
28557
28684
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -28758,7 +28885,7 @@ var JobAbortedError = class extends Error {
|
|
|
28758
28885
|
}
|
|
28759
28886
|
};
|
|
28760
28887
|
function defaultWorkspaceRoot() {
|
|
28761
|
-
return process.env.AGLIO_WORKER_WORKSPACE?.trim() ||
|
|
28888
|
+
return process.env.AGLIO_WORKER_WORKSPACE?.trim() || join3(homedir2(), ".aglio", "workspaces");
|
|
28762
28889
|
}
|
|
28763
28890
|
function safeSegment(value) {
|
|
28764
28891
|
return value.replace(/[^A-Za-z0-9._-]+/g, "-").slice(0, 120) || "job";
|
|
@@ -28778,7 +28905,7 @@ function inside(root, target) {
|
|
|
28778
28905
|
async function executeRemoteJob(job, deps) {
|
|
28779
28906
|
if (deps.signal?.aborted) throw new JobAbortedError();
|
|
28780
28907
|
const root = deps.workspaceRoot ?? defaultWorkspaceRoot();
|
|
28781
|
-
const workspace =
|
|
28908
|
+
const workspace = join3(
|
|
28782
28909
|
root,
|
|
28783
28910
|
safeSegment(job.workspaceId || "workspace"),
|
|
28784
28911
|
safeSegment(job.project?.id || "workspace"),
|
|
@@ -28887,7 +29014,7 @@ ${job.instructions.trim()}
|
|
|
28887
29014
|
for (const step of job.steps) {
|
|
28888
29015
|
if (deps.signal?.aborted) throw new JobAbortedError();
|
|
28889
29016
|
if (leaseAbort.signal.aborted) throw new JobLeaseLostError("Lease was lost before the next step.");
|
|
28890
|
-
const cwd = step.cwd ? inside(workspace,
|
|
29017
|
+
const cwd = step.cwd ? inside(workspace, join3(workspace, step.cwd)) : workspace;
|
|
28891
29018
|
write(`[aglio] step: ${step.name}
|
|
28892
29019
|
$ ${step.command}
|
|
28893
29020
|
`);
|
|
@@ -28946,8 +29073,8 @@ async function finish(deps, job, state, error62) {
|
|
|
28946
29073
|
}
|
|
28947
29074
|
async function checkoutRepos(job, workspace, write, signal) {
|
|
28948
29075
|
for (const repo of job.project?.repos ?? []) {
|
|
28949
|
-
const target = inside(workspace,
|
|
28950
|
-
const hasGit = await access(
|
|
29076
|
+
const target = inside(workspace, join3(workspace, repo.name));
|
|
29077
|
+
const hasGit = await access(join3(target, ".git")).then(
|
|
28951
29078
|
() => true,
|
|
28952
29079
|
() => false
|
|
28953
29080
|
);
|
|
@@ -29029,7 +29156,7 @@ async function uploadJobArtifacts(job, workspace, deps, write) {
|
|
|
29029
29156
|
`);
|
|
29030
29157
|
continue;
|
|
29031
29158
|
}
|
|
29032
|
-
const path = inside(workspace,
|
|
29159
|
+
const path = inside(workspace, join3(workspace, artifact.name));
|
|
29033
29160
|
const info = await stat(path).catch(() => null);
|
|
29034
29161
|
if (!info?.isFile()) {
|
|
29035
29162
|
write(`[aglio] artifact missing: ${artifact.name}
|
|
@@ -29078,7 +29205,7 @@ import { dirname as dirname2 } from "node:path";
|
|
|
29078
29205
|
|
|
29079
29206
|
// src/remote-card-run.ts
|
|
29080
29207
|
import { mkdir as mkdir2, readFile as readFile2, rm as rm2, stat as stat3, writeFile } from "node:fs/promises";
|
|
29081
|
-
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";
|
|
29082
29209
|
|
|
29083
29210
|
// ../../packages/agents/src/drivers.ts
|
|
29084
29211
|
function withSystemPrompt(prompt, append) {
|
|
@@ -29732,13 +29859,13 @@ function credentialFiles(payload) {
|
|
|
29732
29859
|
|
|
29733
29860
|
// src/remote-cleanup.ts
|
|
29734
29861
|
import { access as access2, readdir, rm, stat as stat2 } from "node:fs/promises";
|
|
29735
|
-
import { join as
|
|
29862
|
+
import { join as join4 } from "node:path";
|
|
29736
29863
|
import Dockerode2 from "dockerode";
|
|
29737
29864
|
var JOB_WORKSPACE_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
29738
29865
|
var STALE_CONTAINER_RETENTION_SECONDS = 24 * 60 * 60;
|
|
29739
29866
|
var SESSION_RESUME_MARKER = "resume-pending";
|
|
29740
29867
|
function sessionResumeMarkerPath(workspaceDir) {
|
|
29741
|
-
return
|
|
29868
|
+
return join4(workspaceDir, ".aglio", SESSION_RESUME_MARKER);
|
|
29742
29869
|
}
|
|
29743
29870
|
async function workspaceHasResumeMarker(workspaceDir) {
|
|
29744
29871
|
return access2(sessionResumeMarkerPath(workspaceDir)).then(
|
|
@@ -29761,16 +29888,16 @@ async function cleanupRemoteWorkspaces(input2) {
|
|
|
29761
29888
|
const rootEntries = await readdir(input2.workspaceRoot, { withFileTypes: true }).catch(() => []);
|
|
29762
29889
|
for (const workspace of rootEntries) {
|
|
29763
29890
|
if (!workspace.isDirectory()) continue;
|
|
29764
|
-
const workspaceDir =
|
|
29891
|
+
const workspaceDir = join4(input2.workspaceRoot, workspace.name);
|
|
29765
29892
|
const projectEntries = await readdir(workspaceDir, { withFileTypes: true }).catch(() => []);
|
|
29766
29893
|
for (const project of projectEntries) {
|
|
29767
29894
|
if (!project.isDirectory()) continue;
|
|
29768
|
-
const projectDir =
|
|
29895
|
+
const projectDir = join4(workspaceDir, project.name);
|
|
29769
29896
|
const jobEntries = await readdir(projectDir, { withFileTypes: true }).catch(() => []);
|
|
29770
29897
|
for (const job of jobEntries) {
|
|
29771
29898
|
if (!job.isDirectory()) continue;
|
|
29772
29899
|
scanned += 1;
|
|
29773
|
-
const jobDir =
|
|
29900
|
+
const jobDir = join4(projectDir, job.name);
|
|
29774
29901
|
const info = await stat2(jobDir).catch(() => null);
|
|
29775
29902
|
if (!info) continue;
|
|
29776
29903
|
if (!shouldRemoveWorkspace({
|
|
@@ -29932,12 +30059,12 @@ async function runRemoteCardJob(deps) {
|
|
|
29932
30059
|
const lease = { jobId: job.id, leaseId: job.lease.id, token: job.lease.token };
|
|
29933
30060
|
let context;
|
|
29934
30061
|
try {
|
|
29935
|
-
const workspaceReady = await stat3(
|
|
30062
|
+
const workspaceReady = await stat3(join5(workspace, ".aglio")).then(
|
|
29936
30063
|
(info) => info.isDirectory(),
|
|
29937
30064
|
() => false
|
|
29938
30065
|
);
|
|
29939
30066
|
context = await deps.api.fetchContext({ ...lease, workspaceRoot: runRoot, workspaceReady });
|
|
29940
|
-
await rm2(
|
|
30067
|
+
await rm2(join5(workspace, ".aglio", SESSION_RESUME_MARKER), { force: true }).catch(() => void 0);
|
|
29941
30068
|
} catch (error62) {
|
|
29942
30069
|
return { state: "failed", error: `Could not load run context: ${errorText2(error62)}` };
|
|
29943
30070
|
}
|
|
@@ -29946,9 +30073,9 @@ async function runRemoteCardJob(deps) {
|
|
|
29946
30073
|
write("[aglio] waiting for a free run slot\n");
|
|
29947
30074
|
return { state: "succeeded", error: null };
|
|
29948
30075
|
}
|
|
29949
|
-
const toolDir =
|
|
29950
|
-
const homeHost =
|
|
29951
|
-
const gitConfigPath =
|
|
30076
|
+
const toolDir = join5(workspace, ".aglio", context.agent.driver);
|
|
30077
|
+
const homeHost = join5(toolDir, "home");
|
|
30078
|
+
const gitConfigPath = join5(workspace, ".aglio", "gitconfig");
|
|
29952
30079
|
const mcpRelativePath = mcpConfigRelativePath(context.agent.driver);
|
|
29953
30080
|
const mcpUrl = deps.mcp ? `${deps.mcp.baseUrl.replace(/\/+$/, "")}/mcp/${context.run.id}` : "";
|
|
29954
30081
|
const extraServers = context.mcp.servers;
|
|
@@ -29996,7 +30123,7 @@ async function runRemoteCardJob(deps) {
|
|
|
29996
30123
|
options: context.agent.options,
|
|
29997
30124
|
sessionId: context.run.sessionId,
|
|
29998
30125
|
resume: Boolean(context.run.sessionId),
|
|
29999
|
-
mcpConfigPath: mcpRelativePath ? containerPath(runRoot, workspace,
|
|
30126
|
+
mcpConfigPath: mcpRelativePath ? containerPath(runRoot, workspace, join5(toolDir, mcpRelativePath)) : "",
|
|
30000
30127
|
piHome: containerPath(runRoot, workspace, toolDir),
|
|
30001
30128
|
workingDirectory: repoPaths.length === 1 ? containerPath(runRoot, workspace, repoPaths[0]) : runRoot,
|
|
30002
30129
|
systemPromptAppend: context.systemPromptAppend
|
|
@@ -30016,14 +30143,14 @@ async function runRemoteCardJob(deps) {
|
|
|
30016
30143
|
deps.mcp?.stopRun(context.run.id);
|
|
30017
30144
|
await rm2(homeHost, { recursive: true, force: true }).catch(() => void 0);
|
|
30018
30145
|
await rm2(gitConfigPath, { force: true }).catch(() => void 0);
|
|
30019
|
-
await rm2(
|
|
30146
|
+
await rm2(join5(toolDir, "models.json"), { force: true }).catch(() => void 0);
|
|
30020
30147
|
if (mcpRelativePath) {
|
|
30021
|
-
await rm2(
|
|
30148
|
+
await rm2(join5(toolDir, mcpRelativePath), { force: true }).catch(() => void 0);
|
|
30022
30149
|
}
|
|
30023
30150
|
}
|
|
30024
30151
|
async function prepareWorkspace() {
|
|
30025
30152
|
await mkdir2(homeHost, { recursive: true, mode: 448 });
|
|
30026
|
-
await mkdir2(
|
|
30153
|
+
await mkdir2(join5(workspace, ".aglio", "attachments"), { recursive: true });
|
|
30027
30154
|
const files = credentialFiles(context.agent.credential.payload);
|
|
30028
30155
|
if (context.agent.credential.authType === "session_bundle" && Object.keys(files).length === 0) {
|
|
30029
30156
|
throw new Error(`Credential "${context.agent.credential.label}" has no session files.`);
|
|
@@ -30044,7 +30171,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30044
30171
|
throw new Error(`Credential "${context.agent.credential.label}" has no base URL.`);
|
|
30045
30172
|
}
|
|
30046
30173
|
await writeFile(
|
|
30047
|
-
|
|
30174
|
+
join5(toolDir, "models.json"),
|
|
30048
30175
|
JSON.stringify(
|
|
30049
30176
|
{
|
|
30050
30177
|
providers: {
|
|
@@ -30063,7 +30190,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30063
30190
|
);
|
|
30064
30191
|
}
|
|
30065
30192
|
if (mcpRelativePath && mcpUrl) {
|
|
30066
|
-
const hostConfigPath =
|
|
30193
|
+
const hostConfigPath = join5(toolDir, mcpRelativePath);
|
|
30067
30194
|
await mkdir2(dirname(hostConfigPath), { recursive: true });
|
|
30068
30195
|
await writeFile(
|
|
30069
30196
|
hostConfigPath,
|
|
@@ -30093,7 +30220,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30093
30220
|
try {
|
|
30094
30221
|
const data = await deps.api.fetchAttachment({ ...lease, artifactId: attachment.id });
|
|
30095
30222
|
await writeFile(
|
|
30096
|
-
|
|
30223
|
+
join5(workspace, ".aglio", "attachments", artifactFileName(attachment.id, attachment.label)),
|
|
30097
30224
|
data
|
|
30098
30225
|
);
|
|
30099
30226
|
} catch {
|
|
@@ -30105,10 +30232,10 @@ async function runRemoteCardJob(deps) {
|
|
|
30105
30232
|
async function cloneRepos() {
|
|
30106
30233
|
const repoPaths = [];
|
|
30107
30234
|
for (const repo of context.repos) {
|
|
30108
|
-
const target = inside2(workspace,
|
|
30235
|
+
const target = inside2(workspace, join5(workspace, repo.name));
|
|
30109
30236
|
const targetRun = containerPath(runRoot, workspace, target);
|
|
30110
30237
|
repoPaths.push(target);
|
|
30111
|
-
const hasGit = await readFile2(
|
|
30238
|
+
const hasGit = await readFile2(join5(target, ".git", "HEAD"), "utf8").then(
|
|
30112
30239
|
() => true,
|
|
30113
30240
|
() => false
|
|
30114
30241
|
);
|
|
@@ -30212,7 +30339,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30212
30339
|
};
|
|
30213
30340
|
}
|
|
30214
30341
|
async function pushBranch(repo, branch) {
|
|
30215
|
-
const target = inside2(workspace,
|
|
30342
|
+
const target = inside2(workspace, join5(workspace, repo.name));
|
|
30216
30343
|
const targetRun = containerPath(runRoot, workspace, target);
|
|
30217
30344
|
write(`[aglio] pushing ${repo.name} ${branch}
|
|
30218
30345
|
`);
|
|
@@ -30473,7 +30600,7 @@ async function runRemoteCardJob(deps) {
|
|
|
30473
30600
|
|
|
30474
30601
|
// src/remote-provider-login.ts
|
|
30475
30602
|
import { mkdir as mkdir3, readFile as readFile3, rm as rm3 } from "node:fs/promises";
|
|
30476
|
-
import { join as
|
|
30603
|
+
import { join as join6, resolve as resolve4, sep } from "node:path";
|
|
30477
30604
|
var FINAL = ["completed", "failed", "cancelled", "expired"];
|
|
30478
30605
|
var POLL_MS = 700;
|
|
30479
30606
|
var KICKOFF_MS = 4e3;
|
|
@@ -30486,8 +30613,8 @@ function errorText3(error62) {
|
|
|
30486
30613
|
async function runRemoteProviderLogin(input2) {
|
|
30487
30614
|
let session = await input2.api.fetch();
|
|
30488
30615
|
const method = session.command;
|
|
30489
|
-
const root =
|
|
30490
|
-
const home =
|
|
30616
|
+
const root = join6(input2.workspace, "login");
|
|
30617
|
+
const home = join6(root, "home");
|
|
30491
30618
|
const target = resolve4(home, session.bundlePath);
|
|
30492
30619
|
if (!target.startsWith(resolve4(home) + sep)) {
|
|
30493
30620
|
return { state: "failed", error: "Unsafe credential bundle path." };
|
|
@@ -31316,7 +31443,7 @@ async function runRemoteWorker(config2, deps = {}) {
|
|
|
31316
31443
|
// src/remote-mcp.ts
|
|
31317
31444
|
import { createServer as createServer2 } from "node:http";
|
|
31318
31445
|
import { mkdir as mkdir5, readFile as readFile5, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
|
|
31319
|
-
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";
|
|
31320
31447
|
|
|
31321
31448
|
// ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/util.js
|
|
31322
31449
|
var util;
|
|
@@ -43024,7 +43151,7 @@ function fail(message) {
|
|
|
43024
43151
|
function workspaceHostPath(workspace, input2) {
|
|
43025
43152
|
const root = resolve5(workspace);
|
|
43026
43153
|
const trimmed = input2.trim();
|
|
43027
|
-
const mapped = !trimmed.startsWith("/") ?
|
|
43154
|
+
const mapped = !trimmed.startsWith("/") ? join7(root, trimmed) : trimmed === root || trimmed.startsWith(`${root}/`) ? trimmed : join7(root, relative4("/workspace", trimmed));
|
|
43028
43155
|
const normalized = resolve5(mapped);
|
|
43029
43156
|
const rel = relative4(root, normalized);
|
|
43030
43157
|
if (rel.startsWith("..") || isAbsolute3(rel)) throw new Error("Artifact path escapes the workspace");
|
|
@@ -43134,9 +43261,9 @@ function createServerForRun(run) {
|
|
|
43134
43261
|
try {
|
|
43135
43262
|
const result = await run.callTool({ tool, input: input2 });
|
|
43136
43263
|
if (run.kind !== "planning" && (tool === "question_ask" || tool === "spec_propose") && result && typeof result === "object" && ["awaiting_input", "pending"].includes(String(result.status ?? ""))) {
|
|
43137
|
-
await mkdir5(
|
|
43264
|
+
await mkdir5(join7(run.workspace, ".aglio"), { recursive: true }).catch(() => void 0);
|
|
43138
43265
|
await writeFile3(
|
|
43139
|
-
|
|
43266
|
+
join7(run.workspace, ".aglio", SESSION_RESUME_MARKER),
|
|
43140
43267
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
43141
43268
|
"utf8"
|
|
43142
43269
|
).catch((error62) => run.log(`resume marker write failed: ${String(error62)}`));
|
|
@@ -43416,7 +43543,7 @@ async function main() {
|
|
|
43416
43543
|
const mode = normalizeWorkerMode(process.env.AGLIO_WORKER_MODE?.trim() || "docker");
|
|
43417
43544
|
const os = normalizeWorkerOs(process.env.AGLIO_WORKER_OS?.trim() || defaultOs());
|
|
43418
43545
|
const labels = parseWorkerLabels(process.env.AGLIO_WORKER_LABELS ?? "");
|
|
43419
|
-
const stateFile = process.env.AGLIO_WORKER_STATE_FILE?.trim() ||
|
|
43546
|
+
const stateFile = process.env.AGLIO_WORKER_STATE_FILE?.trim() || join8(homedir3(), ".aglio", "worker.json");
|
|
43420
43547
|
const workspaceRoot = process.env.AGLIO_WORKER_WORKSPACE?.trim() || defaultWorkspaceRoot();
|
|
43421
43548
|
const hostWorkspaceRoot = process.env.AGLIO_WORKER_HOST_WORKSPACE?.trim() || workspaceRoot;
|
|
43422
43549
|
const runnerImage = defaultRunnerImage();
|
|
@@ -43472,6 +43599,13 @@ async function main() {
|
|
|
43472
43599
|
);
|
|
43473
43600
|
const hostTools = await verifyHostRuntime({ mode, labels, platform: process.platform });
|
|
43474
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
|
+
}
|
|
43475
43609
|
try {
|
|
43476
43610
|
process.exitCode = await runRemoteWorker(config2, {
|
|
43477
43611
|
signal: controller.signal,
|