@mrrlin-dev/mcp 0.3.42 → 0.3.46
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/bin.cjs +154 -18
- package/package.json +3 -2
package/dist/bin.cjs
CHANGED
|
@@ -20672,12 +20672,18 @@ function browserPaths(homeDir = import_node_os.default.homedir()) {
|
|
|
20672
20672
|
const root = import_node_path.default.join(homeDir, ".mrrlin", "browser");
|
|
20673
20673
|
return { root, browsersPath: import_node_path.default.join(root, "browsers"), profilePath: import_node_path.default.join(root, "profile") };
|
|
20674
20674
|
}
|
|
20675
|
-
function resolvePackageBin(pkg) {
|
|
20675
|
+
function resolvePackageBin(pkg, binName) {
|
|
20676
20676
|
const require2 = moduleRequire();
|
|
20677
20677
|
const pkgJsonPath = require2.resolve(`${pkg}/package.json`);
|
|
20678
20678
|
const pkgJson = require2(`${pkg}/package.json`);
|
|
20679
20679
|
const bin = pkgJson.bin;
|
|
20680
|
-
|
|
20680
|
+
let rel;
|
|
20681
|
+
if (binName && bin && typeof bin !== "string") {
|
|
20682
|
+
rel = bin[binName];
|
|
20683
|
+
if (!rel) throw new Error(`${pkg} has no bin entry named ${binName}`);
|
|
20684
|
+
} else {
|
|
20685
|
+
rel = typeof bin === "string" ? bin : bin ? Object.values(bin)[0] : void 0;
|
|
20686
|
+
}
|
|
20681
20687
|
if (!rel) throw new Error(`${pkg} has no bin entry to resolve a CLI from`);
|
|
20682
20688
|
return import_node_path.default.join(import_node_path.default.dirname(pkgJsonPath), rel);
|
|
20683
20689
|
}
|
|
@@ -20691,6 +20697,13 @@ function browserServeArgs(homeDir = import_node_os.default.homedir()) {
|
|
|
20691
20697
|
env: { PLAYWRIGHT_BROWSERS_PATH: browsersPath }
|
|
20692
20698
|
};
|
|
20693
20699
|
}
|
|
20700
|
+
function externalAgentsServeArgs() {
|
|
20701
|
+
return {
|
|
20702
|
+
command: process.execPath,
|
|
20703
|
+
// node
|
|
20704
|
+
args: [resolvePackageBin("@mrrlin-dev/external-agents", "external-agents-mcp")]
|
|
20705
|
+
};
|
|
20706
|
+
}
|
|
20694
20707
|
async function installBrowser(opts = {}) {
|
|
20695
20708
|
const homeDir = opts.homeDir ?? import_node_os.default.homedir();
|
|
20696
20709
|
const { browsersPath } = browserPaths(homeDir);
|
|
@@ -20747,8 +20760,9 @@ function mrrlinTable(binPath, distribution, serveEnv) {
|
|
|
20747
20760
|
}
|
|
20748
20761
|
return { ...base, startup_timeout_sec: 30 };
|
|
20749
20762
|
}
|
|
20750
|
-
function externalAgentsTable() {
|
|
20751
|
-
|
|
20763
|
+
function externalAgentsTable(binPath, distribution) {
|
|
20764
|
+
const base = distribution === "npm" ? { command: "mrrlin-mcp", args: ["external-agents-serve"] } : { command: "node", args: [binPath, "external-agents-serve"] };
|
|
20765
|
+
return { ...base, startup_timeout_sec: 30 };
|
|
20752
20766
|
}
|
|
20753
20767
|
function browserTable(binPath, distribution, homeDir) {
|
|
20754
20768
|
const base = distribution === "npm" ? { command: "mrrlin-mcp", args: ["browser-serve"] } : { command: "node", args: [binPath, "browser-serve"] };
|
|
@@ -20773,8 +20787,22 @@ function managedBlocks(options, distribution) {
|
|
|
20773
20787
|
},
|
|
20774
20788
|
{
|
|
20775
20789
|
id: "external-agents",
|
|
20776
|
-
expected: externalAgentsTable(),
|
|
20777
|
-
blockRe: blockRegex("external-agents")
|
|
20790
|
+
expected: externalAgentsTable(options.binPath, distribution),
|
|
20791
|
+
blockRe: blockRegex("external-agents"),
|
|
20792
|
+
// Match ONLY the exact shape this installer used to write: a bare command, optionally with
|
|
20793
|
+
// empty args / a startup_timeout_sec, and NOTHING else. An extra key means the operator
|
|
20794
|
+
// customized the block — most plausibly a hand-added `env` carrying provider API keys, which
|
|
20795
|
+
// renderBlock() would silently drop on rewrite (it renders strictly from `expected`). Such a
|
|
20796
|
+
// block must fall through to CODEX_CONFIG_CONFLICT so replacing it takes an explicit --force,
|
|
20797
|
+
// rather than losing an operator's credentials during a routine update. We deliberately do
|
|
20798
|
+
// NOT carry the env forward instead: KNOWN_MRRLIN_ENV_KEYS treats any foreign key as
|
|
20799
|
+
// operator-authored and never-preserve, so a carried key would turn the NEXT bare re-run into
|
|
20800
|
+
// a conflict and break idempotency.
|
|
20801
|
+
isLegacyOurs: (actual) => {
|
|
20802
|
+
const legacyKeys = /* @__PURE__ */ new Set(["command", "args", "startup_timeout_sec"]);
|
|
20803
|
+
if (!Object.keys(actual).every((key) => legacyKeys.has(key))) return false;
|
|
20804
|
+
return actual.command === "external-agents-mcp" && (actual.args === void 0 || Array.isArray(actual.args) && actual.args.length === 0);
|
|
20805
|
+
}
|
|
20778
20806
|
}
|
|
20779
20807
|
];
|
|
20780
20808
|
}
|
|
@@ -20917,6 +20945,12 @@ async function installCodex(options) {
|
|
|
20917
20945
|
if (action === "noop") action = "env-preserved";
|
|
20918
20946
|
continue;
|
|
20919
20947
|
}
|
|
20948
|
+
if (block.isLegacyOurs?.(existingTable)) {
|
|
20949
|
+
content = replaceBlock(content, block.blockRe, renderBlock(block));
|
|
20950
|
+
action = "replaced";
|
|
20951
|
+
mutated = true;
|
|
20952
|
+
continue;
|
|
20953
|
+
}
|
|
20920
20954
|
if (!options.force) {
|
|
20921
20955
|
const err = {
|
|
20922
20956
|
code: "CODEX_CONFIG_CONFLICT",
|
|
@@ -37497,6 +37531,38 @@ var mrrlinSchedulerPendingReasonSchema = external_exports.enum([
|
|
|
37497
37531
|
"verification_due",
|
|
37498
37532
|
"wake_floor_passed"
|
|
37499
37533
|
]);
|
|
37534
|
+
var mrrlinAutonomousStepSchema = external_exports.enum([
|
|
37535
|
+
"noop",
|
|
37536
|
+
"needs_human",
|
|
37537
|
+
"enqueue_spec_draft",
|
|
37538
|
+
"enqueue_consensus",
|
|
37539
|
+
"auto_approve_spec_and_run",
|
|
37540
|
+
"create_execution_run"
|
|
37541
|
+
]);
|
|
37542
|
+
var mrrlinAutonomousStepReasonSchema = external_exports.enum([
|
|
37543
|
+
"task_terminal",
|
|
37544
|
+
"active_run_exists",
|
|
37545
|
+
"blocked_by_active",
|
|
37546
|
+
"draft_running",
|
|
37547
|
+
"consensus_running",
|
|
37548
|
+
"spec_missing",
|
|
37549
|
+
"spec_draft_failed",
|
|
37550
|
+
"spec_not_specs_folder",
|
|
37551
|
+
"awaiting_clarification",
|
|
37552
|
+
"consensus_blocked",
|
|
37553
|
+
"consensus_disabled_spec_not_ready",
|
|
37554
|
+
"spec_approval_pending",
|
|
37555
|
+
"non_code_execution_unsupported"
|
|
37556
|
+
]);
|
|
37557
|
+
var mrrlinAutonomousStepVerdictSchema = external_exports.object({
|
|
37558
|
+
step: mrrlinAutonomousStepSchema,
|
|
37559
|
+
reason: mrrlinAutonomousStepReasonSchema.nullable(),
|
|
37560
|
+
// Additive: when reason is "spec_draft_failed" or "consensus_blocked" (the two reasons that park
|
|
37561
|
+
// a task until a human intervenes), the attempt_count of the async_jobs row that produced that
|
|
37562
|
+
// outcome — so the operator can see "failed x3" and judge whether a retry mechanism is needed.
|
|
37563
|
+
// null for every other reason/step (not applicable), and also null if the job could not be found.
|
|
37564
|
+
failureCount: external_exports.number().int().min(0).nullable()
|
|
37565
|
+
});
|
|
37500
37566
|
var mrrlinSchedulerPendingTaskSchema = external_exports.object({
|
|
37501
37567
|
dueAt: external_exports.string().datetime(),
|
|
37502
37568
|
reason: mrrlinSchedulerPendingReasonSchema,
|
|
@@ -37506,7 +37572,12 @@ var mrrlinSchedulerPendingTaskSchema = external_exports.object({
|
|
|
37506
37572
|
// trigger at all. Consumers must read this instead of re-deriving due-ness by re-parsing `dueAt`
|
|
37507
37573
|
// against "now" — a task with no trigger has `dueAt === evaluatedAt`, which a naive `dueAt <= now`
|
|
37508
37574
|
// re-derivation would misclassify as due.
|
|
37509
|
-
due: external_exports.boolean()
|
|
37575
|
+
due: external_exports.boolean(),
|
|
37576
|
+
// Additive: the REAL admission verdict for this row, computed only inside the tick's bounded
|
|
37577
|
+
// per-cycle slice (`resolveSchedulerProjectLimit`). `null` means "not evaluated this cycle" —
|
|
37578
|
+
// the row is a genuine actionable candidate by the cheap classifier, but beyond the bound the
|
|
37579
|
+
// tick actually looks at, so no real verdict exists yet. Never infer readiness from `null`.
|
|
37580
|
+
verdict: mrrlinAutonomousStepVerdictSchema.nullable()
|
|
37510
37581
|
});
|
|
37511
37582
|
var mrrlinSchedulerParkedReasonSchema = external_exports.enum([
|
|
37512
37583
|
"running",
|
|
@@ -37522,6 +37593,32 @@ var mrrlinSchedulerParkedTaskSchema = external_exports.object({
|
|
|
37522
37593
|
inboxItemId: external_exports.string().nullable(),
|
|
37523
37594
|
runId: mrrlinExecutionRunIdSchema.nullable()
|
|
37524
37595
|
});
|
|
37596
|
+
var mrrlinInFlightSchedulerEntrySchema = external_exports.discriminatedUnion("kind", [
|
|
37597
|
+
external_exports.object({
|
|
37598
|
+
kind: external_exports.literal("run"),
|
|
37599
|
+
taskId: mrrlinTaskIdSchema,
|
|
37600
|
+
runId: mrrlinExecutionRunIdSchema,
|
|
37601
|
+
status: external_exports.string(),
|
|
37602
|
+
// Additive: the task's own title, resolved server-side from the same task list the handler
|
|
37603
|
+
// already loaded for this request (no extra query). null only when the task itself could not
|
|
37604
|
+
// be found (should not happen for a `run` entry, whose taskId is required, but the wire
|
|
37605
|
+
// contract stays honest about it rather than assuming).
|
|
37606
|
+
taskTitle: external_exports.string().nullable()
|
|
37607
|
+
}),
|
|
37608
|
+
external_exports.object({
|
|
37609
|
+
kind: external_exports.literal("async_job"),
|
|
37610
|
+
taskId: mrrlinTaskIdSchema.nullable(),
|
|
37611
|
+
jobId: external_exports.string(),
|
|
37612
|
+
toolName: external_exports.enum(["draft_task_spec", "run_spec_consensus"]),
|
|
37613
|
+
// Additive: same resolution as the `run` variant above. null when `taskId` is null (a job
|
|
37614
|
+
// genuinely untied to a visible task) or the task could not be found.
|
|
37615
|
+
taskTitle: external_exports.string().nullable()
|
|
37616
|
+
})
|
|
37617
|
+
]);
|
|
37618
|
+
var mrrlinIneligibleTaskSchema = external_exports.object({
|
|
37619
|
+
task: mrrlinTaskSchema,
|
|
37620
|
+
autonomyLevel: mrrlinAutonomyLevelSchema
|
|
37621
|
+
});
|
|
37525
37622
|
var schedulerPendingTasksResponseSchema = external_exports.object({
|
|
37526
37623
|
data: external_exports.object({
|
|
37527
37624
|
evaluatedAt: external_exports.string().datetime(),
|
|
@@ -37529,7 +37626,21 @@ var schedulerPendingTasksResponseSchema = external_exports.object({
|
|
|
37529
37626
|
parked: external_exports.array(mrrlinSchedulerParkedTaskSchema),
|
|
37530
37627
|
slots: external_exports.number().int(),
|
|
37531
37628
|
perProjectLimit: external_exports.number().int().min(1),
|
|
37532
|
-
policy: mrrlinProjectSchedulerPolicySchema
|
|
37629
|
+
policy: mrrlinProjectSchedulerPolicySchema,
|
|
37630
|
+
// Additive (see mrrlinInFlightSchedulerEntrySchema doc): the actual runs/jobs occupying
|
|
37631
|
+
// autonomous capacity right now. `slots` is derived from THIS SAME list server-side, so the
|
|
37632
|
+
// count and the list can never disagree.
|
|
37633
|
+
inFlight: external_exports.array(mrrlinInFlightSchedulerEntrySchema),
|
|
37634
|
+
// Additive: tasks the tick never even considers (autonomyLevel outside
|
|
37635
|
+
// policy.eligibleAutonomyLevels), excluding terminal tasks.
|
|
37636
|
+
ineligible: external_exports.array(mrrlinIneligibleTaskSchema),
|
|
37637
|
+
// Additive: how many non-terminal tasks were excluded from BOTH `pending` and `parked` because
|
|
37638
|
+
// their assignee is set and does not match the project operator (github.operator). A COUNT
|
|
37639
|
+
// only, deliberately — the operator asked not to show a list of other people's tasks, just a
|
|
37640
|
+
// disclosure that some exist, so a misconfigured `github.operator` (e.g. it doesn't match the
|
|
37641
|
+
// person actually running the project) is visible as a large number instead of the autopilot
|
|
37642
|
+
// silently doing nothing.
|
|
37643
|
+
excludedByOwnershipCount: external_exports.number().int().min(0)
|
|
37533
37644
|
})
|
|
37534
37645
|
});
|
|
37535
37646
|
var wakeDirectorRequestSchema = external_exports.object({
|
|
@@ -38999,7 +39110,7 @@ function startCodexProbe(deps = {}) {
|
|
|
38999
39110
|
}
|
|
39000
39111
|
|
|
39001
39112
|
// src/_generated/version.ts
|
|
39002
|
-
var PKG_VERSION = "0.3.
|
|
39113
|
+
var PKG_VERSION = "0.3.46";
|
|
39003
39114
|
|
|
39004
39115
|
// src/api-base-url.ts
|
|
39005
39116
|
var DEFAULT_API_BASE_URL = "http://127.0.0.1:8787";
|
|
@@ -39292,6 +39403,19 @@ async function collectAgentPoolSnapshot(deps) {
|
|
|
39292
39403
|
);
|
|
39293
39404
|
}
|
|
39294
39405
|
|
|
39406
|
+
// src/external-agents-bin.ts
|
|
39407
|
+
function resolveExternalAgentsCliInvocation(override) {
|
|
39408
|
+
if (override && override.trim()) return { command: override, prefixArgs: [] };
|
|
39409
|
+
try {
|
|
39410
|
+
return {
|
|
39411
|
+
command: "node",
|
|
39412
|
+
prefixArgs: [resolvePackageBin("@mrrlin-dev/external-agents", "external-agents")]
|
|
39413
|
+
};
|
|
39414
|
+
} catch {
|
|
39415
|
+
return { command: "external-agents", prefixArgs: [] };
|
|
39416
|
+
}
|
|
39417
|
+
}
|
|
39418
|
+
|
|
39295
39419
|
// src/agent-pool-loop.ts
|
|
39296
39420
|
var DEFAULT_INTERVAL_MS2 = 6e4;
|
|
39297
39421
|
function agentPoolLoopDisabled(env = process.env) {
|
|
@@ -39299,7 +39423,7 @@ function agentPoolLoopDisabled(env = process.env) {
|
|
|
39299
39423
|
}
|
|
39300
39424
|
function defaultRunCli(args) {
|
|
39301
39425
|
const cli = process.env["EXTERNAL_AGENTS_CLI"];
|
|
39302
|
-
const
|
|
39426
|
+
const { command, prefixArgs } = cli ? { command: "node", prefixArgs: [cli] } : resolveExternalAgentsCliInvocation();
|
|
39303
39427
|
return new Promise((resolve) => {
|
|
39304
39428
|
let stdout = "";
|
|
39305
39429
|
let settled = false;
|
|
@@ -39310,7 +39434,7 @@ function defaultRunCli(args) {
|
|
|
39310
39434
|
}
|
|
39311
39435
|
};
|
|
39312
39436
|
try {
|
|
39313
|
-
const child = (0, import_node_child_process5.spawn)(
|
|
39437
|
+
const child = (0, import_node_child_process5.spawn)(command, [...prefixArgs, ...args], { stdio: ["ignore", "pipe", "ignore"] });
|
|
39314
39438
|
const timer = setTimeout(() => {
|
|
39315
39439
|
try {
|
|
39316
39440
|
child.kill("SIGKILL");
|
|
@@ -53172,7 +53296,7 @@ var import_node_child_process11 = require("node:child_process");
|
|
|
53172
53296
|
function errMessage3(err) {
|
|
53173
53297
|
return err instanceof Error ? err.message : String(err);
|
|
53174
53298
|
}
|
|
53175
|
-
function runExternalAgents(bin, args, timeoutMs, spawn3) {
|
|
53299
|
+
function runExternalAgents(bin, args, label, timeoutMs, spawn3) {
|
|
53176
53300
|
return new Promise((resolve) => {
|
|
53177
53301
|
let settled = false;
|
|
53178
53302
|
const timerRef = { id: void 0 };
|
|
@@ -53198,20 +53322,20 @@ function runExternalAgents(bin, args, timeoutMs, spawn3) {
|
|
|
53198
53322
|
let stdout = "";
|
|
53199
53323
|
timerRef.id = setTimeout(() => {
|
|
53200
53324
|
child.kill("SIGKILL");
|
|
53201
|
-
finish({ code: null, stdout, error: `external-agents ${
|
|
53325
|
+
finish({ code: null, stdout, error: `external-agents ${label} timed out after ${timeoutMs}ms` });
|
|
53202
53326
|
}, timeoutMs);
|
|
53203
53327
|
child.stdout?.on("data", (chunk) => {
|
|
53204
53328
|
stdout += chunk.toString();
|
|
53205
53329
|
});
|
|
53206
53330
|
child.stderr?.on("data", () => {
|
|
53207
53331
|
});
|
|
53208
|
-
child.on("error", (err) => finish({ code: null, stdout, error: `external-agents ${
|
|
53332
|
+
child.on("error", (err) => finish({ code: null, stdout, error: `external-agents ${label} error: ${errMessage3(err)}` }));
|
|
53209
53333
|
child.on("close", (code) => finish({ code, stdout }));
|
|
53210
53334
|
});
|
|
53211
53335
|
}
|
|
53212
53336
|
async function pickPanel(input, deps) {
|
|
53213
53337
|
const spawn3 = deps?.spawn ?? import_node_child_process11.spawn;
|
|
53214
|
-
const
|
|
53338
|
+
const { command, prefixArgs } = resolveExternalAgentsCliInvocation(deps?.externalAgentsBin);
|
|
53215
53339
|
const args = [
|
|
53216
53340
|
"pick",
|
|
53217
53341
|
"--tier-prefer",
|
|
@@ -53223,7 +53347,7 @@ async function pickPanel(input, deps) {
|
|
|
53223
53347
|
"--exclude-providers",
|
|
53224
53348
|
input.excludeProvider
|
|
53225
53349
|
];
|
|
53226
|
-
const r = await runExternalAgents(
|
|
53350
|
+
const r = await runExternalAgents(command, [...prefixArgs, ...args], "pick", input.timeoutMs, spawn3);
|
|
53227
53351
|
if (r.error) return { ok: false, error: r.error };
|
|
53228
53352
|
if (r.code === 3) return { ok: true, agents: [] };
|
|
53229
53353
|
if (r.code !== 0) return { ok: false, error: `external-agents pick exited ${r.code}` };
|
|
@@ -53232,9 +53356,9 @@ async function pickPanel(input, deps) {
|
|
|
53232
53356
|
}
|
|
53233
53357
|
async function dispatchHead(agentId, prompt, timeoutMs, deps) {
|
|
53234
53358
|
const spawn3 = deps?.spawn ?? import_node_child_process11.spawn;
|
|
53235
|
-
const
|
|
53359
|
+
const { command, prefixArgs } = resolveExternalAgentsCliInvocation(deps?.externalAgentsBin);
|
|
53236
53360
|
const args = ["dispatch", agentId, "--json", prompt];
|
|
53237
|
-
const r = await runExternalAgents(
|
|
53361
|
+
const r = await runExternalAgents(command, [...prefixArgs, ...args], "dispatch", timeoutMs, spawn3);
|
|
53238
53362
|
if (r.error) return { ok: false, error: r.error };
|
|
53239
53363
|
const lines2 = r.stdout.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
53240
53364
|
const jsonLine = [...lines2].reverse().find((l) => l[0] === "{");
|
|
@@ -55902,6 +56026,12 @@ Usage:
|
|
|
55902
56026
|
command install-codex wires for the
|
|
55903
56027
|
[mcp_servers.mrrlin-browser] block.
|
|
55904
56028
|
|
|
56029
|
+
mrrlin-mcp external-agents-serve
|
|
56030
|
+
Launch the bundled @mrrlin-dev/external-agents
|
|
56031
|
+
MCP server (multi-LLM dispatch pool). This is the
|
|
56032
|
+
command install-codex wires for the
|
|
56033
|
+
[mcp_servers.external-agents] block.
|
|
56034
|
+
|
|
55905
56035
|
mrrlin-mcp install-codex Idempotently register Mrrlin in
|
|
55906
56036
|
[--force] ~/.codex/config.toml (or CODEX_HOME). Adds an
|
|
55907
56037
|
[mcp_servers.mrrlin] block. Development
|
|
@@ -56057,6 +56187,12 @@ async function main() {
|
|
|
56057
56187
|
process.exitCode = r.status ?? 1;
|
|
56058
56188
|
return;
|
|
56059
56189
|
}
|
|
56190
|
+
case "external-agents-serve": {
|
|
56191
|
+
const { command: command2, args } = externalAgentsServeArgs();
|
|
56192
|
+
const r = (0, import_node_child_process13.spawnSync)(command2, args, { stdio: "inherit", env: process.env });
|
|
56193
|
+
process.exitCode = r.status ?? 1;
|
|
56194
|
+
return;
|
|
56195
|
+
}
|
|
56060
56196
|
case "install-codex": {
|
|
56061
56197
|
const force = rest.includes("--force");
|
|
56062
56198
|
const binPath = resolveSelfBinPath();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.46",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -21,16 +21,17 @@
|
|
|
21
21
|
"@types/ws": "^8.18.1",
|
|
22
22
|
"esbuild": "^0.24.0",
|
|
23
23
|
"tsx": "^4.22.3",
|
|
24
|
+
"@mrrlin/client": "0.0.0",
|
|
24
25
|
"@mrrlin/director-e2e": "0.0.0",
|
|
25
26
|
"@mrrlin/wiki": "0.0.0",
|
|
26
27
|
"@mrrlin/schemas": "0.0.0",
|
|
27
|
-
"@mrrlin/client": "0.0.0",
|
|
28
28
|
"@mrrlin/codex-client": "0.0.0",
|
|
29
29
|
"@mrrlin/tsconfig": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@iarna/toml": "^2.2.5",
|
|
33
33
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
34
|
+
"@mrrlin-dev/external-agents": "^0.33.1",
|
|
34
35
|
"@playwright/mcp": "0.0.76",
|
|
35
36
|
"playwright-core": "1.61.0-alpha-1781023400000",
|
|
36
37
|
"proper-lockfile": "^4.1.2",
|