negotium 0.1.25 → 0.1.26
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/README.md +2 -0
- package/dist/agent-helpers.js +2 -2
- package/dist/agent-helpers.js.map +2 -2
- package/dist/cron.js +93 -38
- package/dist/cron.js.map +6 -5
- package/dist/hosted-agent.js +2 -2
- package/dist/hosted-agent.js.map +2 -2
- package/dist/main.js +149 -94
- package/dist/main.js.map +6 -5
- package/dist/runtime/src/platform/playwright/headed-launch.ts +61 -0
- package/dist/runtime/src/platform/playwright/manager.ts +39 -9
- package/dist/runtime/src/version.ts +1 -1
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/cron.js
CHANGED
|
@@ -3468,7 +3468,7 @@ var init_claude_provider = __esm(async () => {
|
|
|
3468
3468
|
});
|
|
3469
3469
|
|
|
3470
3470
|
// ../../packages/core/src/version.ts
|
|
3471
|
-
var NEGOTIUM_VERSION = "0.1.
|
|
3471
|
+
var NEGOTIUM_VERSION = "0.1.26";
|
|
3472
3472
|
|
|
3473
3473
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
3474
3474
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -5790,6 +5790,38 @@ var init_self_schedules = __esm(async () => {
|
|
|
5790
5790
|
`);
|
|
5791
5791
|
});
|
|
5792
5792
|
|
|
5793
|
+
// ../../packages/core/src/platform/playwright/headed-launch.ts
|
|
5794
|
+
import { accessSync, constants } from "fs";
|
|
5795
|
+
import { delimiter, isAbsolute, resolve as resolve5 } from "path";
|
|
5796
|
+
function findExecutableOnPath(command, environment = process.env) {
|
|
5797
|
+
const candidates = isAbsolute(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve5(directory, command));
|
|
5798
|
+
for (const candidate of candidates) {
|
|
5799
|
+
try {
|
|
5800
|
+
accessSync(candidate, constants.X_OK);
|
|
5801
|
+
return candidate;
|
|
5802
|
+
} catch {}
|
|
5803
|
+
}
|
|
5804
|
+
return null;
|
|
5805
|
+
}
|
|
5806
|
+
function resolveHeadedPlaywrightSpawn(command, args, options = {}) {
|
|
5807
|
+
const platform2 = options.platform ?? process.platform;
|
|
5808
|
+
const environment = options.environment ?? process.env;
|
|
5809
|
+
const hasDisplay = Boolean(environment.DISPLAY?.trim() || environment.WAYLAND_DISPLAY?.trim());
|
|
5810
|
+
if (platform2 !== "linux" || hasDisplay) {
|
|
5811
|
+
return { command, args: [...args], virtualDisplay: false };
|
|
5812
|
+
}
|
|
5813
|
+
const xvfbRun = (options.findExecutable ?? findExecutableOnPath)("xvfb-run", environment);
|
|
5814
|
+
if (!xvfbRun) {
|
|
5815
|
+
throw new Error("Headed Playwright on Linux requires DISPLAY/WAYLAND_DISPLAY or xvfb-run; install Xvfb and ensure xvfb-run is on PATH");
|
|
5816
|
+
}
|
|
5817
|
+
return {
|
|
5818
|
+
command: xvfbRun,
|
|
5819
|
+
args: ["-a", "-s", "-screen 0 1440x1000x24", command, ...args],
|
|
5820
|
+
virtualDisplay: true
|
|
5821
|
+
};
|
|
5822
|
+
}
|
|
5823
|
+
var init_headed_launch = () => {};
|
|
5824
|
+
|
|
5793
5825
|
// ../../packages/core/src/storage/browser-profiles.ts
|
|
5794
5826
|
function normalizeBrowserProfileName(name) {
|
|
5795
5827
|
const value = name.trim().toLowerCase();
|
|
@@ -5894,7 +5926,7 @@ import {
|
|
|
5894
5926
|
unlinkSync as unlinkSync9,
|
|
5895
5927
|
writeFileSync as writeFileSync7
|
|
5896
5928
|
} from "fs";
|
|
5897
|
-
import { dirname as dirname8, join as join13, resolve as
|
|
5929
|
+
import { dirname as dirname8, join as join13, resolve as resolve6 } from "path";
|
|
5898
5930
|
function makeInstanceKey(userId, topic) {
|
|
5899
5931
|
if (!topic)
|
|
5900
5932
|
return makeBrowserProfileInstanceKey(userId, "default");
|
|
@@ -5912,7 +5944,7 @@ function migrateLegacyTopicProfile(ownerId, topic) {
|
|
|
5912
5944
|
const current2 = getTopicBrowserProfile(topic);
|
|
5913
5945
|
if (current2 !== "default" || !hasBrowserProfileTopic(topic))
|
|
5914
5946
|
return current2;
|
|
5915
|
-
const legacyDir =
|
|
5947
|
+
const legacyDir = resolve6(BROWSER_PROFILES_DIR, sanitizeTopicName(topic));
|
|
5916
5948
|
if (!existsSync13(legacyDir))
|
|
5917
5949
|
return current2;
|
|
5918
5950
|
const profile = legacyBrowserProfileName(topic);
|
|
@@ -6091,11 +6123,11 @@ async function killPlaywrightOnPort(port, expectedUserDataDir) {
|
|
|
6091
6123
|
}
|
|
6092
6124
|
function browserProcessMatchesExpectedProfile(cmdline, expectedUserDataDir) {
|
|
6093
6125
|
const actualUserDataDir = extractUserDataDirArg(cmdline);
|
|
6094
|
-
return actualUserDataDir !== null &&
|
|
6126
|
+
return actualUserDataDir !== null && resolve6(actualUserDataDir) === resolve6(expectedUserDataDir);
|
|
6095
6127
|
}
|
|
6096
6128
|
function killBrowserProcsForUserDataDir(userDataDir) {
|
|
6097
|
-
const target =
|
|
6098
|
-
if (!target.startsWith(
|
|
6129
|
+
const target = resolve6(userDataDir);
|
|
6130
|
+
if (!target.startsWith(resolve6(BROWSER_PROFILES_DIR)))
|
|
6099
6131
|
return;
|
|
6100
6132
|
let pids;
|
|
6101
6133
|
try {
|
|
@@ -6115,7 +6147,7 @@ function killBrowserProcsForUserDataDir(userDataDir) {
|
|
|
6115
6147
|
stdio: "pipe"
|
|
6116
6148
|
}).toString().trim();
|
|
6117
6149
|
const argDir = extractUserDataDirArg(cmdline);
|
|
6118
|
-
if (!argDir ||
|
|
6150
|
+
if (!argDir || resolve6(argDir) !== target)
|
|
6119
6151
|
continue;
|
|
6120
6152
|
killProcessTreeChildren(pidNum);
|
|
6121
6153
|
process.kill(pidNum, "SIGKILL");
|
|
@@ -6126,13 +6158,13 @@ function killBrowserProcsForUserDataDir(userDataDir) {
|
|
|
6126
6158
|
}
|
|
6127
6159
|
}
|
|
6128
6160
|
function selectOrphanBrowserPids(procs, liveUserDataDirs, profileRoot, selfPid) {
|
|
6129
|
-
const root =
|
|
6130
|
-
const live = new Set([...liveUserDataDirs].map((d) =>
|
|
6161
|
+
const root = resolve6(profileRoot);
|
|
6162
|
+
const live = new Set([...liveUserDataDirs].map((d) => resolve6(d)));
|
|
6131
6163
|
const out = [];
|
|
6132
6164
|
for (const { pid, userDataDir } of procs) {
|
|
6133
6165
|
if (pid === selfPid || !userDataDir)
|
|
6134
6166
|
continue;
|
|
6135
|
-
const dir =
|
|
6167
|
+
const dir = resolve6(userDataDir);
|
|
6136
6168
|
if (!dir.startsWith(root))
|
|
6137
6169
|
continue;
|
|
6138
6170
|
if (live.has(dir))
|
|
@@ -6148,7 +6180,7 @@ function reapOrphanBrowsers() {
|
|
|
6148
6180
|
const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
|
|
6149
6181
|
if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid))
|
|
6150
6182
|
return;
|
|
6151
|
-
const profileRoot =
|
|
6183
|
+
const profileRoot = resolve6(BROWSER_PROFILES_DIR);
|
|
6152
6184
|
let pids;
|
|
6153
6185
|
try {
|
|
6154
6186
|
pids = execFileSync4("pgrep", ["-f", "--", profileRoot], { stdio: "pipe" }).toString().trim();
|
|
@@ -6201,7 +6233,7 @@ function cleanSingletonFiles(userDataDir) {
|
|
|
6201
6233
|
for (const f of files) {
|
|
6202
6234
|
if (f.startsWith("Singleton")) {
|
|
6203
6235
|
try {
|
|
6204
|
-
unlinkSync9(
|
|
6236
|
+
unlinkSync9(resolve6(userDataDir, f));
|
|
6205
6237
|
logger.info({ file: f, userDataDir }, "Removed stale Singleton file");
|
|
6206
6238
|
} catch (e) {
|
|
6207
6239
|
logger.warn({ err: e, file: f }, "Failed to remove stale Chrome Singleton file");
|
|
@@ -6237,7 +6269,7 @@ function ownerDirectory(ownerId) {
|
|
|
6237
6269
|
return `${sanitizeTopicName(ownerId).slice(0, 24)}_${digest}`;
|
|
6238
6270
|
}
|
|
6239
6271
|
function resolveProfileDir(ownerId, profile) {
|
|
6240
|
-
return
|
|
6272
|
+
return resolve6(BROWSER_PROFILES_DIR, "profiles", ownerDirectory(ownerId), profile);
|
|
6241
6273
|
}
|
|
6242
6274
|
function resolveUserDataDir(instanceKey) {
|
|
6243
6275
|
const { ownerId, profile } = parseInstanceKey(instanceKey);
|
|
@@ -6295,11 +6327,25 @@ async function spawnPlaywright(instanceKey, userId, reservedPort, browserBin = P
|
|
|
6295
6327
|
}
|
|
6296
6328
|
const command = browserBin.endsWith(".mjs") ? process.execPath : browserBin;
|
|
6297
6329
|
const args = browserBin.endsWith(".mjs") ? [browserBin, ...mcpArgs] : mcpArgs;
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6330
|
+
let spawnSpec;
|
|
6331
|
+
try {
|
|
6332
|
+
spawnSpec = resolveHeadedPlaywrightSpawn(command, args, { environment: childEnv });
|
|
6333
|
+
} catch (err) {
|
|
6334
|
+
releasePort(port);
|
|
6335
|
+
throw err;
|
|
6336
|
+
}
|
|
6337
|
+
let proc;
|
|
6338
|
+
try {
|
|
6339
|
+
proc = spawn4(spawnSpec.command, spawnSpec.args, {
|
|
6340
|
+
stdio: "ignore",
|
|
6341
|
+
detached: false,
|
|
6342
|
+
env: childEnv
|
|
6343
|
+
});
|
|
6344
|
+
} catch (err) {
|
|
6345
|
+
releasePort(port);
|
|
6346
|
+
throw err;
|
|
6347
|
+
}
|
|
6348
|
+
const spawnError = waitForChildProcessSpawnError(proc);
|
|
6303
6349
|
const reapCrashedBrowser = () => {
|
|
6304
6350
|
const userDataDir2 = resolveUserDataDir(instanceKey);
|
|
6305
6351
|
killBrowserProcsForUserDataDir(userDataDir2);
|
|
@@ -6332,7 +6378,10 @@ async function spawnPlaywright(instanceKey, userId, reservedPort, browserBin = P
|
|
|
6332
6378
|
lastUsedAt: now,
|
|
6333
6379
|
capability
|
|
6334
6380
|
});
|
|
6335
|
-
const ready = await
|
|
6381
|
+
const ready = await Promise.race([
|
|
6382
|
+
(async () => await waitForServer(port, 1e4) && await supportsOwnerCleanup(port, capability))(),
|
|
6383
|
+
spawnError
|
|
6384
|
+
]);
|
|
6336
6385
|
if (!ready) {
|
|
6337
6386
|
const exitCode = proc.exitCode;
|
|
6338
6387
|
killInstance(instanceKey);
|
|
@@ -6343,7 +6392,7 @@ async function spawnPlaywright(instanceKey, userId, reservedPort, browserBin = P
|
|
|
6343
6392
|
throw new Error(`Playwright MCP failed health check after spawn on port ${port}` + (exitCode === null ? "" : ` (exitCode=${exitCode})`));
|
|
6344
6393
|
}
|
|
6345
6394
|
writePortFile(instanceKey, port);
|
|
6346
|
-
logger.info({ instanceKey, port, pid: proc.pid, ready }, "Playwright MCP started");
|
|
6395
|
+
logger.info({ instanceKey, port, pid: proc.pid, ready, virtualDisplay: spawnSpec.virtualDisplay }, "Playwright MCP started");
|
|
6347
6396
|
return port;
|
|
6348
6397
|
}
|
|
6349
6398
|
async function supportsOwnerCleanup(port, capability) {
|
|
@@ -6411,10 +6460,16 @@ async function waitForServer(port, timeoutMs) {
|
|
|
6411
6460
|
logger.warn({ port, timeoutMs }, "Playwright MCP not ready before timeout");
|
|
6412
6461
|
return false;
|
|
6413
6462
|
}
|
|
6463
|
+
function waitForChildProcessSpawnError(proc) {
|
|
6464
|
+
return new Promise((_resolve, reject) => {
|
|
6465
|
+
proc.once("error", reject);
|
|
6466
|
+
});
|
|
6467
|
+
}
|
|
6414
6468
|
var BASE_PORT, MAX_PORT, MAX_IDLE_MS, instances, usedPorts, spawning, pinnedInstances, _onPlaywrightExit = null;
|
|
6415
6469
|
var init_manager2 = __esm(async () => {
|
|
6416
6470
|
init_config();
|
|
6417
6471
|
init_logger();
|
|
6472
|
+
init_headed_launch();
|
|
6418
6473
|
await init_browser_profiles();
|
|
6419
6474
|
await init_runtime_process_leases();
|
|
6420
6475
|
BASE_PORT = PLAYWRIGHT_BASE_PORT;
|
|
@@ -7039,9 +7094,9 @@ var init_runtime_turn_requests = __esm(async () => {
|
|
|
7039
7094
|
|
|
7040
7095
|
// ../../packages/core/src/prompts/builders.ts
|
|
7041
7096
|
import { readFileSync as readFileSync10 } from "fs";
|
|
7042
|
-
import { resolve as
|
|
7097
|
+
import { resolve as resolve7 } from "path";
|
|
7043
7098
|
function loadPrompt(filename, dir = SESSIONS_DIR) {
|
|
7044
|
-
const raw = readFileSync10(
|
|
7099
|
+
const raw = readFileSync10(resolve7(dir, filename), "utf-8");
|
|
7045
7100
|
return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
|
|
7046
7101
|
}
|
|
7047
7102
|
function replaceVars(template, vars) {
|
|
@@ -7084,7 +7139,7 @@ function visualDesignGuide() {
|
|
|
7084
7139
|
return _visualDesignGuide;
|
|
7085
7140
|
}
|
|
7086
7141
|
function loadAgentPrompt(filename) {
|
|
7087
|
-
const raw = readFileSync10(
|
|
7142
|
+
const raw = readFileSync10(resolve7(AGENTS_PROMPTS_DIR, filename), "utf-8");
|
|
7088
7143
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
7089
7144
|
if (!match)
|
|
7090
7145
|
throw new Error(`Agent prompt ${filename} is missing frontmatter`);
|
|
@@ -7284,8 +7339,8 @@ var init_builders = __esm(() => {
|
|
|
7284
7339
|
init_model_catalog();
|
|
7285
7340
|
init_config();
|
|
7286
7341
|
init_logger();
|
|
7287
|
-
PROMPTS_DIR =
|
|
7288
|
-
SESSIONS_DIR =
|
|
7342
|
+
PROMPTS_DIR = resolve7(PROJECT_ROOT, "src/prompts");
|
|
7343
|
+
SESSIONS_DIR = resolve7(PROMPTS_DIR, "sessions");
|
|
7289
7344
|
});
|
|
7290
7345
|
|
|
7291
7346
|
// ../../packages/core/src/storage/api-topic-brief.ts
|
|
@@ -8646,12 +8701,12 @@ var init_errors = __esm(() => {
|
|
|
8646
8701
|
|
|
8647
8702
|
// ../../packages/core/src/runtime/event-heartbeat.ts
|
|
8648
8703
|
function nextOrHeartbeat(pending, intervalMs) {
|
|
8649
|
-
return new Promise((
|
|
8650
|
-
const timer = setTimeout(() =>
|
|
8704
|
+
return new Promise((resolve8, reject) => {
|
|
8705
|
+
const timer = setTimeout(() => resolve8({ kind: "heartbeat" }), intervalMs);
|
|
8651
8706
|
timer.unref?.();
|
|
8652
8707
|
pending.then((result) => {
|
|
8653
8708
|
clearTimeout(timer);
|
|
8654
|
-
|
|
8709
|
+
resolve8({ kind: "event", result });
|
|
8655
8710
|
}, (error) => {
|
|
8656
8711
|
clearTimeout(timer);
|
|
8657
8712
|
reject(error);
|
|
@@ -9078,7 +9133,7 @@ var init_visual_html = __esm(() => {
|
|
|
9078
9133
|
|
|
9079
9134
|
// ../../packages/core/src/runtime/visuals.ts
|
|
9080
9135
|
import { realpathSync as realpathSync2 } from "fs";
|
|
9081
|
-
import { isAbsolute, resolve as
|
|
9136
|
+
import { isAbsolute as isAbsolute2, resolve as resolve8 } from "path";
|
|
9082
9137
|
function activeVisualHtmlForPrompt(html) {
|
|
9083
9138
|
if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
|
|
9084
9139
|
return { html, omittedChars: 0 };
|
|
@@ -9129,8 +9184,8 @@ function topicAllowsVisualFileId(topicId, fileId) {
|
|
|
9129
9184
|
return topicHasAttachmentFileId(topicId, fileId) || topicHasVisualFileId(topicId, fileId);
|
|
9130
9185
|
}
|
|
9131
9186
|
function isPathInside(baseDir, filePath) {
|
|
9132
|
-
const base =
|
|
9133
|
-
const normalized =
|
|
9187
|
+
const base = resolve8(baseDir);
|
|
9188
|
+
const normalized = resolve8(filePath);
|
|
9134
9189
|
try {
|
|
9135
9190
|
const realBase = realpathSync2(base);
|
|
9136
9191
|
const real = realpathSync2(normalized);
|
|
@@ -9192,7 +9247,7 @@ function resolveVisualMediaInput(topicId, input) {
|
|
|
9192
9247
|
}
|
|
9193
9248
|
const rawPath = input.file_path.trim();
|
|
9194
9249
|
const cwd = workspaceCwdFor(topicId);
|
|
9195
|
-
const candidate =
|
|
9250
|
+
const candidate = isAbsolute2(rawPath) ? rawPath : resolve8(cwd, rawPath);
|
|
9196
9251
|
if (!isPathInside(cwd, candidate)) {
|
|
9197
9252
|
return { error: "file_path must be inside the topic workspace" };
|
|
9198
9253
|
}
|
|
@@ -9906,7 +9961,7 @@ __export(exports_turn_runner, {
|
|
|
9906
9961
|
});
|
|
9907
9962
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
9908
9963
|
import { mkdirSync as mkdirSync16, realpathSync as realpathSync3, statSync as statSync6 } from "fs";
|
|
9909
|
-
import { isAbsolute as
|
|
9964
|
+
import { isAbsolute as isAbsolute3, resolve as resolve9 } from "path";
|
|
9910
9965
|
function withDefaultPlaywright(configuredMcp, isManager) {
|
|
9911
9966
|
if (isManager)
|
|
9912
9967
|
return configuredMcp;
|
|
@@ -10250,7 +10305,7 @@ async function streamAgentEvents(topicId, topicTitle, queryId, events, control,
|
|
|
10250
10305
|
case "file":
|
|
10251
10306
|
if (!silent && peerBridge) {
|
|
10252
10307
|
const cwd = workspaceCwdFor(topicId);
|
|
10253
|
-
const path =
|
|
10308
|
+
const path = isAbsolute3(event.path) ? event.path : resolve9(cwd, event.path);
|
|
10254
10309
|
if (!isPathInside(cwd, path)) {
|
|
10255
10310
|
logger.warn({ topicId, path }, "peer output file is outside the topic workspace");
|
|
10256
10311
|
break;
|
|
@@ -12530,8 +12585,8 @@ function computeNextCronRun(expression, after = new Date, timezone) {
|
|
|
12530
12585
|
// ../../packages/module-cron/src/scripts.ts
|
|
12531
12586
|
import { spawn as spawn5 } from "child_process";
|
|
12532
12587
|
import { existsSync as existsSync16, mkdirSync as mkdirSync19, readdirSync as readdirSync6 } from "fs";
|
|
12533
|
-
import { resolve as
|
|
12534
|
-
var CRON_JOBS_DIR =
|
|
12588
|
+
import { resolve as resolve10, sep } from "path";
|
|
12589
|
+
var CRON_JOBS_DIR = resolve10(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve10(WORKSPACE_DIR, "cron", "jobs"));
|
|
12535
12590
|
var DEFAULT_SCRIPT_TIMEOUT_MS = 10 * 60000;
|
|
12536
12591
|
var DEFAULT_OUTPUT_LIMIT_BYTES = 1024 * 1024;
|
|
12537
12592
|
var KILL_GRACE_MS = 2500;
|
|
@@ -12554,7 +12609,7 @@ function resolveCronScriptPath(script) {
|
|
|
12554
12609
|
const valid = validateCronScriptName(script);
|
|
12555
12610
|
if (!valid.ok)
|
|
12556
12611
|
throw new Error(valid.error);
|
|
12557
|
-
const path =
|
|
12612
|
+
const path = resolve10(CRON_JOBS_DIR, script);
|
|
12558
12613
|
if (!path.startsWith(`${CRON_JOBS_DIR}${sep}`))
|
|
12559
12614
|
throw new Error("script path escaped jobs directory");
|
|
12560
12615
|
return path;
|
|
@@ -14012,4 +14067,4 @@ export {
|
|
|
14012
14067
|
CRON_CONTEXT_RETAIN_TURNS
|
|
14013
14068
|
};
|
|
14014
14069
|
|
|
14015
|
-
//# debugId=
|
|
14070
|
+
//# debugId=8A97E8B383252AFE64756E2164756E21
|