@mrrlin-dev/mcp 0.4.54 → 0.4.57
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 +356 -87
- package/package.json +2 -2
package/dist/bin.cjs
CHANGED
|
@@ -20659,6 +20659,7 @@ var toml = __toESM(require_toml(), 1);
|
|
|
20659
20659
|
// src/api-base-url.ts
|
|
20660
20660
|
var DEFAULT_API_BASE_URL = "http://127.0.0.1:8787";
|
|
20661
20661
|
var STAGING_API_BASE_URL = "https://api-staging.mrrlin.com";
|
|
20662
|
+
var PRODUCTION_API_BASE_URL = "https://api.mrrlin.com";
|
|
20662
20663
|
function normalizeUrl(value) {
|
|
20663
20664
|
return value.replace(/\/$/, "");
|
|
20664
20665
|
}
|
|
@@ -20668,7 +20669,9 @@ var RETIRED_API_BASE_URLS = {
|
|
|
20668
20669
|
"mrrlin-api-fnnzzz-fnnzzzs-projects.vercel.app": STAGING_API_BASE_URL,
|
|
20669
20670
|
// Retired 2026-08-14 when staging decoupled onto the stable custom domain api-staging.mrrlin.com.
|
|
20670
20671
|
// A persisted operator configuration may still pin the mrrlin3 Vercel alias; retarget it in one hop.
|
|
20671
|
-
"mrrlin-api-mrrlin3.vercel.app": STAGING_API_BASE_URL
|
|
20672
|
+
"mrrlin-api-mrrlin3.vercel.app": STAGING_API_BASE_URL,
|
|
20673
|
+
// Retired 2026-09-07 when production moved from the transitional api-prod host to api.mrrlin.com.
|
|
20674
|
+
"api-prod.mrrlin.com": PRODUCTION_API_BASE_URL
|
|
20672
20675
|
};
|
|
20673
20676
|
function isSameApiHost(a, b) {
|
|
20674
20677
|
const origin = (value) => {
|
|
@@ -20685,6 +20688,9 @@ function isSameApiHost(a, b) {
|
|
|
20685
20688
|
function isStagingApiBaseUrl(input) {
|
|
20686
20689
|
return isSameApiHost(input, STAGING_API_BASE_URL);
|
|
20687
20690
|
}
|
|
20691
|
+
function isProductionApiBaseUrl(input) {
|
|
20692
|
+
return isSameApiHost(input, PRODUCTION_API_BASE_URL);
|
|
20693
|
+
}
|
|
20688
20694
|
function retargetRetiredApiBaseUrl(input) {
|
|
20689
20695
|
const normalized = normalizeUrl(input.trim());
|
|
20690
20696
|
let hostname3;
|
|
@@ -20703,7 +20709,6 @@ function retiredApiHostnames() {
|
|
|
20703
20709
|
function bridgeUrlImpliesStaging(input) {
|
|
20704
20710
|
const raw = input?.trim();
|
|
20705
20711
|
if (!raw) return false;
|
|
20706
|
-
if (isLoopbackUrl(raw)) return true;
|
|
20707
20712
|
let hostname3;
|
|
20708
20713
|
try {
|
|
20709
20714
|
hostname3 = new URL(raw).hostname.toLowerCase();
|
|
@@ -20728,8 +20733,14 @@ function readConfiguredApiBaseUrl(explicit) {
|
|
|
20728
20733
|
if (process.env.MRRLIN_STAGING === "1") return STAGING_API_BASE_URL;
|
|
20729
20734
|
return DEFAULT_API_BASE_URL;
|
|
20730
20735
|
}
|
|
20736
|
+
function readDirectorBridgeApiBaseUrl(explicit) {
|
|
20737
|
+
const direct = explicit ?? process.env.MRRLIN_API_BASE_URL;
|
|
20738
|
+
if (direct?.trim()) return retargetRetiredApiBaseUrl(direct.trim()).baseUrl;
|
|
20739
|
+
if (process.env.MRRLIN_STAGING === "1") return STAGING_API_BASE_URL;
|
|
20740
|
+
return PRODUCTION_API_BASE_URL;
|
|
20741
|
+
}
|
|
20731
20742
|
function isRemoteApiAllowed(apiBaseUrl) {
|
|
20732
|
-
return isLoopbackUrl(apiBaseUrl) || isStagingApiBaseUrl(apiBaseUrl) ||
|
|
20743
|
+
return isLoopbackUrl(apiBaseUrl) || isStagingApiBaseUrl(apiBaseUrl) || isProductionApiBaseUrl(apiBaseUrl) || process.env.MRRLIN_API_ALLOW_REMOTE === "1";
|
|
20733
20744
|
}
|
|
20734
20745
|
function readProbeTimeoutMs(env) {
|
|
20735
20746
|
const raw = Number((env.MRRLIN_API_PROBE_TIMEOUT_MS ?? "").trim());
|
|
@@ -20770,7 +20781,7 @@ async function resolveApiBaseUrlDynamic(opts = {}) {
|
|
|
20770
20781
|
return { baseUrl: STAGING_API_BASE_URL, source: "staging-autodetect" };
|
|
20771
20782
|
}
|
|
20772
20783
|
throw new Error(
|
|
20773
|
-
`Local Mrrlin API not reachable at ${DEFAULT_API_BASE_URL}. Start it, or set
|
|
20784
|
+
`Local Mrrlin API not reachable at ${DEFAULT_API_BASE_URL}. Start it, or set MRRLIN_API_BASE_URL=${STAGING_API_BASE_URL} to target staging (or MRRLIN_API_AUTODETECT=1 to auto-fall back to staging when local is down).`
|
|
20774
20785
|
);
|
|
20775
20786
|
}
|
|
20776
20787
|
|
|
@@ -20994,6 +21005,24 @@ function blockEnvHasRetiredApiUrl(actual) {
|
|
|
20994
21005
|
}
|
|
20995
21006
|
return retiredApiHostnames().includes(hostname3);
|
|
20996
21007
|
}
|
|
21008
|
+
function isFirstPartyOrLoopbackApiUrl(value) {
|
|
21009
|
+
return isStagingApiBaseUrl(value) || isProductionApiBaseUrl(value) || isLoopbackUrl(value);
|
|
21010
|
+
}
|
|
21011
|
+
function normalizedApiUrlFromEnv(env) {
|
|
21012
|
+
const pinned = env.MRRLIN_API_BASE_URL;
|
|
21013
|
+
if (typeof pinned === "string" && pinned.trim()) return retargetRetiredApiBaseUrl(pinned.trim()).baseUrl;
|
|
21014
|
+
return env.MRRLIN_STAGING === "1" ? STAGING_API_BASE_URL : void 0;
|
|
21015
|
+
}
|
|
21016
|
+
function blockEnvNeedsApiRoutingMigration(actual) {
|
|
21017
|
+
if (typeof actual !== "object" || actual === null) return false;
|
|
21018
|
+
const env = actual.env;
|
|
21019
|
+
if (typeof env !== "object" || env === null) return false;
|
|
21020
|
+
const vars = env;
|
|
21021
|
+
if (vars.MRRLIN_STAGING === "1") return true;
|
|
21022
|
+
if (blockEnvHasRetiredApiUrl(actual)) return true;
|
|
21023
|
+
const apiUrl = normalizedApiUrlFromEnv(vars);
|
|
21024
|
+
return apiUrl !== void 0 && (isProductionApiBaseUrl(apiUrl) || vars.MRRLIN_API_ALLOW_REMOTE === "1" && isFirstPartyOrLoopbackApiUrl(apiUrl));
|
|
21025
|
+
}
|
|
20997
21026
|
function renderEnvPreservedNotice(blockId, envKeys) {
|
|
20998
21027
|
const keys = [...envKeys].sort().join(", ");
|
|
20999
21028
|
return `[mrrlin-mcp configure bridge] kept your existing [mcp_servers.${blockId}] env keys (${keys}) so your API target is preserved. Re-run with --force to replace or remove it.`;
|
|
@@ -21001,16 +21030,30 @@ function renderEnvPreservedNotice(blockId, envKeys) {
|
|
|
21001
21030
|
function reconcileMcpServerBlock(existing, desired) {
|
|
21002
21031
|
if (existing === void 0) return { block: desired, preservedEnvKeys: [] };
|
|
21003
21032
|
if (tablesEqual(existing, desired)) return { block: existing, preservedEnvKeys: [] };
|
|
21004
|
-
if (
|
|
21033
|
+
if (blockEnvNeedsApiRoutingMigration(existing)) {
|
|
21005
21034
|
const existingEnv = existing.env ?? {};
|
|
21035
|
+
const apiUrl = normalizedApiUrlFromEnv(existingEnv);
|
|
21006
21036
|
const healedEnv = {};
|
|
21007
21037
|
for (const [key, value] of Object.entries(existingEnv)) {
|
|
21008
|
-
if (key === "MRRLIN_API_BASE_URL")
|
|
21038
|
+
if (key === "MRRLIN_API_BASE_URL" || key === "MRRLIN_STAGING" || key === "MRRLIN_API_ALLOW_REMOTE") {
|
|
21039
|
+
continue;
|
|
21040
|
+
}
|
|
21009
21041
|
if (typeof value === "string") healedEnv[key] = value;
|
|
21010
21042
|
}
|
|
21011
|
-
|
|
21012
|
-
|
|
21013
|
-
|
|
21043
|
+
if (apiUrl && !isProductionApiBaseUrl(apiUrl)) {
|
|
21044
|
+
healedEnv.MRRLIN_API_BASE_URL = apiUrl;
|
|
21045
|
+
if (existingEnv.MRRLIN_API_ALLOW_REMOTE === "1" && !isFirstPartyOrLoopbackApiUrl(apiUrl)) {
|
|
21046
|
+
healedEnv.MRRLIN_API_ALLOW_REMOTE = "1";
|
|
21047
|
+
}
|
|
21048
|
+
}
|
|
21049
|
+
const finalEnv = { ...desired.env, ...healedEnv };
|
|
21050
|
+
const block = { ...desired };
|
|
21051
|
+
if (Object.keys(finalEnv).length > 0) {
|
|
21052
|
+
block.env = finalEnv;
|
|
21053
|
+
} else {
|
|
21054
|
+
delete block.env;
|
|
21055
|
+
}
|
|
21056
|
+
return { block, preservedEnvKeys: [] };
|
|
21014
21057
|
}
|
|
21015
21058
|
if (classifyExistingBlock(existing, desired) === "preserve-env-noop") {
|
|
21016
21059
|
const env = existing.env ?? {};
|
|
@@ -21057,11 +21100,15 @@ function deriveServeEnvFromProcessEnv(env) {
|
|
|
21057
21100
|
const rawApiUrl = env.MRRLIN_API_BASE_URL?.trim();
|
|
21058
21101
|
const bridgeWantsStaging = bridgeUrlImpliesStaging(env.MRRLIN_BRIDGE_URL);
|
|
21059
21102
|
const apiUrl = rawApiUrl ? retargetRetiredApiBaseUrl(rawApiUrl).baseUrl : void 0;
|
|
21060
|
-
if (apiUrl
|
|
21061
|
-
|
|
21062
|
-
|
|
21063
|
-
|
|
21064
|
-
|
|
21103
|
+
if (apiUrl) {
|
|
21104
|
+
if (!isProductionApiBaseUrl(apiUrl)) {
|
|
21105
|
+
out.MRRLIN_API_BASE_URL = apiUrl;
|
|
21106
|
+
if (env.MRRLIN_API_ALLOW_REMOTE === "1" && !isStagingApiBaseUrl(apiUrl) && !isLoopbackUrl(apiUrl)) {
|
|
21107
|
+
out.MRRLIN_API_ALLOW_REMOTE = "1";
|
|
21108
|
+
}
|
|
21109
|
+
}
|
|
21110
|
+
} else if (env.MRRLIN_STAGING === "1" || bridgeWantsStaging) {
|
|
21111
|
+
out.MRRLIN_API_BASE_URL = STAGING_API_BASE_URL;
|
|
21065
21112
|
}
|
|
21066
21113
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
21067
21114
|
}
|
|
@@ -21087,7 +21134,7 @@ function browserTable(binPath, distribution, homeDir) {
|
|
|
21087
21134
|
}
|
|
21088
21135
|
function blockRegex(id) {
|
|
21089
21136
|
const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
21090
|
-
return new RegExp(`(^|\\n)\\[mcp_servers\\.${escaped}\\][\\s\\S]*?(?=\\n\\[|$)`);
|
|
21137
|
+
return new RegExp(`(^|\\n)\\[mcp_servers\\.${escaped}\\][\\s\\S]*?(?=\\n\\[(?!\\]|mcp_servers\\.${escaped}\\.)|$)`);
|
|
21091
21138
|
}
|
|
21092
21139
|
function renderBlock(id, expected) {
|
|
21093
21140
|
const lines2 = [
|
|
@@ -21204,7 +21251,7 @@ async function installCodex(options) {
|
|
|
21204
21251
|
}
|
|
21205
21252
|
const classification = classifyExistingBlock(existingTable, block.expected);
|
|
21206
21253
|
if (classification === "equal") continue;
|
|
21207
|
-
if (
|
|
21254
|
+
if (blockEnvNeedsApiRoutingMigration(existingTable)) {
|
|
21208
21255
|
const { block: healedBlock } = reconcileMcpServerBlock(existingTable, block.expected);
|
|
21209
21256
|
content = replaceBlock(content, block.blockRe, renderBlock(block.id, healedBlock));
|
|
21210
21257
|
action = "replaced";
|
|
@@ -21377,7 +21424,7 @@ async function installClaude(options) {
|
|
|
21377
21424
|
}
|
|
21378
21425
|
const classification = classifyExistingBlock(existingEntry, entry.expected);
|
|
21379
21426
|
if (classification === "equal") continue;
|
|
21380
|
-
if (
|
|
21427
|
+
if (blockEnvNeedsApiRoutingMigration(existingEntry)) {
|
|
21381
21428
|
const { block: healedBlock } = reconcileMcpServerBlock(existingEntry, entry.expected);
|
|
21382
21429
|
mcpServers[entry.id] = healedBlock;
|
|
21383
21430
|
action = "replaced";
|
|
@@ -42441,7 +42488,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
42441
42488
|
}
|
|
42442
42489
|
|
|
42443
42490
|
// src/_generated/version.ts
|
|
42444
|
-
var PKG_VERSION = "0.4.
|
|
42491
|
+
var PKG_VERSION = "0.4.57";
|
|
42445
42492
|
|
|
42446
42493
|
// src/api-base-url-migrate.ts
|
|
42447
42494
|
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
@@ -42475,9 +42522,13 @@ function renderEcosystemConfig(opts) {
|
|
|
42475
42522
|
if (telemetryProject) env.MRRLIN_TELEMETRY_DEFAULT_PROJECT = telemetryProject;
|
|
42476
42523
|
if (opts.user) env.USER = opts.user;
|
|
42477
42524
|
if (opts.codexHome) env.CODEX_HOME = opts.codexHome;
|
|
42478
|
-
const
|
|
42479
|
-
if (
|
|
42480
|
-
|
|
42525
|
+
const apiBaseUrl = opts.apiBaseUrl ? retargetRetiredApiBaseUrl(opts.apiBaseUrl).baseUrl : void 0;
|
|
42526
|
+
if (apiBaseUrl && !isProductionApiBaseUrl(apiBaseUrl)) {
|
|
42527
|
+
env.MRRLIN_API_BASE_URL = apiBaseUrl;
|
|
42528
|
+
if (opts.apiAllowRemote && !isStagingApiBaseUrl(apiBaseUrl) && !isLoopbackUrl(apiBaseUrl)) {
|
|
42529
|
+
env.MRRLIN_API_ALLOW_REMOTE = "1";
|
|
42530
|
+
}
|
|
42531
|
+
}
|
|
42481
42532
|
const envLines = Object.entries(env).map(([k, v]) => ` ${k}: ${JSON.stringify(v)},`).join("\n");
|
|
42482
42533
|
return `module.exports = {
|
|
42483
42534
|
apps: [{
|
|
@@ -42521,6 +42572,10 @@ function defaultDeps(deps) {
|
|
|
42521
42572
|
function findRetiredHost(text) {
|
|
42522
42573
|
return retiredApiHostnames().find((host) => text.toLowerCase().includes(host));
|
|
42523
42574
|
}
|
|
42575
|
+
function hasLiveLegacyStagingFlag(text, syntax) {
|
|
42576
|
+
const lineRe = syntax === "js" ? /^[ \t]*MRRLIN_STAGING:\s*"1",?[ \t]*\r?$/gm : /^[ \t]*(?!#).*MRRLIN_STAGING\s*=\s*"1"/gm;
|
|
42577
|
+
return lineRe.test(text);
|
|
42578
|
+
}
|
|
42524
42579
|
function isRetiredApiUrl(value) {
|
|
42525
42580
|
if (!value?.trim()) return false;
|
|
42526
42581
|
try {
|
|
@@ -42529,6 +42584,19 @@ function isRetiredApiUrl(value) {
|
|
|
42529
42584
|
return false;
|
|
42530
42585
|
}
|
|
42531
42586
|
}
|
|
42587
|
+
function migratedApiUrl(value) {
|
|
42588
|
+
const { baseUrl, retargeted } = retargetRetiredApiBaseUrl(value);
|
|
42589
|
+
return retargeted ? baseUrl : void 0;
|
|
42590
|
+
}
|
|
42591
|
+
function isFirstPartyOrLoopbackUrl(value) {
|
|
42592
|
+
return isStagingApiBaseUrl(value) || isProductionApiBaseUrl(value) || isLoopbackUrl(value);
|
|
42593
|
+
}
|
|
42594
|
+
function isRecord(value) {
|
|
42595
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
42596
|
+
}
|
|
42597
|
+
function removeStandaloneAllowRemoteLine(text) {
|
|
42598
|
+
return text.replace(/^[ \t]*MRRLIN_API_ALLOW_REMOTE:\s*"1",?[ \t]*\r?\n/gm, "");
|
|
42599
|
+
}
|
|
42532
42600
|
function backupOnce(file2, text, deps) {
|
|
42533
42601
|
const bak = `${file2}.bak-retired-api-url`;
|
|
42534
42602
|
if (!deps.exists(bak)) deps.writeFile(bak, text);
|
|
@@ -42538,7 +42606,8 @@ function migrateEcosystemConfig(deps = {}) {
|
|
|
42538
42606
|
const file2 = import_node_path8.default.join(d.home, ".mrrlin", "ecosystem.config.cjs");
|
|
42539
42607
|
if (!d.exists(file2)) return { file: file2, status: "absent" };
|
|
42540
42608
|
const text = d.readFile(file2);
|
|
42541
|
-
if (!findRetiredHost(text)
|
|
42609
|
+
if (!findRetiredHost(text) && !hasLiveLegacyStagingFlag(text, "js"))
|
|
42610
|
+
return { file: file2, status: "clean" };
|
|
42542
42611
|
const fields = {
|
|
42543
42612
|
cwd: /cwd:\s*"((?:[^"\\]|\\.)*)"/.exec(text)?.[1],
|
|
42544
42613
|
home: /HOME:\s*"((?:[^"\\]|\\.)*)"/.exec(text)?.[1],
|
|
@@ -42547,14 +42616,48 @@ function migrateEcosystemConfig(deps = {}) {
|
|
|
42547
42616
|
};
|
|
42548
42617
|
if (fields.cwd && fields.home && fields.pathEnv) {
|
|
42549
42618
|
const retiredUrl = /MRRLIN_API_BASE_URL:\s*"((?:[^"\\]|\\.)*)"/.exec(text)?.[1];
|
|
42550
|
-
const
|
|
42619
|
+
const legacyUrlShape = retiredUrl && isRetiredApiUrl(retiredUrl) ? renderEcosystemConfigLegacy({
|
|
42551
42620
|
cwd: fields.cwd,
|
|
42552
42621
|
home: fields.home,
|
|
42553
42622
|
pathEnv: fields.pathEnv,
|
|
42554
42623
|
codexHome: fields.codexHome,
|
|
42555
42624
|
apiBaseUrl: retiredUrl
|
|
42556
42625
|
}) : void 0;
|
|
42557
|
-
|
|
42626
|
+
const legacyStagingShape = renderEcosystemConfigWithEnv(
|
|
42627
|
+
{ cwd: fields.cwd, home: fields.home, pathEnv: fields.pathEnv, codexHome: fields.codexHome },
|
|
42628
|
+
{ MRRLIN_STAGING: "1" }
|
|
42629
|
+
);
|
|
42630
|
+
if (legacyUrlShape && legacyUrlShape === text) {
|
|
42631
|
+
const nextUrl = migratedApiUrl(retiredUrl ?? "");
|
|
42632
|
+
if (!nextUrl) {
|
|
42633
|
+
return {
|
|
42634
|
+
file: file2,
|
|
42635
|
+
status: "skipped-drifted",
|
|
42636
|
+
detail: "the pinned API URL did not resolve to a known successor"
|
|
42637
|
+
};
|
|
42638
|
+
}
|
|
42639
|
+
backupOnce(file2, text, d);
|
|
42640
|
+
d.writeFile(
|
|
42641
|
+
file2,
|
|
42642
|
+
isProductionApiBaseUrl(nextUrl) ? renderEcosystemConfigWithEnv(
|
|
42643
|
+
{
|
|
42644
|
+
cwd: fields.cwd,
|
|
42645
|
+
home: fields.home,
|
|
42646
|
+
pathEnv: fields.pathEnv,
|
|
42647
|
+
codexHome: fields.codexHome
|
|
42648
|
+
},
|
|
42649
|
+
{ MRRLIN_API_BASE_URL: nextUrl }
|
|
42650
|
+
) : renderEcosystemConfig({
|
|
42651
|
+
cwd: fields.cwd,
|
|
42652
|
+
home: fields.home,
|
|
42653
|
+
pathEnv: fields.pathEnv,
|
|
42654
|
+
codexHome: fields.codexHome,
|
|
42655
|
+
apiBaseUrl: STAGING_API_BASE_URL
|
|
42656
|
+
})
|
|
42657
|
+
);
|
|
42658
|
+
return { file: file2, status: "migrated", detail: "regenerated from the canonical template" };
|
|
42659
|
+
}
|
|
42660
|
+
if (legacyStagingShape === text) {
|
|
42558
42661
|
backupOnce(file2, text, d);
|
|
42559
42662
|
d.writeFile(
|
|
42560
42663
|
file2,
|
|
@@ -42563,8 +42666,7 @@ function migrateEcosystemConfig(deps = {}) {
|
|
|
42563
42666
|
home: fields.home,
|
|
42564
42667
|
pathEnv: fields.pathEnv,
|
|
42565
42668
|
codexHome: fields.codexHome,
|
|
42566
|
-
|
|
42567
|
-
apiBaseUrl: void 0
|
|
42669
|
+
apiBaseUrl: STAGING_API_BASE_URL
|
|
42568
42670
|
})
|
|
42569
42671
|
);
|
|
42570
42672
|
return { file: file2, status: "migrated", detail: "regenerated from the canonical template" };
|
|
@@ -42574,16 +42676,49 @@ function migrateEcosystemConfig(deps = {}) {
|
|
|
42574
42676
|
const matches = (text.match(lineRe) ?? []).filter((line) => !line.trimStart().startsWith("//"));
|
|
42575
42677
|
const target = matches.length === 1 ? matches[0] : void 0;
|
|
42576
42678
|
if (target !== void 0 && isRetiredApiUrl(/"((?:[^"\\]|\\.)*)"/.exec(target)?.[1])) {
|
|
42679
|
+
const nextUrl = migratedApiUrl(/"((?:[^"\\]|\\.)*)"/.exec(target)?.[1] ?? "");
|
|
42680
|
+
if (!nextUrl) {
|
|
42681
|
+
return {
|
|
42682
|
+
file: file2,
|
|
42683
|
+
status: "skipped-drifted",
|
|
42684
|
+
detail: "the pinned API URL did not resolve to a known successor"
|
|
42685
|
+
};
|
|
42686
|
+
}
|
|
42577
42687
|
backupOnce(file2, text, d);
|
|
42578
42688
|
const indent = /^[ \t]*/.exec(target)?.[0] ?? " ";
|
|
42579
|
-
|
|
42580
|
-
|
|
42581
|
-
|
|
42689
|
+
let out = text.replace(
|
|
42690
|
+
target,
|
|
42691
|
+
() => `${indent}MRRLIN_API_BASE_URL: ${JSON.stringify(nextUrl)},
|
|
42692
|
+
`
|
|
42693
|
+
);
|
|
42694
|
+
if (isFirstPartyOrLoopbackUrl(nextUrl)) out = removeStandaloneAllowRemoteLine(out);
|
|
42695
|
+
return d.writeFile(file2, out), { file: file2, status: "migrated", detail: "replaced the pinned URL line" };
|
|
42696
|
+
}
|
|
42697
|
+
const stagingLineRe = /^[ \t]*MRRLIN_STAGING:\s*"1",?[ \t]*\r?\n/gm;
|
|
42698
|
+
const stagingMatches = (text.match(stagingLineRe) ?? []).filter(
|
|
42699
|
+
(line) => !line.trimStart().startsWith("//")
|
|
42700
|
+
);
|
|
42701
|
+
const stagingTarget = stagingMatches.length === 1 ? stagingMatches[0] : void 0;
|
|
42702
|
+
if (stagingTarget !== void 0) {
|
|
42703
|
+
backupOnce(file2, text, d);
|
|
42704
|
+
const indent = /^[ \t]*/.exec(stagingTarget)?.[0] ?? " ";
|
|
42705
|
+
let out = text.replace(
|
|
42706
|
+
stagingTarget,
|
|
42707
|
+
() => `${indent}MRRLIN_API_BASE_URL: ${JSON.stringify(STAGING_API_BASE_URL)},
|
|
42708
|
+
`
|
|
42709
|
+
);
|
|
42710
|
+
out = removeStandaloneAllowRemoteLine(out);
|
|
42711
|
+
d.writeFile(file2, out);
|
|
42712
|
+
return {
|
|
42713
|
+
file: file2,
|
|
42714
|
+
status: "migrated",
|
|
42715
|
+
detail: "replaced the legacy staging flag with the explicit API URL"
|
|
42716
|
+
};
|
|
42582
42717
|
}
|
|
42583
42718
|
return {
|
|
42584
42719
|
file: file2,
|
|
42585
42720
|
status: "skipped-drifted",
|
|
42586
|
-
detail: matches.length === 0 ? "a retired host appears in the file but no
|
|
42721
|
+
detail: matches.length === 0 ? "a retired host or legacy staging flag appears in the file but no live routing env line was found \u2014 fix it by hand or re-run install-service" : `found ${matches.length} MRRLIN_API_BASE_URL lines; refusing to guess \u2014 fix by hand or re-run install-service`
|
|
42587
42722
|
};
|
|
42588
42723
|
}
|
|
42589
42724
|
function renderEcosystemConfigLegacy(opts) {
|
|
@@ -42622,8 +42757,8 @@ function migrateCodexConfig(deps = {}) {
|
|
|
42622
42757
|
if (!d.exists(file2)) return { file: file2, status: "absent" };
|
|
42623
42758
|
const text = d.readFile(file2);
|
|
42624
42759
|
const host = findRetiredHost(text);
|
|
42625
|
-
if (!host) return { file: file2, status: "clean" };
|
|
42626
|
-
const managedBlockRe = /^\[mcp_servers\.mrrlin\][^\n]*\n(?:(?!\[)[^\n]*\n?)*/gm;
|
|
42760
|
+
if (!host && !hasLiveLegacyStagingFlag(text, "toml")) return { file: file2, status: "clean" };
|
|
42761
|
+
const managedBlockRe = /^\[mcp_servers\.mrrlin(?:\]|\.env\])[^\n]*\n(?:(?!\[)[^\n]*\n?)*/gm;
|
|
42627
42762
|
const blocks = text.match(managedBlockRe) ?? [];
|
|
42628
42763
|
if (blocks.length === 0) {
|
|
42629
42764
|
return {
|
|
@@ -42633,21 +42768,57 @@ function migrateCodexConfig(deps = {}) {
|
|
|
42633
42768
|
};
|
|
42634
42769
|
}
|
|
42635
42770
|
const assignRe = /MRRLIN_API_BASE_URL\s*=\s*"([^"]*)"/g;
|
|
42771
|
+
const stagingAssignRe = /MRRLIN_STAGING\s*=\s*"1"/g;
|
|
42772
|
+
const allowRemoteAssignRe = /,?\s*MRRLIN_API_ALLOW_REMOTE\s*=\s*"1"\s*,?/g;
|
|
42636
42773
|
let out = text;
|
|
42637
42774
|
let changed = 0;
|
|
42638
42775
|
for (const block of blocks) {
|
|
42776
|
+
let blockNextApiUrl;
|
|
42777
|
+
const urlMatch = /MRRLIN_API_BASE_URL\s*=\s*"([^"]*)"/.exec(block);
|
|
42778
|
+
const matchedUrl = urlMatch?.[1];
|
|
42779
|
+
if (matchedUrl !== void 0) {
|
|
42780
|
+
const successor = migratedApiUrl(matchedUrl);
|
|
42781
|
+
if (successor) blockNextApiUrl = successor;
|
|
42782
|
+
}
|
|
42783
|
+
if (!blockNextApiUrl && /MRRLIN_STAGING\s*=\s*"1"/.test(block)) {
|
|
42784
|
+
blockNextApiUrl = STAGING_API_BASE_URL;
|
|
42785
|
+
}
|
|
42639
42786
|
let blockChanged = false;
|
|
42640
42787
|
const healedBlock = block.split("\n").map((line) => {
|
|
42641
42788
|
if (line.trimStart().startsWith("#")) return line;
|
|
42642
|
-
|
|
42643
|
-
|
|
42644
|
-
|
|
42645
|
-
|
|
42789
|
+
let healedLine = line.replace(assignRe, (match, url2) => {
|
|
42790
|
+
const successor = migratedApiUrl(url2);
|
|
42791
|
+
if (!successor) return match;
|
|
42792
|
+
return `MRRLIN_API_BASE_URL = ${JSON.stringify(successor)}`;
|
|
42793
|
+
});
|
|
42794
|
+
healedLine = healedLine.replace(stagingAssignRe, () => {
|
|
42795
|
+
return `MRRLIN_API_BASE_URL = ${JSON.stringify(STAGING_API_BASE_URL)}`;
|
|
42796
|
+
});
|
|
42797
|
+
if (blockNextApiUrl && isFirstPartyOrLoopbackUrl(blockNextApiUrl)) {
|
|
42798
|
+
healedLine = healedLine.replace(
|
|
42799
|
+
allowRemoteAssignRe,
|
|
42800
|
+
(match) => match.startsWith(",") && match.endsWith(",") ? "," : ""
|
|
42801
|
+
).replace(/\{\s*,\s*/, "{ ").replace(/,\s*\}/, " }").replace(/,\s*,/g, ",");
|
|
42802
|
+
if (/^[ \t]*MRRLIN_API_ALLOW_REMOTE\s*=\s*"1"[ \t]*$/.test(healedLine)) {
|
|
42803
|
+
healedLine = "";
|
|
42804
|
+
}
|
|
42805
|
+
}
|
|
42646
42806
|
if (healedLine !== line) blockChanged = true;
|
|
42647
42807
|
return healedLine;
|
|
42808
|
+
}).filter((line, i) => {
|
|
42809
|
+
if (line === "" && block.split("\n")[i] !== "") return false;
|
|
42810
|
+
return true;
|
|
42648
42811
|
}).join("\n");
|
|
42649
42812
|
if (!blockChanged) continue;
|
|
42650
|
-
|
|
42813
|
+
let deduped = healedBlock.replace(
|
|
42814
|
+
/MRRLIN_API_BASE_URL = "https:\/\/api-staging\.mrrlin\.com"\s*,\s*MRRLIN_API_BASE_URL = "https:\/\/api-staging\.mrrlin\.com"/g,
|
|
42815
|
+
`MRRLIN_API_BASE_URL = "https://api-staging.mrrlin.com"`
|
|
42816
|
+
);
|
|
42817
|
+
deduped = deduped.replace(
|
|
42818
|
+
/^[ \t]*MRRLIN_API_BASE_URL\s*=\s*"https:\/\/api-staging\.mrrlin\.com"[ \t]*\r?\n([\s\S]*?)^[ \t]*MRRLIN_API_BASE_URL\s*=\s*"https:\/\/api-staging\.mrrlin\.com"[ \t]*\r?\n/gm,
|
|
42819
|
+
`MRRLIN_API_BASE_URL = "https://api-staging.mrrlin.com"
|
|
42820
|
+
$1`
|
|
42821
|
+
);
|
|
42651
42822
|
out = out.replace(block, () => deduped);
|
|
42652
42823
|
changed += 1;
|
|
42653
42824
|
}
|
|
@@ -42660,14 +42831,67 @@ function migrateCodexConfig(deps = {}) {
|
|
|
42660
42831
|
}
|
|
42661
42832
|
backupOnce(file2, text, d);
|
|
42662
42833
|
d.writeFile(file2, out);
|
|
42663
|
-
return {
|
|
42834
|
+
return {
|
|
42835
|
+
file: file2,
|
|
42836
|
+
status: "migrated",
|
|
42837
|
+
detail: "rewrote legacy API routing env to explicit URL form"
|
|
42838
|
+
};
|
|
42839
|
+
}
|
|
42840
|
+
function migrateClaudeConfig(deps = {}) {
|
|
42841
|
+
const d = defaultDeps(deps);
|
|
42842
|
+
const file2 = import_node_path8.default.join(d.home, ".claude.json");
|
|
42843
|
+
if (!d.exists(file2)) return { file: file2, status: "absent" };
|
|
42844
|
+
const text = d.readFile(file2);
|
|
42845
|
+
let parsed;
|
|
42846
|
+
try {
|
|
42847
|
+
parsed = text.trim() === "" ? {} : JSON.parse(text);
|
|
42848
|
+
} catch (error51) {
|
|
42849
|
+
const detail2 = error51 instanceof Error ? error51.message : String(error51);
|
|
42850
|
+
return { file: file2, status: "failed", detail: `malformed JSON: ${detail2}` };
|
|
42851
|
+
}
|
|
42852
|
+
if (!isRecord(parsed) || !isRecord(parsed.mcpServers)) {
|
|
42853
|
+
return { file: file2, status: "clean" };
|
|
42854
|
+
}
|
|
42855
|
+
const entry = parsed.mcpServers.mrrlin;
|
|
42856
|
+
if (!isRecord(entry) || entry.command !== "mrrlin-mcp" || !Array.isArray(entry.args) || entry.args.length !== 1 || entry.args[0] !== "serve") {
|
|
42857
|
+
return { file: file2, status: "clean" };
|
|
42858
|
+
}
|
|
42859
|
+
const env = entry.env;
|
|
42860
|
+
if (!isRecord(env)) return { file: file2, status: "clean" };
|
|
42861
|
+
const envKeys = Object.keys(env);
|
|
42862
|
+
const routingKeys = /* @__PURE__ */ new Set(["MRRLIN_STAGING", "MRRLIN_API_BASE_URL", "MRRLIN_API_ALLOW_REMOTE"]);
|
|
42863
|
+
if (envKeys.length === 0 || !envKeys.every((key) => routingKeys.has(key)) || !envKeys.every((key) => typeof env[key] === "string")) {
|
|
42864
|
+
return { file: file2, status: "clean" };
|
|
42865
|
+
}
|
|
42866
|
+
let nextEnv;
|
|
42867
|
+
let detail;
|
|
42868
|
+
if (env.MRRLIN_STAGING === "1" && envKeys.length === 1) {
|
|
42869
|
+
nextEnv = { MRRLIN_API_BASE_URL: STAGING_API_BASE_URL };
|
|
42870
|
+
detail = "replaced the legacy staging flag with the explicit API URL";
|
|
42871
|
+
} else if (typeof env.MRRLIN_API_BASE_URL === "string" && (envKeys.length === 1 || envKeys.length === 2 && env.MRRLIN_API_ALLOW_REMOTE === "1")) {
|
|
42872
|
+
const nextUrl = migratedApiUrl(env.MRRLIN_API_BASE_URL);
|
|
42873
|
+
if (!nextUrl) return { file: file2, status: "clean" };
|
|
42874
|
+
nextEnv = isProductionApiBaseUrl(nextUrl) ? {} : { MRRLIN_API_BASE_URL: nextUrl };
|
|
42875
|
+
detail = isProductionApiBaseUrl(nextUrl) ? "removed the retired production routing override" : "retargeted the retired API URL";
|
|
42876
|
+
}
|
|
42877
|
+
if (!nextEnv) return { file: file2, status: "clean" };
|
|
42878
|
+
backupOnce(file2, text, d);
|
|
42879
|
+
const next = JSON.parse(JSON.stringify(parsed));
|
|
42880
|
+
const nextServers = next.mcpServers;
|
|
42881
|
+
const nextEntry = nextServers.mrrlin;
|
|
42882
|
+
if (Object.keys(nextEnv).length === 0) delete nextEntry.env;
|
|
42883
|
+
else nextEntry.env = nextEnv;
|
|
42884
|
+
d.writeFile(file2, `${JSON.stringify(next, null, 2)}
|
|
42885
|
+
`);
|
|
42886
|
+
return { file: file2, status: "migrated", detail };
|
|
42664
42887
|
}
|
|
42665
42888
|
function migrateRetiredApiBaseUrls(deps = {}) {
|
|
42666
42889
|
const d = defaultDeps(deps);
|
|
42667
42890
|
const results = [];
|
|
42668
42891
|
const steps = [
|
|
42669
42892
|
{ run: migrateEcosystemConfig, file: import_node_path8.default.join(d.home, ".mrrlin", "ecosystem.config.cjs") },
|
|
42670
|
-
{ run: migrateCodexConfig, file: import_node_path8.default.join(d.codexHome, "config.toml") }
|
|
42893
|
+
{ run: migrateCodexConfig, file: import_node_path8.default.join(d.codexHome, "config.toml") },
|
|
42894
|
+
{ run: migrateClaudeConfig, file: import_node_path8.default.join(d.home, ".claude.json") }
|
|
42671
42895
|
];
|
|
42672
42896
|
for (const step of steps) {
|
|
42673
42897
|
try {
|
|
@@ -45062,7 +45286,7 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
45062
45286
|
}
|
|
45063
45287
|
|
|
45064
45288
|
// src/telemetry/pii-gate.ts
|
|
45065
|
-
function
|
|
45289
|
+
function isRecord2(v) {
|
|
45066
45290
|
return v !== null && typeof v === "object";
|
|
45067
45291
|
}
|
|
45068
45292
|
function resolvePath(root, path24) {
|
|
@@ -45074,7 +45298,7 @@ function resolvePath(root, path24) {
|
|
|
45074
45298
|
if (Array.isArray(cur) && Number.isInteger(idx)) {
|
|
45075
45299
|
if (idx < 0 || idx >= cur.length) return null;
|
|
45076
45300
|
cur = cur[idx];
|
|
45077
|
-
} else if (
|
|
45301
|
+
} else if (isRecord2(cur) && part in cur) {
|
|
45078
45302
|
cur = cur[part];
|
|
45079
45303
|
} else {
|
|
45080
45304
|
return null;
|
|
@@ -45086,7 +45310,7 @@ function resolvePath(root, path24) {
|
|
|
45086
45310
|
if (lastIdx < 0 || lastIdx >= cur.length) return null;
|
|
45087
45311
|
return { parent: cur, key: lastIdx };
|
|
45088
45312
|
}
|
|
45089
|
-
if (
|
|
45313
|
+
if (isRecord2(cur) && last in cur) {
|
|
45090
45314
|
return { parent: cur, key: last };
|
|
45091
45315
|
}
|
|
45092
45316
|
return null;
|
|
@@ -45095,7 +45319,7 @@ function withheldExcerptClone(artifact) {
|
|
|
45095
45319
|
const clone2 = { ...artifact };
|
|
45096
45320
|
if (Array.isArray(clone2.steps)) {
|
|
45097
45321
|
clone2.steps = clone2.steps.map((step) => {
|
|
45098
|
-
if (!
|
|
45322
|
+
if (!isRecord2(step)) return step;
|
|
45099
45323
|
const copy = { ...step };
|
|
45100
45324
|
delete copy.excerpt;
|
|
45101
45325
|
return copy;
|
|
@@ -45126,9 +45350,9 @@ async function gate(artifact, prose, ctx) {
|
|
|
45126
45350
|
} catch {
|
|
45127
45351
|
return { ok: true, gated: true, artifact: withheldExcerptClone(artifact) };
|
|
45128
45352
|
}
|
|
45129
|
-
const fields =
|
|
45353
|
+
const fields = isRecord2(parsed) && Array.isArray(parsed.fields) ? parsed.fields : null;
|
|
45130
45354
|
const isValidShape = fields !== null && fields.length === prose.length && fields.every(
|
|
45131
|
-
(entry, i) =>
|
|
45355
|
+
(entry, i) => isRecord2(entry) && typeof entry.path === "string" && typeof entry.masked === "string" && entry.path === prose[i]?.path && resolvePath(artifact, entry.path) !== null
|
|
45132
45356
|
);
|
|
45133
45357
|
if (!isValidShape || fields === null) {
|
|
45134
45358
|
return { ok: true, gated: true, artifact: withheldExcerptClone(artifact) };
|
|
@@ -49350,27 +49574,27 @@ function redact(input) {
|
|
|
49350
49574
|
|
|
49351
49575
|
// src/telemetry/projector.ts
|
|
49352
49576
|
var import_node_crypto8 = require("node:crypto");
|
|
49353
|
-
function
|
|
49577
|
+
function isRecord3(x) {
|
|
49354
49578
|
return typeof x === "object" && x !== null;
|
|
49355
49579
|
}
|
|
49356
49580
|
function buildProjection(payload, type, ms, telemetry) {
|
|
49357
|
-
if (!
|
|
49581
|
+
if (!isRecord3(payload)) return null;
|
|
49358
49582
|
const seqRaw = payload.seq;
|
|
49359
49583
|
const turnIdRaw = payload.turnId;
|
|
49360
49584
|
const maybeSeq = typeof seqRaw === "number" ? seqRaw : void 0;
|
|
49361
49585
|
const maybeTurnId = typeof turnIdRaw === "string" ? turnIdRaw : void 0;
|
|
49362
49586
|
if (type === "event") {
|
|
49363
49587
|
const rawEvent = payload.event;
|
|
49364
|
-
if (!
|
|
49588
|
+
if (!isRecord3(rawEvent) || typeof rawEvent.type !== "string") return null;
|
|
49365
49589
|
if (rawEvent.type === "tool_use") {
|
|
49366
49590
|
const tool = rawEvent.tool;
|
|
49367
|
-
if (!
|
|
49591
|
+
if (!isRecord3(tool)) return null;
|
|
49368
49592
|
const toolId = tool.id;
|
|
49369
49593
|
const toolName = tool.name;
|
|
49370
49594
|
if (typeof toolId !== "string" || typeof toolName !== "string") return null;
|
|
49371
49595
|
const input = tool.input;
|
|
49372
|
-
const argKeys =
|
|
49373
|
-
const projectSlug =
|
|
49596
|
+
const argKeys = isRecord3(input) ? Object.keys(input) : [];
|
|
49597
|
+
const projectSlug = isRecord3(input) && typeof input.projectSlug === "string" ? input.projectSlug : void 0;
|
|
49374
49598
|
const proj = { kind: "tool_use", toolId, toolName, argKeys, seq: maybeSeq ?? 0 };
|
|
49375
49599
|
if (maybeTurnId !== void 0) proj.turnId = maybeTurnId;
|
|
49376
49600
|
if (ms !== void 0) proj.ms = ms;
|
|
@@ -49379,7 +49603,7 @@ function buildProjection(payload, type, ms, telemetry) {
|
|
|
49379
49603
|
}
|
|
49380
49604
|
if (rawEvent.type === "tool_result") {
|
|
49381
49605
|
const tool = rawEvent.tool;
|
|
49382
|
-
if (!
|
|
49606
|
+
if (!isRecord3(tool)) return null;
|
|
49383
49607
|
const toolId = tool.id;
|
|
49384
49608
|
if (typeof toolId !== "string") return null;
|
|
49385
49609
|
const exitCode = tool.exitCode;
|
|
@@ -49427,7 +49651,7 @@ function buildProjection(payload, type, ms, telemetry) {
|
|
|
49427
49651
|
const proj = { kind: "plumbing", plumbingKind: "rate_limit_status", seq: maybeSeq ?? 0 };
|
|
49428
49652
|
if (maybeTurnId !== void 0) proj.turnId = maybeTurnId;
|
|
49429
49653
|
if (ms !== void 0) proj.ms = ms;
|
|
49430
|
-
if (
|
|
49654
|
+
if (isRecord3(rateLimits)) proj.rateLimitReached = rateLimits.rateLimitReachedType != null;
|
|
49431
49655
|
return proj;
|
|
49432
49656
|
}
|
|
49433
49657
|
const bracketMatch = /^<([a-z-]+)>$/.exec(type);
|
|
@@ -54680,7 +54904,7 @@ function dayFilesInWindow(now, windowMs) {
|
|
|
54680
54904
|
}
|
|
54681
54905
|
return files;
|
|
54682
54906
|
}
|
|
54683
|
-
function
|
|
54907
|
+
function isRecord4(value) {
|
|
54684
54908
|
return typeof value === "object" && value !== null;
|
|
54685
54909
|
}
|
|
54686
54910
|
function extractItems(lines2, sessionIds) {
|
|
@@ -54698,7 +54922,7 @@ function extractItems(lines2, sessionIds) {
|
|
|
54698
54922
|
skipped2++;
|
|
54699
54923
|
continue;
|
|
54700
54924
|
}
|
|
54701
|
-
if (!
|
|
54925
|
+
if (!isRecord4(parsed)) {
|
|
54702
54926
|
skipped2++;
|
|
54703
54927
|
continue;
|
|
54704
54928
|
}
|
|
@@ -54707,11 +54931,11 @@ function extractItems(lines2, sessionIds) {
|
|
|
54707
54931
|
const ts = obj.ts;
|
|
54708
54932
|
const type = obj.type;
|
|
54709
54933
|
const payload = obj.payload;
|
|
54710
|
-
if (type === "turn" &&
|
|
54934
|
+
if (type === "turn" && isRecord4(payload) && typeof payload.message === "string") {
|
|
54711
54935
|
items.push({ ts, kind: "user", text: payload.message });
|
|
54712
|
-
} else if (type === "event" &&
|
|
54936
|
+
} else if (type === "event" && isRecord4(payload) && isRecord4(payload.event) && payload.event.type === "assistant" && typeof payload.event.content === "string") {
|
|
54713
54937
|
items.push({ ts, kind: "assistant", text: payload.event.content });
|
|
54714
|
-
} else if (type === "event" &&
|
|
54938
|
+
} else if (type === "event" && isRecord4(payload) && isRecord4(payload.event) && payload.event.type === "tool_use" && isRecord4(payload.event.tool) && typeof payload.event.tool.name === "string") {
|
|
54715
54939
|
items.push({ ts, kind: "tool", text: payload.event.tool.name });
|
|
54716
54940
|
}
|
|
54717
54941
|
}
|
|
@@ -54804,7 +55028,7 @@ async function readSessionRows(logsDir, files, sessionIds) {
|
|
|
54804
55028
|
stream.destroy();
|
|
54805
55029
|
}
|
|
54806
55030
|
} catch (e) {
|
|
54807
|
-
if (
|
|
55031
|
+
if (isRecord4(e) && e.code === "ENOENT") {
|
|
54808
55032
|
missingFiles++;
|
|
54809
55033
|
continue;
|
|
54810
55034
|
}
|
|
@@ -57402,10 +57626,10 @@ async function startDirectorBridge() {
|
|
|
57402
57626
|
migrateRetiredApiBaseUrls();
|
|
57403
57627
|
} catch {
|
|
57404
57628
|
}
|
|
57405
|
-
const apiBaseUrl =
|
|
57629
|
+
const apiBaseUrl = readDirectorBridgeApiBaseUrl();
|
|
57406
57630
|
if (!isRemoteApiAllowed(apiBaseUrl)) {
|
|
57407
57631
|
throw new Error(
|
|
57408
|
-
`Refusing to run the director-bridge execution consumer against
|
|
57632
|
+
`Refusing to run the director-bridge execution consumer against untrusted remote API URL ${apiBaseUrl}. Set MRRLIN_API_ALLOW_REMOTE=1 for a custom remote, or set MRRLIN_API_BASE_URL=https://api-staging.mrrlin.com for the official staging API.`
|
|
57409
57633
|
);
|
|
57410
57634
|
}
|
|
57411
57635
|
const envToken = process.env.MRRLIN_TOKEN?.trim() || void 0;
|
|
@@ -64607,7 +64831,7 @@ async function invokeCapability(server, toolName, payload) {
|
|
|
64607
64831
|
await client.connect(transport);
|
|
64608
64832
|
result = await client.callTool({
|
|
64609
64833
|
name: toolName,
|
|
64610
|
-
arguments:
|
|
64834
|
+
arguments: isRecord5(payload) ? payload : void 0
|
|
64611
64835
|
});
|
|
64612
64836
|
} catch (error51) {
|
|
64613
64837
|
throw classifyInvokeError(error51, server.url, toolName);
|
|
@@ -64659,7 +64883,7 @@ function classifyInvokeError(error51, url2, toolName) {
|
|
|
64659
64883
|
if (error51 instanceof TypeError) return new InvokeUnreachableError(url2, error51);
|
|
64660
64884
|
return new InvokeMalformedResultError(error51 instanceof Error ? error51.message : String(error51), error51);
|
|
64661
64885
|
}
|
|
64662
|
-
function
|
|
64886
|
+
function isRecord5(value) {
|
|
64663
64887
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
64664
64888
|
}
|
|
64665
64889
|
function extractFirstUrl(content) {
|
|
@@ -64668,7 +64892,7 @@ function extractFirstUrl(content) {
|
|
|
64668
64892
|
function extractFirstText(content) {
|
|
64669
64893
|
if (Array.isArray(content)) {
|
|
64670
64894
|
for (const block of content) {
|
|
64671
|
-
if (
|
|
64895
|
+
if (isRecord5(block) && typeof block.text === "string") return block.text;
|
|
64672
64896
|
}
|
|
64673
64897
|
}
|
|
64674
64898
|
return JSON.stringify(content);
|
|
@@ -64685,7 +64909,7 @@ function findFirstMatch(value, pattern) {
|
|
|
64685
64909
|
}
|
|
64686
64910
|
return null;
|
|
64687
64911
|
}
|
|
64688
|
-
if (
|
|
64912
|
+
if (isRecord5(value)) {
|
|
64689
64913
|
for (const nested of Object.values(value)) {
|
|
64690
64914
|
const found = findFirstMatch(nested, pattern);
|
|
64691
64915
|
if (found) return found;
|
|
@@ -64906,7 +65130,7 @@ async function recordUse(client, route, input, outcome) {
|
|
|
64906
65130
|
}
|
|
64907
65131
|
}
|
|
64908
65132
|
async function callDestination(client, projectSlug, route, payload, mcpConfigPath) {
|
|
64909
|
-
const operation =
|
|
65133
|
+
const operation = isRecord6(payload) ? payload.operation : void 0;
|
|
64910
65134
|
if (route.capability === "wiki") {
|
|
64911
65135
|
if (operation !== "create" && operation !== "update") {
|
|
64912
65136
|
throw new Error(`callDestination: payload.operation "${String(operation)}" is not valid for wiki`);
|
|
@@ -64923,7 +65147,7 @@ async function callDestination(client, projectSlug, route, payload, mcpConfigPat
|
|
|
64923
65147
|
}
|
|
64924
65148
|
throw new Error(`callDestination: no destination wired for capability "${route.capability}"`);
|
|
64925
65149
|
}
|
|
64926
|
-
function
|
|
65150
|
+
function isRecord6(value) {
|
|
64927
65151
|
return typeof value === "object" && value !== null;
|
|
64928
65152
|
}
|
|
64929
65153
|
|
|
@@ -65973,7 +66197,7 @@ var import_node_path31 = require("node:path");
|
|
|
65973
66197
|
function evidenceDedupKey(sessionId, reportId) {
|
|
65974
66198
|
return `${sessionId}:${reportId}`;
|
|
65975
66199
|
}
|
|
65976
|
-
function
|
|
66200
|
+
function isRecord7(v) {
|
|
65977
66201
|
return v !== null && typeof v === "object";
|
|
65978
66202
|
}
|
|
65979
66203
|
async function readDayLines(stateDir, day, sessionId) {
|
|
@@ -65992,7 +66216,7 @@ async function readDayLines(stateDir, day, sessionId) {
|
|
|
65992
66216
|
} catch {
|
|
65993
66217
|
continue;
|
|
65994
66218
|
}
|
|
65995
|
-
if (!
|
|
66219
|
+
if (!isRecord7(rec) || !isRecord7(rec.payload)) continue;
|
|
65996
66220
|
if (rec.payload.directorSessionId !== sessionId) continue;
|
|
65997
66221
|
kept.push(rec);
|
|
65998
66222
|
}
|
|
@@ -66062,7 +66286,7 @@ async function assembleEvidence(stateDir, sessionId, reportId, gateCtx) {
|
|
|
66062
66286
|
} catch {
|
|
66063
66287
|
continue;
|
|
66064
66288
|
}
|
|
66065
|
-
if (!
|
|
66289
|
+
if (!isRecord7(rec) || !Array.isArray(rec.daysRemoved)) continue;
|
|
66066
66290
|
for (const removedDay of rec.daysRemoved) {
|
|
66067
66291
|
if (daysRead.includes(removedDay)) {
|
|
66068
66292
|
windowPruned = true;
|
|
@@ -68115,7 +68339,7 @@ function resolveServerOptions(input = {}) {
|
|
|
68115
68339
|
const remoteAllowed = isRemoteApiAllowed(apiBaseUrl) || input.allowRemoteApi === true;
|
|
68116
68340
|
if (!remoteAllowed) {
|
|
68117
68341
|
throw new Error(
|
|
68118
|
-
`Refusing to start MCP server against non-loopback URL ${apiBaseUrl}. Set MRRLIN_API_ALLOW_REMOTE=1 or
|
|
68342
|
+
`Refusing to start MCP server against non-loopback URL ${apiBaseUrl}. Set MRRLIN_API_ALLOW_REMOTE=1 for a custom remote, or set MRRLIN_API_BASE_URL=https://api-staging.mrrlin.com for the official staging API.`
|
|
68119
68343
|
);
|
|
68120
68344
|
}
|
|
68121
68345
|
return {
|
|
@@ -68297,6 +68521,50 @@ function readExistingTelemetryDefaultProject(deps, ecoPath) {
|
|
|
68297
68521
|
return void 0;
|
|
68298
68522
|
}
|
|
68299
68523
|
}
|
|
68524
|
+
function isOwnedEcosystemConfig(text) {
|
|
68525
|
+
return /name:\s*"mrrlin-bridge"/.test(text) && /script:\s*"mrrlin-mcp"/.test(text) && /args:\s*"director-bridge"/.test(text);
|
|
68526
|
+
}
|
|
68527
|
+
function readEnvString(text, key) {
|
|
68528
|
+
const match = new RegExp(`\\b${key}:\\s*("(?:[^"\\\\]|\\\\.)*")`).exec(text);
|
|
68529
|
+
if (!match?.[1]) return void 0;
|
|
68530
|
+
try {
|
|
68531
|
+
const value = JSON.parse(match[1]);
|
|
68532
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
68533
|
+
} catch {
|
|
68534
|
+
return void 0;
|
|
68535
|
+
}
|
|
68536
|
+
}
|
|
68537
|
+
function normalizeServiceApiTarget(apiBaseUrl, allowRemote) {
|
|
68538
|
+
const normalized = retargetRetiredApiBaseUrl(apiBaseUrl).baseUrl;
|
|
68539
|
+
if (isProductionApiBaseUrl(normalized)) return {};
|
|
68540
|
+
const target = { apiBaseUrl: normalized };
|
|
68541
|
+
if (allowRemote && !isStagingApiBaseUrl(normalized) && !isLoopbackUrl(normalized)) target.apiAllowRemote = true;
|
|
68542
|
+
return target;
|
|
68543
|
+
}
|
|
68544
|
+
function readExistingServiceApiTarget(deps, ecoPath) {
|
|
68545
|
+
try {
|
|
68546
|
+
const existing = deps.readFile?.(ecoPath);
|
|
68547
|
+
if (!existing || !isOwnedEcosystemConfig(existing)) return void 0;
|
|
68548
|
+
const apiBaseUrl = readEnvString(existing, "MRRLIN_API_BASE_URL");
|
|
68549
|
+
if (apiBaseUrl) {
|
|
68550
|
+
return normalizeServiceApiTarget(apiBaseUrl, readEnvString(existing, "MRRLIN_API_ALLOW_REMOTE") === "1");
|
|
68551
|
+
}
|
|
68552
|
+
if (readEnvString(existing, "MRRLIN_STAGING") === "1") {
|
|
68553
|
+
return { apiBaseUrl: STAGING_API_BASE_URL };
|
|
68554
|
+
}
|
|
68555
|
+
return void 0;
|
|
68556
|
+
} catch {
|
|
68557
|
+
return void 0;
|
|
68558
|
+
}
|
|
68559
|
+
}
|
|
68560
|
+
function deriveServiceApiTarget(deps, ecoPath) {
|
|
68561
|
+
const apiBaseUrl = deps.env.MRRLIN_API_BASE_URL?.trim();
|
|
68562
|
+
if (apiBaseUrl) return normalizeServiceApiTarget(apiBaseUrl, deps.env.MRRLIN_API_ALLOW_REMOTE === "1");
|
|
68563
|
+
if (deps.env.MRRLIN_STAGING === "1" || bridgeUrlImpliesStaging(deps.env.MRRLIN_BRIDGE_URL)) {
|
|
68564
|
+
return { apiBaseUrl: STAGING_API_BASE_URL };
|
|
68565
|
+
}
|
|
68566
|
+
return readExistingServiceApiTarget(deps, ecoPath) ?? {};
|
|
68567
|
+
}
|
|
68300
68568
|
var SYSTEM_PATH_DIRS = ["/usr/bin", "/bin", "/usr/sbin", "/sbin"];
|
|
68301
68569
|
function defaultHomeOwnerUid(home) {
|
|
68302
68570
|
try {
|
|
@@ -68363,9 +68631,8 @@ function installService(deps) {
|
|
|
68363
68631
|
const pathEnv = Array.from(
|
|
68364
68632
|
/* @__PURE__ */ new Set([...resolvedBins.map((b) => import_node_path33.default.dirname(b)), ...SYSTEM_PATH_DIRS])
|
|
68365
68633
|
).join(":");
|
|
68366
|
-
const apiBaseUrl = deps.env.MRRLIN_API_BASE_URL?.trim() || void 0;
|
|
68367
|
-
const staging = deps.env.MRRLIN_STAGING === "1" || !apiBaseUrl && bridgeUrlImpliesStaging(deps.env.MRRLIN_BRIDGE_URL);
|
|
68368
68634
|
const ecoPath = import_node_path33.default.join(home, ".mrrlin", "ecosystem.config.cjs");
|
|
68635
|
+
const apiTarget = deriveServiceApiTarget(deps, ecoPath);
|
|
68369
68636
|
const telemetryDefaultProject = "MRRLIN_TELEMETRY_DEFAULT_PROJECT" in deps.env ? deps.env.MRRLIN_TELEMETRY_DEFAULT_PROJECT?.trim() || void 0 : readExistingTelemetryDefaultProject(deps, ecoPath);
|
|
68370
68637
|
const text = renderEcosystemConfig({
|
|
68371
68638
|
cwd: resolved.cwd,
|
|
@@ -68373,8 +68640,8 @@ function installService(deps) {
|
|
|
68373
68640
|
user,
|
|
68374
68641
|
codexHome: deps.env.CODEX_HOME?.trim() || void 0,
|
|
68375
68642
|
pathEnv,
|
|
68376
|
-
|
|
68377
|
-
|
|
68643
|
+
apiBaseUrl: apiTarget.apiBaseUrl,
|
|
68644
|
+
apiAllowRemote: apiTarget.apiAllowRemote,
|
|
68378
68645
|
telemetryDefaultProject
|
|
68379
68646
|
});
|
|
68380
68647
|
deps.writeFile(ecoPath, text);
|
|
@@ -68512,7 +68779,7 @@ function parseArgs(args) {
|
|
|
68512
68779
|
}
|
|
68513
68780
|
return map2;
|
|
68514
68781
|
}
|
|
68515
|
-
function
|
|
68782
|
+
function isRecord8(v) {
|
|
68516
68783
|
return v !== null && typeof v === "object";
|
|
68517
68784
|
}
|
|
68518
68785
|
async function runExtract(args, deps) {
|
|
@@ -68549,8 +68816,8 @@ Missing required flag(s): ${missing.join(
|
|
|
68549
68816
|
} catch {
|
|
68550
68817
|
continue;
|
|
68551
68818
|
}
|
|
68552
|
-
const payload =
|
|
68553
|
-
if (
|
|
68819
|
+
const payload = isRecord8(parsed) ? parsed.payload : void 0;
|
|
68820
|
+
if (isRecord8(payload) && payload.directorSessionId === sessionId) {
|
|
68554
68821
|
matchingLines.push(line);
|
|
68555
68822
|
}
|
|
68556
68823
|
}
|
|
@@ -68658,8 +68925,9 @@ Usage:
|
|
|
68658
68925
|
mrrlin-mcp serve Run the stdio MCP server. Honors
|
|
68659
68926
|
MRRLIN_API_BASE_URL (default http://127.0.0.1:8787)
|
|
68660
68927
|
and MRRLIN_TOKEN. Non-loopback API URLs require
|
|
68661
|
-
MRRLIN_API_ALLOW_REMOTE=1
|
|
68662
|
-
|
|
68928
|
+
MRRLIN_API_ALLOW_REMOTE=1 for a custom remote. Use
|
|
68929
|
+
MRRLIN_API_BASE_URL=https://api-staging.mrrlin.com
|
|
68930
|
+
to target the known staging API.
|
|
68663
68931
|
|
|
68664
68932
|
mrrlin-mcp install-browser Idempotently install the dedicated, pinned
|
|
68665
68933
|
Chromium into ~/.mrrlin/browser/browsers (its own
|
|
@@ -68695,16 +68963,16 @@ Usage:
|
|
|
68695
68963
|
Bakes API-target env vars into the block as an
|
|
68696
68964
|
inline env table so \`serve\` boots even when the
|
|
68697
68965
|
local 127.0.0.1:8787 is not running. Setup guide:
|
|
68698
|
-
|
|
68699
|
-
is the canonical path
|
|
68700
|
-
allow gate in one flag). For a non-staging remote,
|
|
68966
|
+
MRRLIN_API_BASE_URL=https://api-staging.mrrlin.com mrrlin-mcp configure bridge
|
|
68967
|
+
is the canonical staging path. For a custom remote,
|
|
68701
68968
|
pass BOTH the URL AND the allow gate:
|
|
68702
68969
|
MRRLIN_API_BASE_URL=https://... \\
|
|
68703
68970
|
MRRLIN_API_ALLOW_REMOTE=1 mrrlin-mcp configure bridge
|
|
68704
68971
|
Without an allow gate, \`serve\` still refuses any
|
|
68705
68972
|
non-loopback URL (see \`mrrlin-mcp serve\` above).
|
|
68706
|
-
URL
|
|
68707
|
-
|
|
68973
|
+
An explicit URL is the only routing selector at bake
|
|
68974
|
+
time. Production needs no routing env; a custom
|
|
68975
|
+
remote also needs MRRLIN_API_ALLOW_REMOTE=1.
|
|
68708
68976
|
Re-running BARE (no env vars) over a block you
|
|
68709
68977
|
already pinned to staging/a remote target is safe:
|
|
68710
68978
|
it keeps your env verbatim (reported as
|
|
@@ -68738,8 +69006,9 @@ Usage:
|
|
|
68738
69006
|
pending runs, read-only). Consumer env:
|
|
68739
69007
|
- MRRLIN_API_BASE_URL (default http://127.0.0.1:8787;
|
|
68740
69008
|
non-loopback requires MRRLIN_API_ALLOW_REMOTE=1,
|
|
68741
|
-
or
|
|
68742
|
-
-
|
|
69009
|
+
or an explicit staging API URL)
|
|
69010
|
+
- MRRLIN_API_BASE_URL=https://api-staging.mrrlin.com
|
|
69011
|
+
(known staging API)
|
|
68743
69012
|
- MRRLIN_TOKEN (service-principal auth to the API;
|
|
68744
69013
|
if unset, chat still works but claim-sweep /
|
|
68745
69014
|
autonomous execution stays disabled)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrlin-dev/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.57",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -21,9 +21,9 @@
|
|
|
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/agent-client": "0.0.0",
|
|
25
26
|
"@mrrlin/director-e2e": "0.0.0",
|
|
26
|
-
"@mrrlin/client": "0.0.0",
|
|
27
27
|
"@mrrlin/schemas": "0.0.0",
|
|
28
28
|
"@mrrlin/tsconfig": "0.0.0",
|
|
29
29
|
"@mrrlin/wiki": "0.0.0"
|