@elizaos/plugin-agent-orchestrator 0.4.2 → 0.4.3
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/dist/actions/coding-task-helpers.d.ts +8 -2
- package/dist/actions/coding-task-helpers.d.ts.map +1 -1
- package/dist/index.js +146 -46
- package/dist/index.js.map +9 -8
- package/dist/services/config-env.d.ts +11 -0
- package/dist/services/config-env.d.ts.map +1 -0
- package/dist/services/swarm-coordinator.d.ts +8 -0
- package/dist/services/swarm-coordinator.d.ts.map +1 -1
- package/dist/services/workspace-lifecycle.d.ts +1 -1
- package/dist/services/workspace-lifecycle.d.ts.map +1 -1
- package/dist/services/workspace-service.d.ts +10 -1
- package/dist/services/workspace-service.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -9,8 +9,14 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { type HandlerCallback, type IAgentRuntime } from "@elizaos/core";
|
|
11
11
|
import type { PTYService } from "../services/pty-service.js";
|
|
12
|
-
/**
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Create a scratch sandbox directory for non-repo tasks.
|
|
14
|
+
*
|
|
15
|
+
* When `PARALLAX_CODING_DIRECTORY` is set (e.g. `~/Projects`), creates a
|
|
16
|
+
* named subdir like `~/Projects/todo-app/` derived from the task label.
|
|
17
|
+
* Otherwise falls back to `~/.milady/workspaces/{uuid}`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createScratchDir(runtime?: IAgentRuntime, label?: string): string;
|
|
14
20
|
/**
|
|
15
21
|
* Generate a short semantic label from repo URL and/or task description.
|
|
16
22
|
* e.g. "git-workspace-service-testbed/hello-mima" or "scratch/react-research"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coding-task-helpers.d.ts","sourceRoot":"","sources":["../../src/actions/coding-task-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"coding-task-helpers.d.ts","sourceRoot":"","sources":["../../src/actions/coding-task-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AA+B7D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,CAAC,EAAE,aAAa,EACvB,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CA0BR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,MAAM,CA4BR;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,QAAQ,CAAC,EAAE,eAAe,EAC1B,iBAAiB,UAAQ,GACxB,IAAI,CAsEN"}
|
package/dist/index.js
CHANGED
|
@@ -3600,9 +3600,30 @@ class SwarmCoordinator {
|
|
|
3600
3600
|
constructor(runtime) {
|
|
3601
3601
|
this.runtime = runtime;
|
|
3602
3602
|
}
|
|
3603
|
+
scratchDecisionWired = false;
|
|
3603
3604
|
setChatCallback(cb) {
|
|
3604
3605
|
this.chatCallback = cb;
|
|
3605
3606
|
this.log("Chat callback wired");
|
|
3607
|
+
this.wireScratchDecisionCallback();
|
|
3608
|
+
}
|
|
3609
|
+
wireScratchDecisionCallback() {
|
|
3610
|
+
if (this.scratchDecisionWired || !this.chatCallback)
|
|
3611
|
+
return;
|
|
3612
|
+
const wsService = this.runtime.getService("CODING_WORKSPACE_SERVICE");
|
|
3613
|
+
if (wsService?.setScratchDecisionCallback) {
|
|
3614
|
+
const chatCb = this.chatCallback;
|
|
3615
|
+
wsService.setScratchDecisionCallback(async (record) => {
|
|
3616
|
+
const ttlNote = record.expiresAt ? (() => {
|
|
3617
|
+
const remainMs = record.expiresAt - Date.now();
|
|
3618
|
+
const hours = Math.round(remainMs / (60 * 60 * 1000));
|
|
3619
|
+
return hours >= 1 ? `It will be automatically cleaned up in ~${hours} hour${hours === 1 ? "" : "s"}.` : `It will be automatically cleaned up shortly.`;
|
|
3620
|
+
})() : "It will be automatically cleaned up after the configured retention period.";
|
|
3621
|
+
await chatCb(`Task "${record.label}" finished. Code is at \`${record.path}\`.
|
|
3622
|
+
` + `${ttlNote} To keep it, say "keep the workspace" or manage it in Settings → Coding Agents.`, "coding-agent");
|
|
3623
|
+
});
|
|
3624
|
+
this.scratchDecisionWired = true;
|
|
3625
|
+
this.log("Scratch decision callback wired");
|
|
3626
|
+
}
|
|
3606
3627
|
}
|
|
3607
3628
|
setWsBroadcast(cb) {
|
|
3608
3629
|
this.wsBroadcast = cb;
|
|
@@ -3911,6 +3932,9 @@ class SwarmCoordinator {
|
|
|
3911
3932
|
} catch {}
|
|
3912
3933
|
}
|
|
3913
3934
|
async handleSessionEvent(sessionId, event, data) {
|
|
3935
|
+
if (!this.scratchDecisionWired) {
|
|
3936
|
+
this.wireScratchDecisionCallback();
|
|
3937
|
+
}
|
|
3914
3938
|
const tsMatch = sessionId.match(/^pty-(\d+)-/);
|
|
3915
3939
|
if (tsMatch) {
|
|
3916
3940
|
const sessionCreatedAt = Number(tsMatch[1]);
|
|
@@ -5284,15 +5308,55 @@ function formatAge(timestamp) {
|
|
|
5284
5308
|
// src/actions/coding-task-helpers.ts
|
|
5285
5309
|
import { randomUUID } from "node:crypto";
|
|
5286
5310
|
import * as fs2 from "node:fs";
|
|
5287
|
-
import * as
|
|
5288
|
-
import * as
|
|
5311
|
+
import * as os4 from "node:os";
|
|
5312
|
+
import * as path5 from "node:path";
|
|
5289
5313
|
import {
|
|
5290
5314
|
logger as logger5
|
|
5291
5315
|
} from "@elizaos/core";
|
|
5292
|
-
|
|
5293
|
-
|
|
5316
|
+
|
|
5317
|
+
// src/services/config-env.ts
|
|
5318
|
+
import { readFileSync } from "node:fs";
|
|
5319
|
+
import * as os3 from "node:os";
|
|
5320
|
+
import * as path4 from "node:path";
|
|
5321
|
+
function readConfigEnvKey(key) {
|
|
5322
|
+
try {
|
|
5323
|
+
const configPath = path4.join(process.env.MILADY_STATE_DIR ?? process.env.ELIZA_STATE_DIR ?? path4.join(os3.homedir(), ".milady"), process.env.ELIZA_NAMESPACE === "milady" || !process.env.ELIZA_NAMESPACE ? "milady.json" : `${process.env.ELIZA_NAMESPACE}.json`);
|
|
5324
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
5325
|
+
const config = JSON.parse(raw);
|
|
5326
|
+
const val = config?.env?.[key];
|
|
5327
|
+
return typeof val === "string" ? val : undefined;
|
|
5328
|
+
} catch {
|
|
5329
|
+
return;
|
|
5330
|
+
}
|
|
5331
|
+
}
|
|
5332
|
+
|
|
5333
|
+
// src/actions/coding-task-helpers.ts
|
|
5334
|
+
function sanitizeDirName(label) {
|
|
5335
|
+
return label.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "").slice(0, 60) || "scratch";
|
|
5336
|
+
}
|
|
5337
|
+
function resolveNonColliding(baseDir, name) {
|
|
5338
|
+
let candidate = path5.join(baseDir, name);
|
|
5339
|
+
if (!fs2.existsSync(candidate))
|
|
5340
|
+
return candidate;
|
|
5341
|
+
for (let i = 2;i < 100; i++) {
|
|
5342
|
+
candidate = path5.join(baseDir, `${name}-${i}`);
|
|
5343
|
+
if (!fs2.existsSync(candidate))
|
|
5344
|
+
return candidate;
|
|
5345
|
+
}
|
|
5346
|
+
return path5.join(baseDir, `${name}-${randomUUID().slice(0, 8)}`);
|
|
5347
|
+
}
|
|
5348
|
+
function createScratchDir(runtime, label) {
|
|
5349
|
+
const codingDir = runtime?.getSetting("PARALLAX_CODING_DIRECTORY") ?? readConfigEnvKey("PARALLAX_CODING_DIRECTORY") ?? process.env.PARALLAX_CODING_DIRECTORY;
|
|
5350
|
+
if (codingDir?.trim()) {
|
|
5351
|
+
const resolved = codingDir.startsWith("~") ? path5.join(os4.homedir(), codingDir.slice(1)) : path5.resolve(codingDir);
|
|
5352
|
+
const dirName = label ? sanitizeDirName(label) : `scratch-${randomUUID().slice(0, 8)}`;
|
|
5353
|
+
const scratchDir2 = resolveNonColliding(resolved, dirName);
|
|
5354
|
+
fs2.mkdirSync(scratchDir2, { recursive: true });
|
|
5355
|
+
return scratchDir2;
|
|
5356
|
+
}
|
|
5357
|
+
const baseDir = path5.join(os4.homedir(), ".milady", "workspaces");
|
|
5294
5358
|
const scratchId = randomUUID();
|
|
5295
|
-
const scratchDir =
|
|
5359
|
+
const scratchDir = path5.join(baseDir, scratchId);
|
|
5296
5360
|
fs2.mkdirSync(scratchDir, { recursive: true });
|
|
5297
5361
|
return scratchDir;
|
|
5298
5362
|
}
|
|
@@ -5312,6 +5376,7 @@ function generateLabel(repo, task) {
|
|
|
5312
5376
|
return parts.join("/");
|
|
5313
5377
|
}
|
|
5314
5378
|
function registerSessionEvents(ptyService, runtime, sessionId, label, scratchDir, callback, coordinatorActive = false) {
|
|
5379
|
+
let scratchRegistered = false;
|
|
5315
5380
|
ptyService.onSessionEvent((sid, event, data) => {
|
|
5316
5381
|
if (sid !== sessionId)
|
|
5317
5382
|
return;
|
|
@@ -5341,10 +5406,15 @@ ${preview}` : `Agent "${label}" completed the task.`
|
|
|
5341
5406
|
});
|
|
5342
5407
|
}
|
|
5343
5408
|
}
|
|
5344
|
-
if ((event === "stopped" || event === "task_complete" || event === "error") && scratchDir) {
|
|
5409
|
+
if ((event === "stopped" || event === "task_complete" || event === "error") && scratchDir && !scratchRegistered) {
|
|
5410
|
+
logger5.info(`[scratch-lifecycle] Terminal event "${event}" for "${label}" — registering scratch workspace at ${scratchDir}`);
|
|
5345
5411
|
const wsService = runtime.getService("CODING_WORKSPACE_SERVICE");
|
|
5346
|
-
if (wsService) {
|
|
5347
|
-
|
|
5412
|
+
if (!wsService) {
|
|
5413
|
+
logger5.warn(`[scratch-lifecycle] CODING_WORKSPACE_SERVICE not found — cannot register scratch workspace`);
|
|
5414
|
+
} else {
|
|
5415
|
+
wsService.registerScratchWorkspace(sessionId, scratchDir, label, event).then(() => {
|
|
5416
|
+
scratchRegistered = true;
|
|
5417
|
+
}).catch((err) => {
|
|
5348
5418
|
logger5.warn(`[START_CODING_TASK] Failed to register scratch workspace for "${label}": ${err}`);
|
|
5349
5419
|
});
|
|
5350
5420
|
}
|
|
@@ -5523,7 +5593,7 @@ async function handleMultiAgent(ctx, agentsParam) {
|
|
|
5523
5593
|
branch = workspace.branch;
|
|
5524
5594
|
wsService.setLabel(workspace.id, specLabel);
|
|
5525
5595
|
} else {
|
|
5526
|
-
workdir = createScratchDir();
|
|
5596
|
+
workdir = createScratchDir(runtime, specLabel);
|
|
5527
5597
|
}
|
|
5528
5598
|
if (specAgentType !== "shell" && specAgentType !== "pi") {
|
|
5529
5599
|
const [preflight] = await ptyService.checkAvailableAgents([
|
|
@@ -6191,7 +6261,7 @@ var activeWorkspaceContextProvider = {
|
|
|
6191
6261
|
try {
|
|
6192
6262
|
sessions = await Promise.race([
|
|
6193
6263
|
ptyService.listSessions(),
|
|
6194
|
-
new Promise((
|
|
6264
|
+
new Promise((resolve4) => setTimeout(() => resolve4([]), 2000))
|
|
6195
6265
|
]);
|
|
6196
6266
|
} catch {
|
|
6197
6267
|
sessions = [];
|
|
@@ -6275,8 +6345,8 @@ var activeWorkspaceContextProvider = {
|
|
|
6275
6345
|
};
|
|
6276
6346
|
|
|
6277
6347
|
// src/services/workspace-service.ts
|
|
6278
|
-
import * as
|
|
6279
|
-
import * as
|
|
6348
|
+
import * as os6 from "node:os";
|
|
6349
|
+
import * as path7 from "node:path";
|
|
6280
6350
|
import * as fs4 from "node:fs/promises";
|
|
6281
6351
|
import {
|
|
6282
6352
|
CredentialService,
|
|
@@ -6491,12 +6561,18 @@ async function createPR(workspaceService, workspace, workspaceId, options, log)
|
|
|
6491
6561
|
|
|
6492
6562
|
// src/services/workspace-lifecycle.ts
|
|
6493
6563
|
import * as fs3 from "node:fs";
|
|
6494
|
-
import * as
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
const
|
|
6498
|
-
|
|
6499
|
-
|
|
6564
|
+
import * as os5 from "node:os";
|
|
6565
|
+
import * as path6 from "node:path";
|
|
6566
|
+
async function removeScratchDir(dirPath, baseDir, log, allowedDirs) {
|
|
6567
|
+
const resolved = path6.resolve(dirPath);
|
|
6568
|
+
const expandTilde = (p) => p.startsWith("~") ? path6.join(os5.homedir(), p.slice(1)) : p;
|
|
6569
|
+
const allAllowed = [baseDir, ...allowedDirs ?? []];
|
|
6570
|
+
const isAllowed = allAllowed.some((dir) => {
|
|
6571
|
+
const resolvedDir = path6.resolve(expandTilde(dir)) + path6.sep;
|
|
6572
|
+
return resolved.startsWith(resolvedDir) || resolved === path6.resolve(expandTilde(dir));
|
|
6573
|
+
});
|
|
6574
|
+
if (!isAllowed) {
|
|
6575
|
+
console.warn(`[CodingWorkspaceService] Refusing to remove dir outside allowed paths: ${resolved}`);
|
|
6500
6576
|
return;
|
|
6501
6577
|
}
|
|
6502
6578
|
try {
|
|
@@ -6527,7 +6603,7 @@ async function gcOrphanedWorkspaces(baseDir, workspaceTtlMs, trackedWorkspaceIds
|
|
|
6527
6603
|
skipped++;
|
|
6528
6604
|
continue;
|
|
6529
6605
|
}
|
|
6530
|
-
const dirPath =
|
|
6606
|
+
const dirPath = path6.join(baseDir, entry.name);
|
|
6531
6607
|
try {
|
|
6532
6608
|
const stat2 = await fs3.promises.stat(dirPath);
|
|
6533
6609
|
const age = now - stat2.mtimeMs;
|
|
@@ -6563,10 +6639,11 @@ class CodingWorkspaceService {
|
|
|
6563
6639
|
scratchCleanupTimers = new Map;
|
|
6564
6640
|
eventCallbacks = [];
|
|
6565
6641
|
authPromptCallback = null;
|
|
6642
|
+
scratchDecisionCallback = null;
|
|
6566
6643
|
constructor(runtime, config = {}) {
|
|
6567
6644
|
this.runtime = runtime;
|
|
6568
6645
|
this.serviceConfig = {
|
|
6569
|
-
baseDir: config.baseDir ??
|
|
6646
|
+
baseDir: config.baseDir ?? path7.join(os6.homedir(), ".milady", "workspaces"),
|
|
6570
6647
|
branchPrefix: config.branchPrefix ?? "milady",
|
|
6571
6648
|
debug: config.debug ?? false,
|
|
6572
6649
|
workspaceTtlMs: config.workspaceTtlMs ?? 24 * 60 * 60 * 1000
|
|
@@ -6761,6 +6838,9 @@ class CodingWorkspaceService {
|
|
|
6761
6838
|
setAuthPromptCallback(callback) {
|
|
6762
6839
|
this.authPromptCallback = callback;
|
|
6763
6840
|
}
|
|
6841
|
+
setScratchDecisionCallback(callback) {
|
|
6842
|
+
this.scratchDecisionCallback = callback;
|
|
6843
|
+
}
|
|
6764
6844
|
async createIssue(repo, options) {
|
|
6765
6845
|
return createIssue(this.getGitHubContext(), repo, options);
|
|
6766
6846
|
}
|
|
@@ -6819,7 +6899,10 @@ class CodingWorkspaceService {
|
|
|
6819
6899
|
}
|
|
6820
6900
|
}
|
|
6821
6901
|
async removeScratchDir(dirPath) {
|
|
6822
|
-
|
|
6902
|
+
const rawCodingDir = this.runtime.getSetting("PARALLAX_CODING_DIRECTORY") ?? this.readConfigEnvKey("PARALLAX_CODING_DIRECTORY") ?? process.env.PARALLAX_CODING_DIRECTORY;
|
|
6903
|
+
const codingDir = rawCodingDir?.trim() ? rawCodingDir.trim().startsWith("~") ? path7.join(os6.homedir(), rawCodingDir.trim().slice(1)) : path7.resolve(rawCodingDir.trim()) : undefined;
|
|
6904
|
+
const allowedDirs = codingDir ? [codingDir] : undefined;
|
|
6905
|
+
return removeScratchDir(dirPath, this.serviceConfig.baseDir, (msg) => this.log(msg), allowedDirs);
|
|
6823
6906
|
}
|
|
6824
6907
|
listScratchWorkspaces() {
|
|
6825
6908
|
return Array.from(this.scratchBySession.values()).sort((a, b) => b.terminalAt - a.terminalAt);
|
|
@@ -6837,6 +6920,7 @@ class CodingWorkspaceService {
|
|
|
6837
6920
|
status: "pending_decision"
|
|
6838
6921
|
};
|
|
6839
6922
|
const policy = this.getScratchRetentionPolicy();
|
|
6923
|
+
this.log(`Scratch retention policy: "${policy}" for "${label}"`);
|
|
6840
6924
|
if (policy === "ephemeral") {
|
|
6841
6925
|
await this.removeScratchDir(dirPath);
|
|
6842
6926
|
this.scratchBySession.delete(sessionId);
|
|
@@ -6857,6 +6941,14 @@ class CodingWorkspaceService {
|
|
|
6857
6941
|
const ttlMs = this.getScratchDecisionTtlMs();
|
|
6858
6942
|
record.expiresAt = now + ttlMs;
|
|
6859
6943
|
this.scheduleScratchCleanup(sessionId, ttlMs);
|
|
6944
|
+
if (this.scratchDecisionCallback) {
|
|
6945
|
+
this.log(`Firing scratch decision prompt for "${label}" at ${dirPath}`);
|
|
6946
|
+
this.scratchDecisionCallback(record).catch((err) => {
|
|
6947
|
+
console.warn(`[workspace] Failed to send scratch decision prompt: ${err}`);
|
|
6948
|
+
});
|
|
6949
|
+
} else {
|
|
6950
|
+
this.log(`No scratch decision callback wired — skipping prompt for "${label}"`);
|
|
6951
|
+
}
|
|
6860
6952
|
} else {
|
|
6861
6953
|
this.clearScratchCleanupTimer(sessionId);
|
|
6862
6954
|
}
|
|
@@ -6912,14 +7004,22 @@ class CodingWorkspaceService {
|
|
|
6912
7004
|
console.log(`[CodingWorkspaceService] ${message}`);
|
|
6913
7005
|
}
|
|
6914
7006
|
}
|
|
7007
|
+
readConfigEnvKey(key) {
|
|
7008
|
+
return readConfigEnvKey(key);
|
|
7009
|
+
}
|
|
6915
7010
|
getScratchRetentionPolicy() {
|
|
6916
|
-
const setting = this.runtime.getSetting("PARALLAX_SCRATCH_RETENTION") ?? process.env.PARALLAX_SCRATCH_RETENTION;
|
|
7011
|
+
const setting = this.runtime.getSetting("PARALLAX_SCRATCH_RETENTION") ?? this.readConfigEnvKey("PARALLAX_SCRATCH_RETENTION") ?? process.env.PARALLAX_SCRATCH_RETENTION;
|
|
6917
7012
|
const normalized = setting?.trim().toLowerCase();
|
|
6918
7013
|
if (normalized === "ephemeral")
|
|
6919
7014
|
return "ephemeral";
|
|
6920
7015
|
if (normalized === "persistent" || normalized === "keep") {
|
|
6921
7016
|
return "persistent";
|
|
6922
7017
|
}
|
|
7018
|
+
if (!normalized) {
|
|
7019
|
+
const codingDir = this.runtime.getSetting("PARALLAX_CODING_DIRECTORY") ?? this.readConfigEnvKey("PARALLAX_CODING_DIRECTORY") ?? process.env.PARALLAX_CODING_DIRECTORY;
|
|
7020
|
+
if (codingDir?.trim())
|
|
7021
|
+
return "persistent";
|
|
7022
|
+
}
|
|
6923
7023
|
return "pending_decision";
|
|
6924
7024
|
}
|
|
6925
7025
|
getScratchDecisionTtlMs() {
|
|
@@ -6965,11 +7065,11 @@ class CodingWorkspaceService {
|
|
|
6965
7065
|
return compact || `scratch-${Date.now().toString(36)}`;
|
|
6966
7066
|
}
|
|
6967
7067
|
async allocatePromotedPath(baseDir, baseName) {
|
|
6968
|
-
const baseResolved =
|
|
7068
|
+
const baseResolved = path7.resolve(baseDir);
|
|
6969
7069
|
for (let i = 0;i < 1000; i++) {
|
|
6970
7070
|
const candidateName = i === 0 ? baseName : `${baseName}-${i}`;
|
|
6971
|
-
const candidate =
|
|
6972
|
-
if (candidate !== baseResolved && !candidate.startsWith(`${baseResolved}${
|
|
7071
|
+
const candidate = path7.resolve(baseResolved, candidateName);
|
|
7072
|
+
if (candidate !== baseResolved && !candidate.startsWith(`${baseResolved}${path7.sep}`)) {
|
|
6973
7073
|
continue;
|
|
6974
7074
|
}
|
|
6975
7075
|
try {
|
|
@@ -6984,8 +7084,8 @@ class CodingWorkspaceService {
|
|
|
6984
7084
|
// src/api/agent-routes.ts
|
|
6985
7085
|
import { access as access2, readFile as readFile4, realpath, rm as rm2 } from "node:fs/promises";
|
|
6986
7086
|
import { createHash } from "node:crypto";
|
|
6987
|
-
import * as
|
|
6988
|
-
import * as
|
|
7087
|
+
import * as os7 from "node:os";
|
|
7088
|
+
import * as path8 from "node:path";
|
|
6989
7089
|
import { execFile } from "node:child_process";
|
|
6990
7090
|
import { promisify } from "node:util";
|
|
6991
7091
|
var execFileAsync = promisify(execFile);
|
|
@@ -6997,23 +7097,23 @@ function shouldAutoPreflight() {
|
|
|
6997
7097
|
return false;
|
|
6998
7098
|
}
|
|
6999
7099
|
function isPathInside(parent, candidate) {
|
|
7000
|
-
return candidate === parent || candidate.startsWith(`${parent}${
|
|
7100
|
+
return candidate === parent || candidate.startsWith(`${parent}${path8.sep}`);
|
|
7001
7101
|
}
|
|
7002
7102
|
async function resolveSafeVenvPath(workdir, venvDirRaw) {
|
|
7003
7103
|
const venvDir = venvDirRaw.trim();
|
|
7004
7104
|
if (!venvDir) {
|
|
7005
7105
|
throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must be non-empty");
|
|
7006
7106
|
}
|
|
7007
|
-
if (
|
|
7107
|
+
if (path8.isAbsolute(venvDir)) {
|
|
7008
7108
|
throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must be relative to workdir");
|
|
7009
7109
|
}
|
|
7010
|
-
const normalized =
|
|
7011
|
-
if (normalized === "." || normalized === ".." || normalized.startsWith(`..${
|
|
7110
|
+
const normalized = path8.normalize(venvDir);
|
|
7111
|
+
if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path8.sep}`)) {
|
|
7012
7112
|
throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must stay within workdir");
|
|
7013
7113
|
}
|
|
7014
|
-
const workdirResolved =
|
|
7114
|
+
const workdirResolved = path8.resolve(workdir);
|
|
7015
7115
|
const workdirReal = await realpath(workdirResolved);
|
|
7016
|
-
const resolved =
|
|
7116
|
+
const resolved = path8.resolve(workdirReal, normalized);
|
|
7017
7117
|
if (!isPathInside(workdirReal, resolved)) {
|
|
7018
7118
|
throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV resolves outside workdir");
|
|
7019
7119
|
}
|
|
@@ -7029,7 +7129,7 @@ async function resolveSafeVenvPath(workdir, venvDirRaw) {
|
|
|
7029
7129
|
const maybeErr = err;
|
|
7030
7130
|
if (maybeErr?.code !== "ENOENT")
|
|
7031
7131
|
throw err;
|
|
7032
|
-
const parentReal = await realpath(
|
|
7132
|
+
const parentReal = await realpath(path8.dirname(resolved));
|
|
7033
7133
|
if (!isPathInside(workdirReal, parentReal)) {
|
|
7034
7134
|
throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV parent resolves outside workdir");
|
|
7035
7135
|
}
|
|
@@ -7045,10 +7145,10 @@ async function fileExists(filePath) {
|
|
|
7045
7145
|
}
|
|
7046
7146
|
}
|
|
7047
7147
|
async function resolveRequirementsPath(workdir) {
|
|
7048
|
-
const workdirReal = await realpath(
|
|
7148
|
+
const workdirReal = await realpath(path8.resolve(workdir));
|
|
7049
7149
|
const candidates = [
|
|
7050
|
-
|
|
7051
|
-
|
|
7150
|
+
path8.join(workdir, "apps", "api", "requirements.txt"),
|
|
7151
|
+
path8.join(workdir, "requirements.txt")
|
|
7052
7152
|
];
|
|
7053
7153
|
for (const candidate of candidates) {
|
|
7054
7154
|
if (!await fileExists(candidate))
|
|
@@ -7075,7 +7175,7 @@ async function runBenchmarkPreflight(workdir) {
|
|
|
7075
7175
|
const mode = process.env.PARALLAX_BENCHMARK_PREFLIGHT_MODE?.toLowerCase() === "warm" ? "warm" : "cold";
|
|
7076
7176
|
const venvDir = process.env.PARALLAX_BENCHMARK_PREFLIGHT_VENV || ".benchmark-venv";
|
|
7077
7177
|
const venvPath = await resolveSafeVenvPath(workdir, venvDir);
|
|
7078
|
-
const pythonInVenv =
|
|
7178
|
+
const pythonInVenv = path8.join(venvPath, process.platform === "win32" ? "Scripts" : "bin", process.platform === "win32" ? "python.exe" : "python");
|
|
7079
7179
|
const key = `${workdir}::${mode}::${venvPath}::${requirementsFingerprint}`;
|
|
7080
7180
|
if (PREFLIGHT_DONE.has(key)) {
|
|
7081
7181
|
if (await fileExists(pythonInVenv))
|
|
@@ -7283,21 +7383,21 @@ async function handleAgentRoutes(req, res, pathname, ctx) {
|
|
|
7283
7383
|
customCredentials,
|
|
7284
7384
|
metadata
|
|
7285
7385
|
} = body;
|
|
7286
|
-
const workspaceBaseDir =
|
|
7287
|
-
const workspaceBaseDirResolved =
|
|
7288
|
-
const cwdResolved =
|
|
7386
|
+
const workspaceBaseDir = path8.join(os7.homedir(), ".milady", "workspaces");
|
|
7387
|
+
const workspaceBaseDirResolved = path8.resolve(workspaceBaseDir);
|
|
7388
|
+
const cwdResolved = path8.resolve(process.cwd());
|
|
7289
7389
|
const workspaceBaseDirReal = await realpath(workspaceBaseDirResolved).catch(() => workspaceBaseDirResolved);
|
|
7290
7390
|
const cwdReal = await realpath(cwdResolved).catch(() => cwdResolved);
|
|
7291
7391
|
const allowedPrefixes = [workspaceBaseDirReal, cwdReal];
|
|
7292
7392
|
let workdir = rawWorkdir;
|
|
7293
7393
|
if (workdir) {
|
|
7294
|
-
const resolved =
|
|
7394
|
+
const resolved = path8.resolve(workdir);
|
|
7295
7395
|
const resolvedReal = await realpath(resolved).catch(() => null);
|
|
7296
7396
|
if (!resolvedReal) {
|
|
7297
7397
|
sendError(res, "workdir must exist", 403);
|
|
7298
7398
|
return true;
|
|
7299
7399
|
}
|
|
7300
|
-
const isAllowed = allowedPrefixes.some((prefix2) => resolvedReal === prefix2 || resolvedReal.startsWith(prefix2 +
|
|
7400
|
+
const isAllowed = allowedPrefixes.some((prefix2) => resolvedReal === prefix2 || resolvedReal.startsWith(prefix2 + path8.sep));
|
|
7301
7401
|
if (!isAllowed) {
|
|
7302
7402
|
sendError(res, "workdir must be within workspace base directory or cwd", 403);
|
|
7303
7403
|
return true;
|
|
@@ -7941,7 +8041,7 @@ async function handleWorkspaceRoutes(req, res, pathname, ctx) {
|
|
|
7941
8041
|
// src/api/routes.ts
|
|
7942
8042
|
var MAX_BODY_SIZE = 1024 * 1024;
|
|
7943
8043
|
async function parseBody(req) {
|
|
7944
|
-
return new Promise((
|
|
8044
|
+
return new Promise((resolve7, reject) => {
|
|
7945
8045
|
let body = "";
|
|
7946
8046
|
let size = 0;
|
|
7947
8047
|
req.on("data", (chunk) => {
|
|
@@ -7955,7 +8055,7 @@ async function parseBody(req) {
|
|
|
7955
8055
|
});
|
|
7956
8056
|
req.on("end", () => {
|
|
7957
8057
|
try {
|
|
7958
|
-
|
|
8058
|
+
resolve7(body ? JSON.parse(body) : {});
|
|
7959
8059
|
} catch {
|
|
7960
8060
|
reject(new Error("Invalid JSON body"));
|
|
7961
8061
|
}
|
|
@@ -8043,5 +8143,5 @@ export {
|
|
|
8043
8143
|
CodingWorkspaceService
|
|
8044
8144
|
};
|
|
8045
8145
|
|
|
8046
|
-
//# debugId=
|
|
8146
|
+
//# debugId=CC31B3CD95C1813A64756E2164756E21
|
|
8047
8147
|
//# sourceMappingURL=index.js.map
|