@mrrlin-dev/mcp 0.4.22 → 0.4.27
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 +304 -46
- package/package.json +3 -3
package/dist/bin.cjs
CHANGED
|
@@ -20815,6 +20815,66 @@ function browserServeArgs(homeDir = import_node_os.default.homedir()) {
|
|
|
20815
20815
|
env: { PLAYWRIGHT_BROWSERS_PATH: browsersPath }
|
|
20816
20816
|
};
|
|
20817
20817
|
}
|
|
20818
|
+
function defaultListProcesses() {
|
|
20819
|
+
const r = (0, import_node_child_process.spawnSync)("ps", ["-ww", "-eo", "pid,ppid,command"], { encoding: "utf8" });
|
|
20820
|
+
const lines2 = (r.stdout ?? "").split("\n").slice(1);
|
|
20821
|
+
const out = [];
|
|
20822
|
+
for (const line of lines2) {
|
|
20823
|
+
const m = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line);
|
|
20824
|
+
if (!m || m[1] === void 0 || m[2] === void 0 || m[3] === void 0) continue;
|
|
20825
|
+
out.push({ pid: Number(m[1]), ppid: Number(m[2]), command: m[3] });
|
|
20826
|
+
}
|
|
20827
|
+
return out;
|
|
20828
|
+
}
|
|
20829
|
+
function defaultIsAlive(pid) {
|
|
20830
|
+
try {
|
|
20831
|
+
process.kill(pid, 0);
|
|
20832
|
+
return true;
|
|
20833
|
+
} catch (err) {
|
|
20834
|
+
return err.code === "EPERM";
|
|
20835
|
+
}
|
|
20836
|
+
}
|
|
20837
|
+
function defaultKill(pid, signal) {
|
|
20838
|
+
try {
|
|
20839
|
+
process.kill(pid, signal);
|
|
20840
|
+
return true;
|
|
20841
|
+
} catch (err) {
|
|
20842
|
+
if (err.code === "ESRCH") return true;
|
|
20843
|
+
return false;
|
|
20844
|
+
}
|
|
20845
|
+
}
|
|
20846
|
+
function safeKill(kill, pid, signal) {
|
|
20847
|
+
try {
|
|
20848
|
+
return kill(pid, signal);
|
|
20849
|
+
} catch {
|
|
20850
|
+
return false;
|
|
20851
|
+
}
|
|
20852
|
+
}
|
|
20853
|
+
function reapOrphanedBrowserProcesses(opts = {}) {
|
|
20854
|
+
const homeDir = opts.homeDir ?? import_node_os.default.homedir();
|
|
20855
|
+
const { profilePath } = browserPaths(homeDir);
|
|
20856
|
+
const listProcesses = opts.listProcesses ?? defaultListProcesses;
|
|
20857
|
+
const isAlive = opts.isAlive ?? defaultIsAlive;
|
|
20858
|
+
const kill = opts.kill ?? defaultKill;
|
|
20859
|
+
const selfPid = opts.selfPid ?? process.pid;
|
|
20860
|
+
const allProcesses = listProcesses();
|
|
20861
|
+
const candidates = allProcesses.filter(
|
|
20862
|
+
(p) => p.pid !== selfPid && p.command.includes("@playwright/mcp") && p.command.includes(profilePath)
|
|
20863
|
+
);
|
|
20864
|
+
const killedPids = [];
|
|
20865
|
+
const killedChildPids = [];
|
|
20866
|
+
for (const proc of candidates) {
|
|
20867
|
+
if (!isAlive(proc.ppid)) {
|
|
20868
|
+
for (const child of allProcesses) {
|
|
20869
|
+
if (child.ppid === proc.pid) {
|
|
20870
|
+
if (safeKill(kill, child.pid, "SIGKILL")) killedChildPids.push(child.pid);
|
|
20871
|
+
}
|
|
20872
|
+
}
|
|
20873
|
+
if (safeKill(kill, proc.pid, "SIGTERM")) killedPids.push(proc.pid);
|
|
20874
|
+
}
|
|
20875
|
+
}
|
|
20876
|
+
return { checked: candidates.length, killedPids, killedChildPids };
|
|
20877
|
+
}
|
|
20818
20878
|
function externalAgentsServeArgs() {
|
|
20819
20879
|
return {
|
|
20820
20880
|
command: process.execPath,
|
|
@@ -21418,14 +21478,23 @@ async function verifyProviderReadiness(provider, run3, opts = {}) {
|
|
|
21418
21478
|
const checks = { cli: false, version: false, auth: false, mcpReachable: false };
|
|
21419
21479
|
const details = [];
|
|
21420
21480
|
let cliResult;
|
|
21481
|
+
let cliUnreachable = false;
|
|
21421
21482
|
try {
|
|
21422
21483
|
cliResult = run3(["--version"]);
|
|
21423
21484
|
checks.cli = cliResult.status === 0;
|
|
21485
|
+
if (!checks.cli) {
|
|
21486
|
+
cliUnreachable = cliResult.status === null;
|
|
21487
|
+
details.push(
|
|
21488
|
+
cliUnreachable ? `cli: \`${provider}\` binary not found on PATH` : `cli: \`${provider} --version\` exited with code ${cliResult.status}`
|
|
21489
|
+
);
|
|
21490
|
+
}
|
|
21424
21491
|
} catch {
|
|
21492
|
+
cliUnreachable = true;
|
|
21493
|
+
details.push(`cli: \`${provider}\` binary not found on PATH`);
|
|
21425
21494
|
}
|
|
21426
|
-
if (!checks.cli) details.push("cli: binary not available or exited non-zero");
|
|
21427
21495
|
if (provider === "codex") {
|
|
21428
21496
|
checks.version = true;
|
|
21497
|
+
} else if (cliUnreachable) {
|
|
21429
21498
|
} else {
|
|
21430
21499
|
const raw = (cliResult?.stdout || cliResult?.stderr) ?? "";
|
|
21431
21500
|
const version2 = parseCodexVersion(raw);
|
|
@@ -21440,6 +21509,7 @@ async function verifyProviderReadiness(provider, run3, opts = {}) {
|
|
|
21440
21509
|
} catch {
|
|
21441
21510
|
details.push(`auth: ${authPath} not found`);
|
|
21442
21511
|
}
|
|
21512
|
+
} else if (cliUnreachable) {
|
|
21443
21513
|
} else {
|
|
21444
21514
|
try {
|
|
21445
21515
|
const result = run3(["auth", "status"]);
|
|
@@ -21456,6 +21526,8 @@ async function verifyProviderReadiness(provider, run3, opts = {}) {
|
|
|
21456
21526
|
checks.mcpReachable = isExternalAgentsWrapper(parsed.mcp_servers?.["external-agents"]);
|
|
21457
21527
|
} catch {
|
|
21458
21528
|
}
|
|
21529
|
+
if (!checks.mcpReachable) details.push("mcpReachable: external-agents server entry missing or invalid");
|
|
21530
|
+
} else if (cliUnreachable) {
|
|
21459
21531
|
} else {
|
|
21460
21532
|
const configPath = resolveClaudeConfigPath2(opts);
|
|
21461
21533
|
try {
|
|
@@ -21464,8 +21536,8 @@ async function verifyProviderReadiness(provider, run3, opts = {}) {
|
|
|
21464
21536
|
checks.mcpReachable = isExternalAgentsWrapper(parsed.mcpServers?.["external-agents"]);
|
|
21465
21537
|
} catch {
|
|
21466
21538
|
}
|
|
21539
|
+
if (!checks.mcpReachable) details.push("mcpReachable: external-agents server entry missing or invalid");
|
|
21467
21540
|
}
|
|
21468
|
-
if (!checks.mcpReachable) details.push("mcpReachable: external-agents server entry missing or invalid");
|
|
21469
21541
|
const ready = checks.cli && checks.version && checks.auth && checks.mcpReachable;
|
|
21470
21542
|
return { ready, checks, details };
|
|
21471
21543
|
}
|
|
@@ -37017,7 +37089,10 @@ var ASYNC_JOB_FAILURE_REASONS = [
|
|
|
37017
37089
|
"provider_unavailable",
|
|
37018
37090
|
"spec_changed",
|
|
37019
37091
|
"stale_lease",
|
|
37020
|
-
"consensus_blocked"
|
|
37092
|
+
"consensus_blocked",
|
|
37093
|
+
// async-flow-throughput Slice 1: the spec drafter rejected its own output for exceeding the
|
|
37094
|
+
// per-impact acceptance-criteria / character budget. Retryable via the normal async-job path.
|
|
37095
|
+
"spec_over_budget"
|
|
37021
37096
|
];
|
|
37022
37097
|
var mrrlinAsyncJobStatusSchema = external_exports.enum(ASYNC_JOB_STATUSES);
|
|
37023
37098
|
var mrrlinAsyncJobFailureReasonSchema = external_exports.enum(ASYNC_JOB_FAILURE_REASONS);
|
|
@@ -41218,6 +41293,82 @@ function describeError(error51) {
|
|
|
41218
41293
|
const detail = detailFromCause(error51.cause);
|
|
41219
41294
|
return detail ? `${error51.message} (${truncate(detail)})` : error51.message;
|
|
41220
41295
|
}
|
|
41296
|
+
function errorKind(error51) {
|
|
41297
|
+
if (!(error51 instanceof Error)) return null;
|
|
41298
|
+
const cause = error51.cause;
|
|
41299
|
+
if (!cause || typeof cause !== "object") return null;
|
|
41300
|
+
const direct = cause.code;
|
|
41301
|
+
if (typeof direct === "string") return direct;
|
|
41302
|
+
const errors = cause.errors;
|
|
41303
|
+
if (Array.isArray(errors) && errors.length > 0) {
|
|
41304
|
+
const first = errors[0]?.code;
|
|
41305
|
+
if (typeof first === "string") return first;
|
|
41306
|
+
}
|
|
41307
|
+
return null;
|
|
41308
|
+
}
|
|
41309
|
+
var DEFAULT_FAILURE_LOG_WINDOW_SECONDS = 60;
|
|
41310
|
+
function failureLogWindowMs(env = process.env) {
|
|
41311
|
+
const raw = (env.MRRLIN_BRIDGE_FAILURE_LOG_WINDOW_SECONDS ?? "").trim();
|
|
41312
|
+
const parsed = Number.parseInt(raw, 10);
|
|
41313
|
+
const seconds = Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_FAILURE_LOG_WINDOW_SECONDS;
|
|
41314
|
+
return seconds * 1e3;
|
|
41315
|
+
}
|
|
41316
|
+
function createFailureLog(opts = {}) {
|
|
41317
|
+
const windowMs = opts.windowMs ?? failureLogWindowMs();
|
|
41318
|
+
const now = opts.now ?? Date.now;
|
|
41319
|
+
const lastEmit = /* @__PURE__ */ new Map();
|
|
41320
|
+
const counts = /* @__PURE__ */ new Map();
|
|
41321
|
+
let windowStartedAt = null;
|
|
41322
|
+
return {
|
|
41323
|
+
/** Decide whether this failure should be printed. Returns the suppressed count to include in
|
|
41324
|
+
* the line when it is. */
|
|
41325
|
+
shouldEmit(operation, kind) {
|
|
41326
|
+
const prev = lastEmit.get(operation);
|
|
41327
|
+
const sameKind = prev != null && prev.kind === kind;
|
|
41328
|
+
const withinWindow = prev != null && now() - prev.at < windowMs;
|
|
41329
|
+
if (sameKind && withinWindow) {
|
|
41330
|
+
prev.suppressed += 1;
|
|
41331
|
+
return { emit: false, suppressed: prev.suppressed };
|
|
41332
|
+
}
|
|
41333
|
+
const suppressed = sameKind ? prev?.suppressed ?? 0 : 0;
|
|
41334
|
+
lastEmit.set(operation, { kind, at: now(), suppressed: 0 });
|
|
41335
|
+
return { emit: true, suppressed };
|
|
41336
|
+
},
|
|
41337
|
+
/** A success resets the operation's suppression state, so the next failure always prints. */
|
|
41338
|
+
noteSuccess(operation) {
|
|
41339
|
+
lastEmit.delete(operation);
|
|
41340
|
+
},
|
|
41341
|
+
/** Count a failure for the degraded report. Only the breaker-tracked operations are counted. */
|
|
41342
|
+
recordFailure(operation, kind) {
|
|
41343
|
+
if (windowStartedAt == null) windowStartedAt = now();
|
|
41344
|
+
const entry = counts.get(operation) ?? { failures: 0, lastErrorKind: null };
|
|
41345
|
+
entry.failures += 1;
|
|
41346
|
+
entry.lastErrorKind = kind;
|
|
41347
|
+
counts.set(operation, entry);
|
|
41348
|
+
},
|
|
41349
|
+
/** Snapshot the window and start a fresh one. Always returns all three operations, reporting
|
|
41350
|
+
* zeros for a clean window — it never fabricates a degraded state and never omits the key. */
|
|
41351
|
+
drainWindow() {
|
|
41352
|
+
const startedAt = windowStartedAt;
|
|
41353
|
+
const report = {
|
|
41354
|
+
windowStartedAt: startedAt == null ? null : new Date(startedAt).toISOString(),
|
|
41355
|
+
operations: {
|
|
41356
|
+
schedulerTick: counts.get("schedulerTick") ?? { failures: 0, lastErrorKind: null },
|
|
41357
|
+
claimSweep: counts.get("claimSweep") ?? { failures: 0, lastErrorKind: null },
|
|
41358
|
+
asyncSweep: counts.get("asyncSweep") ?? { failures: 0, lastErrorKind: null }
|
|
41359
|
+
}
|
|
41360
|
+
};
|
|
41361
|
+
counts.clear();
|
|
41362
|
+
windowStartedAt = null;
|
|
41363
|
+
return report;
|
|
41364
|
+
}
|
|
41365
|
+
};
|
|
41366
|
+
}
|
|
41367
|
+
function failureLogLine(label, error51, suppressed) {
|
|
41368
|
+
const tail2 = suppressed > 0 ? ` (${suppressed} identical suppressed since the last line)` : "";
|
|
41369
|
+
return `[mrrlin-mcp director-bridge] ${label} failed: ${describeError(error51)}${tail2}
|
|
41370
|
+
`;
|
|
41371
|
+
}
|
|
41221
41372
|
|
|
41222
41373
|
// src/token-refresher.ts
|
|
41223
41374
|
function createTokenRefresher(opts) {
|
|
@@ -41232,12 +41383,19 @@ function createTokenRefresher(opts) {
|
|
|
41232
41383
|
opts.persist(token);
|
|
41233
41384
|
last = token;
|
|
41234
41385
|
}
|
|
41386
|
+
opts.failureLog?.noteSuccess("tokenRefresh");
|
|
41235
41387
|
opts.onOk?.();
|
|
41236
41388
|
} catch (error51) {
|
|
41237
|
-
|
|
41238
|
-
|
|
41389
|
+
const label = "token refresh (will retry next tick)";
|
|
41390
|
+
if (opts.failureLog) {
|
|
41391
|
+
const { emit, suppressed } = opts.failureLog.shouldEmit("tokenRefresh", errorKind(error51));
|
|
41392
|
+
if (emit) process.stderr.write(failureLogLine(label, error51, suppressed));
|
|
41393
|
+
} else {
|
|
41394
|
+
process.stderr.write(
|
|
41395
|
+
`[mrrlin-mcp director-bridge] token refresh failed (will retry next tick): ${describeError(error51)}
|
|
41239
41396
|
`
|
|
41240
|
-
|
|
41397
|
+
);
|
|
41398
|
+
}
|
|
41241
41399
|
opts.onError?.(error51);
|
|
41242
41400
|
}
|
|
41243
41401
|
}
|
|
@@ -41664,7 +41822,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
41664
41822
|
}
|
|
41665
41823
|
|
|
41666
41824
|
// src/_generated/version.ts
|
|
41667
|
-
var PKG_VERSION = "0.4.
|
|
41825
|
+
var PKG_VERSION = "0.4.27";
|
|
41668
41826
|
|
|
41669
41827
|
// src/api-base-url-migrate.ts
|
|
41670
41828
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
@@ -42120,6 +42278,32 @@ function computeNextTickDelayMs(nextWakeAt, nowMs, opts = {}) {
|
|
|
42120
42278
|
function count(value) {
|
|
42121
42279
|
return Array.isArray(value) ? value.length : 0;
|
|
42122
42280
|
}
|
|
42281
|
+
var KNOWN_STOP_REASONS = new Set(mrrlinStopReasonSchema.options);
|
|
42282
|
+
function tallyReasons(value) {
|
|
42283
|
+
const out = {};
|
|
42284
|
+
if (!Array.isArray(value)) return out;
|
|
42285
|
+
for (const entry of value) {
|
|
42286
|
+
const raw = entry?.reason;
|
|
42287
|
+
const trimmed = typeof raw === "string" ? raw.trim() : "";
|
|
42288
|
+
const key = KNOWN_STOP_REASONS.has(trimmed) ? trimmed : "unknown";
|
|
42289
|
+
out[key] = (out[key] ?? 0) + 1;
|
|
42290
|
+
}
|
|
42291
|
+
return out;
|
|
42292
|
+
}
|
|
42293
|
+
function mergeReasons(parts) {
|
|
42294
|
+
const out = {};
|
|
42295
|
+
for (const part of parts) {
|
|
42296
|
+
for (const [key, n] of Object.entries(part)) out[key] = (out[key] ?? 0) + n;
|
|
42297
|
+
}
|
|
42298
|
+
return out;
|
|
42299
|
+
}
|
|
42300
|
+
function schedulerTickFailureLine(deps, error51) {
|
|
42301
|
+
const kind = errorKind(error51);
|
|
42302
|
+
deps.failureLog?.recordFailure("schedulerTick", kind);
|
|
42303
|
+
if (!deps.failureLog) return failureLogLine("scheduler tick", error51, 0);
|
|
42304
|
+
const { emit, suppressed } = deps.failureLog.shouldEmit("schedulerTick", kind);
|
|
42305
|
+
return emit ? failureLogLine("scheduler tick", error51, suppressed) : "";
|
|
42306
|
+
}
|
|
42123
42307
|
function dispatchTickWork(deps, results) {
|
|
42124
42308
|
if (!deps.dispatchEnqueued) return;
|
|
42125
42309
|
for (const result of results) {
|
|
@@ -42150,8 +42334,7 @@ async function attemptSchedulerTick(deps) {
|
|
|
42150
42334
|
const { response, parsedBody } = bridgeHealth ? await withTimeout(parseTick) : await parseTick(new AbortController().signal);
|
|
42151
42335
|
if (!response.ok) {
|
|
42152
42336
|
process.stderr.write(
|
|
42153
|
-
|
|
42154
|
-
`
|
|
42337
|
+
schedulerTickFailureLine(deps, new Error(`HTTP ${response.status} from /internal/scheduler/run`))
|
|
42155
42338
|
);
|
|
42156
42339
|
bridgeHealth?.recordFailure("schedulerTick", new Error(`HTTP ${response.status}`));
|
|
42157
42340
|
return { nextWakeAt: null, failed: true };
|
|
@@ -42174,6 +42357,9 @@ async function attemptSchedulerTick(deps) {
|
|
|
42174
42357
|
enqueuedSpecDrafts: results.reduce((sum, r) => sum + count(r.enqueuedSpecDrafts), 0),
|
|
42175
42358
|
autoApprovedSpecs: results.reduce((sum, r) => sum + count(r.autoApprovedSpecs), 0),
|
|
42176
42359
|
skippedTasks: results.reduce((sum, r) => sum + count(r.skippedTasks), 0),
|
|
42360
|
+
// Slice 4: always present. A tick with no skips emits `{}`, never an absent key and never
|
|
42361
|
+
// null, so a consumer never has to distinguish "no skips" from "this bridge is too old".
|
|
42362
|
+
skippedReasons: mergeReasons(results.map((r) => tallyReasons(r.skippedTasks))),
|
|
42177
42363
|
failures: results.reduce((sum, r) => sum + count(r.failures), 0)
|
|
42178
42364
|
},
|
|
42179
42365
|
perProject: results.map((r) => ({
|
|
@@ -42184,18 +42370,31 @@ async function attemptSchedulerTick(deps) {
|
|
|
42184
42370
|
enqueuedSpecDrafts: count(r.enqueuedSpecDrafts),
|
|
42185
42371
|
autoApprovedSpecs: count(r.autoApprovedSpecs),
|
|
42186
42372
|
skippedTasks: count(r.skippedTasks),
|
|
42373
|
+
skippedReasons: tallyReasons(r.skippedTasks),
|
|
42187
42374
|
failures: count(r.failures)
|
|
42188
42375
|
})),
|
|
42189
|
-
nextWakeAt: typeof data.nextWakeAt === "string" ? data.nextWakeAt : null
|
|
42376
|
+
nextWakeAt: typeof data.nextWakeAt === "string" ? data.nextWakeAt : null,
|
|
42377
|
+
// Slice 3: partial degradation had no signal — the breaker only trips on TOTAL failure, so
|
|
42378
|
+
// ~2 800 connect timeouts a day were invisible outside raw stderr. This rides a payload the
|
|
42379
|
+
// operator already reads. Always present, always all three tracked operations, zeros for a
|
|
42380
|
+
// clean window: it never fabricates a degraded state and never omits the key.
|
|
42381
|
+
degraded: deps.failureLog?.drainWindow() ?? {
|
|
42382
|
+
windowStartedAt: null,
|
|
42383
|
+
operations: {
|
|
42384
|
+
schedulerTick: { failures: 0, lastErrorKind: null },
|
|
42385
|
+
claimSweep: { failures: 0, lastErrorKind: null },
|
|
42386
|
+
asyncSweep: { failures: 0, lastErrorKind: null }
|
|
42387
|
+
}
|
|
42388
|
+
}
|
|
42190
42389
|
}
|
|
42191
42390
|
});
|
|
42192
42391
|
dispatchTickWork(deps, results);
|
|
42392
|
+
deps.failureLog?.noteSuccess("schedulerTick");
|
|
42193
42393
|
await bridgeHealth?.recordSuccess();
|
|
42194
42394
|
return { nextWakeAt: typeof data.nextWakeAt === "string" ? data.nextWakeAt : null, failed: false };
|
|
42195
42395
|
} catch (error51) {
|
|
42196
42396
|
process.stderr.write(
|
|
42197
|
-
|
|
42198
|
-
`
|
|
42397
|
+
schedulerTickFailureLine(deps, error51)
|
|
42199
42398
|
);
|
|
42200
42399
|
bridgeHealth?.recordFailure("schedulerTick", error51);
|
|
42201
42400
|
return { nextWakeAt: null, failed: true };
|
|
@@ -43033,6 +43232,16 @@ function parseAcceptanceCriteria(markdown) {
|
|
|
43033
43232
|
}
|
|
43034
43233
|
return items.length > 0 ? { items } : null;
|
|
43035
43234
|
}
|
|
43235
|
+
function resolveSpecBudget(impact) {
|
|
43236
|
+
switch (impact?.toLowerCase()) {
|
|
43237
|
+
case "high":
|
|
43238
|
+
return { maxRequirements: 12, maxChars: 25e3 };
|
|
43239
|
+
case "medium":
|
|
43240
|
+
return { maxRequirements: 6, maxChars: 12e3 };
|
|
43241
|
+
default:
|
|
43242
|
+
return { maxRequirements: 3, maxChars: 6e3 };
|
|
43243
|
+
}
|
|
43244
|
+
}
|
|
43036
43245
|
|
|
43037
43246
|
// src/consensus/spec-gate.ts
|
|
43038
43247
|
var SEED_ABSENCE_QUESTION = "What are the requirements for this task? There is no operator request on record, so the spec cannot be validated against your intent \u2014 please state what this task must do.";
|
|
@@ -43911,7 +44120,7 @@ function renderCriteriaBlock(criteria) {
|
|
|
43911
44120
|
return criteria.map((c, i) => `${i + 1}. [ ] ${c}`).join("\n");
|
|
43912
44121
|
}
|
|
43913
44122
|
async function buildDeliverableGateDeps(args) {
|
|
43914
|
-
const { client, projectSlug, deliverablePageId,
|
|
44123
|
+
const { client, projectSlug, deliverablePageId, attemptOutcome, cwd, codexExecutable, runId, seat } = args;
|
|
43915
44124
|
const rounds = Number(process.env["MRRLIN_CONSENSUS_ROUNDS"] ?? 5);
|
|
43916
44125
|
const timeoutMs = Number(process.env["MRRLIN_CONSENSUS_HEAD_TIMEOUT_MS"] ?? 15e4);
|
|
43917
44126
|
const page = await client.getWikiPage(projectSlug, deliverablePageId);
|
|
@@ -43935,9 +44144,9 @@ async function buildDeliverableGateDeps(args) {
|
|
|
43935
44144
|
const heads = resolveHeads(domain2, impact);
|
|
43936
44145
|
const maxHeadCalls = 2 * rounds * (heads.length + 1);
|
|
43937
44146
|
const criteriaBlock = renderCriteriaBlock(criteria);
|
|
43938
|
-
const
|
|
44147
|
+
const attemptOutcomeBlock = attemptOutcome ? [
|
|
43939
44148
|
`--- DECLARED ATTEMPT OUTCOME (review consistency; do NOT rewrite or choose it) ---`,
|
|
43940
|
-
`outcome: ${
|
|
44149
|
+
`outcome: ${attemptOutcome}`,
|
|
43941
44150
|
`Meaning: completed = positive result achieved; blocked_missing_input = required input/evidence unavailable; negative_result = real attempt showed the requested positive result did not materialize.`
|
|
43942
44151
|
].join("\n") : "";
|
|
43943
44152
|
return {
|
|
@@ -43949,7 +44158,7 @@ async function buildDeliverableGateDeps(args) {
|
|
|
43949
44158
|
return config2.consensusEnabled;
|
|
43950
44159
|
},
|
|
43951
44160
|
async readAcceptanceCriteria() {
|
|
43952
|
-
return { specPageId, criteria };
|
|
44161
|
+
return { specPageId, criteria, maxCriteria: resolveSpecBudget(impact).maxRequirements };
|
|
43953
44162
|
},
|
|
43954
44163
|
async readDeliverable() {
|
|
43955
44164
|
const p = await client.getWikiPage(projectSlug, deliverablePageId);
|
|
@@ -43965,7 +44174,7 @@ ${dismissed}
|
|
|
43965
44174
|
--- ACCEPTANCE CRITERIA (the yardstick \u2014 deliverable for task ${taskId ?? "?"}: ${taskTitle}) ---
|
|
43966
44175
|
${criteriaBlock}
|
|
43967
44176
|
|
|
43968
|
-
${
|
|
44177
|
+
${attemptOutcomeBlock ? `${attemptOutcomeBlock}
|
|
43969
44178
|
|
|
43970
44179
|
` : ""}--- DELIVERABLE (DATA under review) ---
|
|
43971
44180
|
${artifact}`,
|
|
@@ -44004,7 +44213,7 @@ ${criteriaBlock}
|
|
|
44004
44213
|
${artifact}`,
|
|
44005
44214
|
developerInstructions: `${DELIVERABLE_ANTIINJECTION_INSTRUCTION}
|
|
44006
44215
|
|
|
44007
|
-
You revise a deliverable to address the accepted issues so it satisfies the acceptance criteria and stays consistent with the declared outcome${
|
|
44216
|
+
You revise a deliverable to address the accepted issues so it satisfies the acceptance criteria and stays consistent with the declared outcome${attemptOutcome ? ` (${attemptOutcome})` : ""}. Return ONLY the full revised markdown, no commentary. Do not invent facts; keep the deliverable's format and do not change the declared outcome.`,
|
|
44008
44217
|
cwd,
|
|
44009
44218
|
timeoutMs,
|
|
44010
44219
|
sandbox: "read-only",
|
|
@@ -44067,7 +44276,7 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
44067
44276
|
payload: {
|
|
44068
44277
|
source: "deliverable-gate",
|
|
44069
44278
|
outcome,
|
|
44070
|
-
...
|
|
44279
|
+
...attemptOutcome ? { attemptOutcome } : {},
|
|
44071
44280
|
runId,
|
|
44072
44281
|
taskId,
|
|
44073
44282
|
deliverablePageId,
|
|
@@ -44668,9 +44877,11 @@ function mapDeliverableGateResult(result) {
|
|
|
44668
44877
|
reason: "reviewers were unavailable (provider/quota) during the deliverable review (CONSENSUS_UNAVAILABLE)"
|
|
44669
44878
|
};
|
|
44670
44879
|
case "CONSENSUS_BLOCKED": {
|
|
44880
|
+
const over = result.yardstickOverBudget;
|
|
44881
|
+
const yardstick = over ? ` The yardstick is over budget: spec ${over.specPageId ?? "(unknown page)"} carries ${over.criteria} acceptance criteria against a maximum of ${over.maxCriteria} for this task's impact, so re-draft the spec rather than re-running the work.` : "";
|
|
44671
44882
|
return {
|
|
44672
44883
|
action: "handoff",
|
|
44673
|
-
reason: `reviewers did not converge; ${formatDeliverableGateDiagnostics(result.diagnostics ?? [])} (CONSENSUS_BLOCKED)`
|
|
44884
|
+
reason: `reviewers did not converge; ${formatDeliverableGateDiagnostics(result.diagnostics ?? [])} (CONSENSUS_BLOCKED)${yardstick}`
|
|
44674
44885
|
};
|
|
44675
44886
|
}
|
|
44676
44887
|
case "MISSING_ACCEPTANCE_CRITERIA":
|
|
@@ -44696,17 +44907,6 @@ var SPECS_FOLDER = "Specs";
|
|
|
44696
44907
|
var DRAFT_SPEC_TLDR_MAX_CHARS = 300;
|
|
44697
44908
|
var DRAFT_SPEC_PERSONA = "spec-author";
|
|
44698
44909
|
var PAGE_ID_MAX_ATTEMPTS = 5;
|
|
44699
|
-
function resolveSpecBudget(impact) {
|
|
44700
|
-
const tier = impact?.toLowerCase();
|
|
44701
|
-
switch (tier) {
|
|
44702
|
-
case "high":
|
|
44703
|
-
return { maxRequirements: 12, maxChars: 25e3 };
|
|
44704
|
-
case "medium":
|
|
44705
|
-
return { maxRequirements: 6, maxChars: 12e3 };
|
|
44706
|
-
default:
|
|
44707
|
-
return { maxRequirements: 3, maxChars: 6e3 };
|
|
44708
|
-
}
|
|
44709
|
-
}
|
|
44710
44910
|
function defaultSleep(ms) {
|
|
44711
44911
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
44712
44912
|
}
|
|
@@ -44732,6 +44932,25 @@ ${body}`;
|
|
|
44732
44932
|
}
|
|
44733
44933
|
return { markdown: body, tldrChars };
|
|
44734
44934
|
}
|
|
44935
|
+
function checkSpecBudget(markdown, impact) {
|
|
44936
|
+
const budget = resolveSpecBudget(impact);
|
|
44937
|
+
const parsed = parseAcceptanceCriteria(markdown);
|
|
44938
|
+
const criteria = parsed?.items.length ?? 0;
|
|
44939
|
+
const chars = markdown.length;
|
|
44940
|
+
const overCriteria = parsed != null && criteria > budget.maxRequirements;
|
|
44941
|
+
const overChars = chars > budget.maxChars;
|
|
44942
|
+
if (!overCriteria && !overChars) return null;
|
|
44943
|
+
const faults = [];
|
|
44944
|
+
if (overCriteria) faults.push(`${criteria} acceptance-criteria items (maximum ${budget.maxRequirements})`);
|
|
44945
|
+
if (overChars) faults.push(`${chars} characters (maximum ${budget.maxChars})`);
|
|
44946
|
+
return {
|
|
44947
|
+
criteria,
|
|
44948
|
+
chars,
|
|
44949
|
+
maxCriteria: budget.maxRequirements,
|
|
44950
|
+
maxChars: budget.maxChars,
|
|
44951
|
+
message: `spec draft is over budget: ${faults.join(" and ")}`
|
|
44952
|
+
};
|
|
44953
|
+
}
|
|
44735
44954
|
function extractDeployTarget(markdown) {
|
|
44736
44955
|
const section = /(## Deploy Target\s*\n)([\s\S]*?)(?=\n## |$)/i.exec(markdown);
|
|
44737
44956
|
if (!section || section.index === void 0) {
|
|
@@ -44999,6 +45218,10 @@ async function runDraftSpec(ctx, input, deps) {
|
|
|
44999
45218
|
}
|
|
45000
45219
|
const { markdown: specMarkdown, deployTarget: parsedDeployTarget } = extractDeployTarget(exec.text);
|
|
45001
45220
|
const { markdown, tldrChars } = enforceSpecOutputContract(specMarkdown, taskId, task.title);
|
|
45221
|
+
const overBudget = checkSpecBudget(markdown, task.impact);
|
|
45222
|
+
if (overBudget) {
|
|
45223
|
+
return { kind: "retry", failureReason: "spec_over_budget", error: overBudget.message };
|
|
45224
|
+
}
|
|
45002
45225
|
let pageId;
|
|
45003
45226
|
if (adoptedId) {
|
|
45004
45227
|
try {
|
|
@@ -47675,7 +47898,7 @@ async function driveNonCodeRun(deps, projectSlug, runId, leaseId, task, domain2)
|
|
|
47675
47898
|
taskId: task.id,
|
|
47676
47899
|
runId,
|
|
47677
47900
|
deliverablePageId: pageId,
|
|
47678
|
-
|
|
47901
|
+
attemptOutcome: parsedResult.outcome
|
|
47679
47902
|
});
|
|
47680
47903
|
} catch (error51) {
|
|
47681
47904
|
await failRun(
|
|
@@ -48535,6 +48758,7 @@ async function runDeliverableGate(target, deps) {
|
|
|
48535
48758
|
const deliverable = await deps.readDeliverable();
|
|
48536
48759
|
let currentHash = deliverable.contentHash;
|
|
48537
48760
|
let conflicted = false;
|
|
48761
|
+
const yardstickOverBudget = ac.maxCriteria != null && ac.criteria.length > ac.maxCriteria ? { specPageId: ac.specPageId, criteria: ac.criteria.length, maxCriteria: ac.maxCriteria } : void 0;
|
|
48538
48762
|
const result = await runConsensus({
|
|
48539
48763
|
artifact: deliverable.markdownBody,
|
|
48540
48764
|
heads: deps.heads,
|
|
@@ -48576,7 +48800,7 @@ async function runDeliverableGate(target, deps) {
|
|
|
48576
48800
|
}
|
|
48577
48801
|
await deps.writeEvidence({
|
|
48578
48802
|
deliverablePageId: deliverable.pageId,
|
|
48579
|
-
...target.
|
|
48803
|
+
...target.attemptOutcome ? { attemptOutcome: target.attemptOutcome } : {},
|
|
48580
48804
|
outcome: result.outcome,
|
|
48581
48805
|
rounds: result.rounds,
|
|
48582
48806
|
history: result.history,
|
|
@@ -48588,7 +48812,10 @@ async function runDeliverableGate(target, deps) {
|
|
|
48588
48812
|
outcome: "CONSENSUS_BLOCKED",
|
|
48589
48813
|
rounds: result.rounds,
|
|
48590
48814
|
diagnostics: diagnostics ?? [],
|
|
48591
|
-
...result.escalation ? { escalation: result.escalation } : {}
|
|
48815
|
+
...result.escalation ? { escalation: result.escalation } : {},
|
|
48816
|
+
// Slice 2: attached ONLY on the blocked path. A converged review needs no explanation, and a
|
|
48817
|
+
// transient outcome (changed-during-review, reviewers unavailable) is not about the yardstick.
|
|
48818
|
+
...yardstickOverBudget ? { yardstickOverBudget } : {}
|
|
48592
48819
|
};
|
|
48593
48820
|
}
|
|
48594
48821
|
if (result.outcome === "CONVERGED") {
|
|
@@ -54052,6 +54279,7 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
54052
54279
|
"- When creating a task, classify and supply the `domain` field explicitly based on the nature of the work: pass `domain:'code'` for software engineering work, `domain:'seo'` for SEO/optimization, `domain:'marketing'` for marketing/growth, or `domain:'content'` for content creation/writing \u2014 the same way you select category/subcategory. A follow-up task spawned with `parentTaskId` inherits its parent's domain automatically and requires no explicit domain.",
|
|
54053
54280
|
'- When a task needs a browser (login flows, UI verification/clicks, scraping), pass `prerequisites:[{mcp:"mrrlin-browser",reason:"..."}]` to `create_execution_run`. If that run later fails into a handoff, the operator gets a one-click "Launch browser & resume" that brings up the dedicated browser as the turn starts.',
|
|
54054
54281
|
"- When you actually drive a browser (calling `browser_navigate`, `browser_click`, `browser_snapshot`, `browser_type`, etc.), invoke those tools from the **`mrrlin-browser`** MCP server ONLY. Do NOT use tools from any other browser MCP (`playwright`, `chrome-devtools`, `puppeteer`, etc.) even if they are also connected on this operator's box \u2014 those use a shared/default Playwright profile that is fragile across runs. Only `mrrlin-browser` uses this project's dedicated Chromium + persistent operator profile (`~/.mrrlin/browser/profile`). If your host exposes multiple browser MCPs with overlapping tool names, prefer the `mrrlin-browser` server unconditionally.",
|
|
54282
|
+
"- Exactly one browser session exists for this operator, shared across every concurrent chat and task on their box \u2014 a deliberate tradeoff, not a bug, so a second concurrent user of the browser is expected, not exceptional. If a `mrrlin-browser` call fails with `Browser is already in use for ...`, that means another one of the operator's own chats or tasks is driving the browser RIGHT NOW. Do not present this as a broken tool or a mysterious error: tell the operator plainly that the browser is busy with another task, ask them to wait for it to finish (or close it), and offer to retry shortly. Never retry-loop this silently.",
|
|
54055
54283
|
"",
|
|
54056
54284
|
'Research playbook \u2014 when the user asks about a topic, an area, or "what do we have on X":',
|
|
54057
54285
|
"- Tasks are the source of truth. Start with `list_tasks` using `q=<keyword>`; the filter is a",
|
|
@@ -54146,6 +54374,14 @@ function buildThreadStartParams(msg, config2, desiredModel) {
|
|
|
54146
54374
|
"- A failed fetch is a signal to change tool, never a finding. NEVER report that you could not",
|
|
54147
54375
|
" check, verify, or reach a publicly reachable target until you have actually tried the",
|
|
54148
54376
|
" browser on it in this turn.",
|
|
54377
|
+
"- An internal Mrrlin route (`/deliverables/...`, `/wiki/...`, `/specs/...`, `/items/...`,",
|
|
54378
|
+
" `/memory/...`) is NEVER a browser target, even when it appears as a literal URL/path in a",
|
|
54379
|
+
" message or a screenshot. Resolve it through the typed tools instead: `list_wiki_pages` (by",
|
|
54380
|
+
" `folder`/`q`) or `search_wiki_pages` to find the page, then `get_wiki_page` for its content;",
|
|
54381
|
+
" `get_task`/`list_tasks` for `/items/<id>`. Opening these in `mrrlin-browser` is slower, can",
|
|
54382
|
+
" hit an operator login/consent wall the automation cannot clear on its own, and needlessly",
|
|
54383
|
+
" occupies the one shared browser session. Only reach for the browser on a Mrrlin route after a",
|
|
54384
|
+
" typed tool call for it has actually failed.",
|
|
54149
54385
|
"",
|
|
54150
54386
|
"Initiative contract \u2014 do the obvious next step instead of offering it:",
|
|
54151
54387
|
"- When a next step is cheap, clearly wanted, and finishable in this turn, just do it and",
|
|
@@ -55796,6 +56032,11 @@ function checkTelemetryDefaultProject(env, warn) {
|
|
|
55796
56032
|
}
|
|
55797
56033
|
async function startDirectorBridge() {
|
|
55798
56034
|
const config2 = parseConfig();
|
|
56035
|
+
const failureLog = createFailureLog();
|
|
56036
|
+
const suppressedPushLine = (label, error51) => {
|
|
56037
|
+
const { emit, suppressed } = failureLog.shouldEmit(label, errorKind(error51));
|
|
56038
|
+
return emit ? failureLogLine(label, error51, suppressed) : "";
|
|
56039
|
+
};
|
|
55799
56040
|
let telemetryPipelineRunning = false;
|
|
55800
56041
|
checkTelemetryDefaultProject(process.env, (m) => console.error(m));
|
|
55801
56042
|
const turnLock = createTurnLock();
|
|
@@ -55880,6 +56121,7 @@ async function startDirectorBridge() {
|
|
|
55880
56121
|
provider: tokenProvider,
|
|
55881
56122
|
persist: persistOperatorToken,
|
|
55882
56123
|
intervalMs: tokenRefreshIntervalMs(),
|
|
56124
|
+
failureLog,
|
|
55883
56125
|
onOk: () => health.setApiAuth("ok"),
|
|
55884
56126
|
onError: (e) => {
|
|
55885
56127
|
if (e instanceof AgentTokenProviderError && e.code === "REVOKED_OR_EXPIRED") {
|
|
@@ -56010,17 +56252,17 @@ async function startDirectorBridge() {
|
|
|
56010
56252
|
// Builds the gate deps (resolving domain/heads/criteria from the page's task + spec) and runs the
|
|
56011
56253
|
// same convergence loop the spec-gate uses. runId is threaded for verdict provenance (decision 10).
|
|
56012
56254
|
// Authoring uses the defaults (runAgentExec + loadPersona) — no extra wiring needed for those.
|
|
56013
|
-
runDeliverableGate: async ({ projectSlug, runId, deliverablePageId,
|
|
56255
|
+
runDeliverableGate: async ({ projectSlug, runId, deliverablePageId, attemptOutcome }) => {
|
|
56014
56256
|
const gateDeps = await buildDeliverableGateDeps({
|
|
56015
56257
|
client,
|
|
56016
56258
|
projectSlug,
|
|
56017
56259
|
deliverablePageId,
|
|
56018
|
-
|
|
56260
|
+
attemptOutcome,
|
|
56019
56261
|
cwd: config2.codexCwd ?? process.cwd(),
|
|
56020
56262
|
codexExecutable: config2.codexExecutable,
|
|
56021
56263
|
runId
|
|
56022
56264
|
});
|
|
56023
|
-
return runDeliverableGate({ projectSlug, deliverablePageId,
|
|
56265
|
+
return runDeliverableGate({ projectSlug, deliverablePageId, attemptOutcome }, gateDeps);
|
|
56024
56266
|
}
|
|
56025
56267
|
};
|
|
56026
56268
|
const sessionSinks = createSessionSinkRegistry();
|
|
@@ -56402,12 +56644,13 @@ async function startDirectorBridge() {
|
|
|
56402
56644
|
const runTrackedSweep = async (id, label, fn) => {
|
|
56403
56645
|
try {
|
|
56404
56646
|
await fn();
|
|
56647
|
+
failureLog.noteSuccess(id);
|
|
56405
56648
|
await bridgeHealth.recordSuccess();
|
|
56406
56649
|
} catch (error51) {
|
|
56407
|
-
|
|
56408
|
-
|
|
56409
|
-
|
|
56410
|
-
);
|
|
56650
|
+
const kind = errorKind(error51);
|
|
56651
|
+
failureLog.recordFailure(id, kind);
|
|
56652
|
+
const { emit, suppressed } = failureLog.shouldEmit(id, kind);
|
|
56653
|
+
if (emit) process.stderr.write(failureLogLine(label, error51, suppressed));
|
|
56411
56654
|
bridgeHealth.recordFailure(id, error51);
|
|
56412
56655
|
await bridgeHealth.maybeCrash();
|
|
56413
56656
|
}
|
|
@@ -56543,6 +56786,7 @@ async function startDirectorBridge() {
|
|
|
56543
56786
|
fetchImpl: authedFetch,
|
|
56544
56787
|
logger: bridgeLogger,
|
|
56545
56788
|
bridgeHealth,
|
|
56789
|
+
failureLog,
|
|
56546
56790
|
dispatchEnqueued: (projectSlug, work) => {
|
|
56547
56791
|
if (work.kind === "execution_run") driveRunNow(projectSlug, work.runId);
|
|
56548
56792
|
else driveAsyncNow(projectSlug, work.jobId);
|
|
@@ -56572,8 +56816,7 @@ async function startDirectorBridge() {
|
|
|
56572
56816
|
agentPoolLoopHandle = startAgentPoolLoop({
|
|
56573
56817
|
push: (snapshot) => client.putOperatorAgentPool(snapshot),
|
|
56574
56818
|
onError: (err) => process.stderr.write(
|
|
56575
|
-
|
|
56576
|
-
`
|
|
56819
|
+
suppressedPushLine("agent-pool snapshot push", err)
|
|
56577
56820
|
)
|
|
56578
56821
|
});
|
|
56579
56822
|
}
|
|
@@ -56582,8 +56825,7 @@ async function startDirectorBridge() {
|
|
|
56582
56825
|
providerReadinessLoopHandle = startProviderReadinessLoop({
|
|
56583
56826
|
push: (readiness) => client.putOperatorProviderReadiness(readiness),
|
|
56584
56827
|
onError: (err) => process.stderr.write(
|
|
56585
|
-
|
|
56586
|
-
`
|
|
56828
|
+
suppressedPushLine("provider-readiness push", err)
|
|
56587
56829
|
)
|
|
56588
56830
|
});
|
|
56589
56831
|
}
|
|
@@ -64147,7 +64389,8 @@ function installService(deps) {
|
|
|
64147
64389
|
deps.log("pm2 not found on PATH. Install it first: npm i -g pm2");
|
|
64148
64390
|
return { ok: false };
|
|
64149
64391
|
}
|
|
64150
|
-
const
|
|
64392
|
+
const claudeBin = deps.which("claude");
|
|
64393
|
+
const resolvedBins = [...bins, claudeBin].filter((b) => b !== null);
|
|
64151
64394
|
const pathEnv = Array.from(new Set(resolvedBins.map((b) => import_node_path28.default.dirname(b)))).join(":");
|
|
64152
64395
|
const apiBaseUrl = deps.env.MRRLIN_API_BASE_URL?.trim() || void 0;
|
|
64153
64396
|
const staging = deps.env.MRRLIN_STAGING === "1" || !apiBaseUrl && bridgeUrlImpliesStaging(deps.env.MRRLIN_BRIDGE_URL);
|
|
@@ -64544,6 +64787,13 @@ Usage:
|
|
|
64544
64787
|
- MRRLIN_DIRECTOR_BRIDGE_EXEC_TURN_TIMEOUT_SECONDS (DEPRECATED;
|
|
64545
64788
|
unset by default. When set, an extra per-turn hard cap: the
|
|
64546
64789
|
effective turn deadline becomes min(this, remaining run budget))
|
|
64790
|
+
- MRRLIN_BRIDGE_FAILURE_LOG_WINDOW_SECONDS (default 60;
|
|
64791
|
+
how long an IDENTICAL failure of the same periodic operation
|
|
64792
|
+
is suppressed on stderr before it prints again, carrying the
|
|
64793
|
+
count it swallowed. A changed error kind, a different
|
|
64794
|
+
operation, or the first failure after a success always
|
|
64795
|
+
prints immediately. Failures are counted for the tick's
|
|
64796
|
+
scheduler-tick degraded block whether or not the line prints)
|
|
64547
64797
|
- MRRLIN_DIRECTOR_BRIDGE_CONTEXT_MAX_ARTIFACTS (default 50;
|
|
64548
64798
|
most-recent artifacts kept in the execution context bundle)
|
|
64549
64799
|
|
|
@@ -64620,6 +64870,14 @@ async function main() {
|
|
|
64620
64870
|
return;
|
|
64621
64871
|
}
|
|
64622
64872
|
case "browser-serve": {
|
|
64873
|
+
const reaped = reapOrphanedBrowserProcesses();
|
|
64874
|
+
if (reaped.killedPids.length > 0) {
|
|
64875
|
+
const childNote = reaped.killedChildPids.length > 0 ? ` (plus ${reaped.killedChildPids.length} browser child process(es) they had already opened: ${reaped.killedChildPids.join(", ")})` : "";
|
|
64876
|
+
process.stderr.write(
|
|
64877
|
+
`[mrrlin-mcp browser-serve] reaped ${reaped.killedPids.length} orphaned browser process(es) with a dead parent: ${reaped.killedPids.join(", ")}${childNote}
|
|
64878
|
+
`
|
|
64879
|
+
);
|
|
64880
|
+
}
|
|
64623
64881
|
const { command: command2, args, env } = browserServeArgs();
|
|
64624
64882
|
const r = (0, import_node_child_process18.spawnSync)(command2, args, { stdio: "inherit", env: { ...process.env, ...env } });
|
|
64625
64883
|
process.exitCode = r.status ?? 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"@mrrlin/client": "0.0.0",
|
|
25
25
|
"@mrrlin/agent-client": "0.0.0",
|
|
26
26
|
"@mrrlin/director-e2e": "0.0.0",
|
|
27
|
+
"@mrrlin/wiki": "0.0.0",
|
|
27
28
|
"@mrrlin/schemas": "0.0.0",
|
|
28
|
-
"@mrrlin/tsconfig": "0.0.0"
|
|
29
|
-
"@mrrlin/wiki": "0.0.0"
|
|
29
|
+
"@mrrlin/tsconfig": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@iarna/toml": "^2.2.5",
|