@mrrlin-dev/mcp 0.4.54 → 0.4.55
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 +336 -85
- package/package.json +3 -3
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
|
}
|
|
@@ -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.55";
|
|
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,7 +42757,7 @@ 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" };
|
|
42760
|
+
if (!host && !hasLiveLegacyStagingFlag(text, "toml")) return { file: file2, status: "clean" };
|
|
42626
42761
|
const managedBlockRe = /^\[mcp_servers\.mrrlin\][^\n]*\n(?:(?!\[)[^\n]*\n?)*/gm;
|
|
42627
42762
|
const blocks = text.match(managedBlockRe) ?? [];
|
|
42628
42763
|
if (blocks.length === 0) {
|
|
@@ -42633,21 +42768,39 @@ 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) {
|
|
42639
42776
|
let blockChanged = false;
|
|
42640
42777
|
const healedBlock = block.split("\n").map((line) => {
|
|
42641
42778
|
if (line.trimStart().startsWith("#")) return line;
|
|
42642
|
-
|
|
42643
|
-
|
|
42644
|
-
|
|
42645
|
-
|
|
42779
|
+
let nextApiUrl;
|
|
42780
|
+
let healedLine = line.replace(assignRe, (match, url2) => {
|
|
42781
|
+
const successor = migratedApiUrl(url2);
|
|
42782
|
+
if (!successor) return match;
|
|
42783
|
+
nextApiUrl = successor;
|
|
42784
|
+
return `MRRLIN_API_BASE_URL = ${JSON.stringify(successor)}`;
|
|
42785
|
+
});
|
|
42786
|
+
healedLine = healedLine.replace(stagingAssignRe, () => {
|
|
42787
|
+
nextApiUrl = STAGING_API_BASE_URL;
|
|
42788
|
+
return `MRRLIN_API_BASE_URL = ${JSON.stringify(STAGING_API_BASE_URL)}`;
|
|
42789
|
+
});
|
|
42790
|
+
if (nextApiUrl && isFirstPartyOrLoopbackUrl(nextApiUrl)) {
|
|
42791
|
+
healedLine = healedLine.replace(
|
|
42792
|
+
allowRemoteAssignRe,
|
|
42793
|
+
(match) => match.startsWith(",") && match.endsWith(",") ? "," : ""
|
|
42794
|
+
).replace(/\{\s*,\s*/, "{ ").replace(/,\s*\}/, " }").replace(/,\s*,/g, ",");
|
|
42795
|
+
}
|
|
42646
42796
|
if (healedLine !== line) blockChanged = true;
|
|
42647
42797
|
return healedLine;
|
|
42648
42798
|
}).join("\n");
|
|
42649
42799
|
if (!blockChanged) continue;
|
|
42650
|
-
const deduped = healedBlock.replace(
|
|
42800
|
+
const deduped = healedBlock.replace(
|
|
42801
|
+
/MRRLIN_API_BASE_URL = "https:\/\/api-staging\.mrrlin\.com"\s*,\s*MRRLIN_API_BASE_URL = "https:\/\/api-staging\.mrrlin\.com"/g,
|
|
42802
|
+
`MRRLIN_API_BASE_URL = "https://api-staging.mrrlin.com"`
|
|
42803
|
+
);
|
|
42651
42804
|
out = out.replace(block, () => deduped);
|
|
42652
42805
|
changed += 1;
|
|
42653
42806
|
}
|
|
@@ -42660,14 +42813,67 @@ function migrateCodexConfig(deps = {}) {
|
|
|
42660
42813
|
}
|
|
42661
42814
|
backupOnce(file2, text, d);
|
|
42662
42815
|
d.writeFile(file2, out);
|
|
42663
|
-
return {
|
|
42816
|
+
return {
|
|
42817
|
+
file: file2,
|
|
42818
|
+
status: "migrated",
|
|
42819
|
+
detail: "rewrote legacy API routing env to explicit URL form"
|
|
42820
|
+
};
|
|
42821
|
+
}
|
|
42822
|
+
function migrateClaudeConfig(deps = {}) {
|
|
42823
|
+
const d = defaultDeps(deps);
|
|
42824
|
+
const file2 = import_node_path8.default.join(d.home, ".claude.json");
|
|
42825
|
+
if (!d.exists(file2)) return { file: file2, status: "absent" };
|
|
42826
|
+
const text = d.readFile(file2);
|
|
42827
|
+
let parsed;
|
|
42828
|
+
try {
|
|
42829
|
+
parsed = text.trim() === "" ? {} : JSON.parse(text);
|
|
42830
|
+
} catch (error51) {
|
|
42831
|
+
const detail2 = error51 instanceof Error ? error51.message : String(error51);
|
|
42832
|
+
return { file: file2, status: "failed", detail: `malformed JSON: ${detail2}` };
|
|
42833
|
+
}
|
|
42834
|
+
if (!isRecord(parsed) || !isRecord(parsed.mcpServers)) {
|
|
42835
|
+
return { file: file2, status: "clean" };
|
|
42836
|
+
}
|
|
42837
|
+
const entry = parsed.mcpServers.mrrlin;
|
|
42838
|
+
if (!isRecord(entry) || entry.command !== "mrrlin-mcp" || !Array.isArray(entry.args) || entry.args.length !== 1 || entry.args[0] !== "serve") {
|
|
42839
|
+
return { file: file2, status: "clean" };
|
|
42840
|
+
}
|
|
42841
|
+
const env = entry.env;
|
|
42842
|
+
if (!isRecord(env)) return { file: file2, status: "clean" };
|
|
42843
|
+
const envKeys = Object.keys(env);
|
|
42844
|
+
const routingKeys = /* @__PURE__ */ new Set(["MRRLIN_STAGING", "MRRLIN_API_BASE_URL", "MRRLIN_API_ALLOW_REMOTE"]);
|
|
42845
|
+
if (envKeys.length === 0 || !envKeys.every((key) => routingKeys.has(key)) || !envKeys.every((key) => typeof env[key] === "string")) {
|
|
42846
|
+
return { file: file2, status: "clean" };
|
|
42847
|
+
}
|
|
42848
|
+
let nextEnv;
|
|
42849
|
+
let detail;
|
|
42850
|
+
if (env.MRRLIN_STAGING === "1" && envKeys.length === 1) {
|
|
42851
|
+
nextEnv = { MRRLIN_API_BASE_URL: STAGING_API_BASE_URL };
|
|
42852
|
+
detail = "replaced the legacy staging flag with the explicit API URL";
|
|
42853
|
+
} else if (typeof env.MRRLIN_API_BASE_URL === "string" && (envKeys.length === 1 || envKeys.length === 2 && env.MRRLIN_API_ALLOW_REMOTE === "1")) {
|
|
42854
|
+
const nextUrl = migratedApiUrl(env.MRRLIN_API_BASE_URL);
|
|
42855
|
+
if (!nextUrl) return { file: file2, status: "clean" };
|
|
42856
|
+
nextEnv = isProductionApiBaseUrl(nextUrl) ? {} : { MRRLIN_API_BASE_URL: nextUrl };
|
|
42857
|
+
detail = isProductionApiBaseUrl(nextUrl) ? "removed the retired production routing override" : "retargeted the retired API URL";
|
|
42858
|
+
}
|
|
42859
|
+
if (!nextEnv) return { file: file2, status: "clean" };
|
|
42860
|
+
backupOnce(file2, text, d);
|
|
42861
|
+
const next = JSON.parse(JSON.stringify(parsed));
|
|
42862
|
+
const nextServers = next.mcpServers;
|
|
42863
|
+
const nextEntry = nextServers.mrrlin;
|
|
42864
|
+
if (Object.keys(nextEnv).length === 0) delete nextEntry.env;
|
|
42865
|
+
else nextEntry.env = nextEnv;
|
|
42866
|
+
d.writeFile(file2, `${JSON.stringify(next, null, 2)}
|
|
42867
|
+
`);
|
|
42868
|
+
return { file: file2, status: "migrated", detail };
|
|
42664
42869
|
}
|
|
42665
42870
|
function migrateRetiredApiBaseUrls(deps = {}) {
|
|
42666
42871
|
const d = defaultDeps(deps);
|
|
42667
42872
|
const results = [];
|
|
42668
42873
|
const steps = [
|
|
42669
42874
|
{ 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") }
|
|
42875
|
+
{ run: migrateCodexConfig, file: import_node_path8.default.join(d.codexHome, "config.toml") },
|
|
42876
|
+
{ run: migrateClaudeConfig, file: import_node_path8.default.join(d.home, ".claude.json") }
|
|
42671
42877
|
];
|
|
42672
42878
|
for (const step of steps) {
|
|
42673
42879
|
try {
|
|
@@ -45062,7 +45268,7 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
45062
45268
|
}
|
|
45063
45269
|
|
|
45064
45270
|
// src/telemetry/pii-gate.ts
|
|
45065
|
-
function
|
|
45271
|
+
function isRecord2(v) {
|
|
45066
45272
|
return v !== null && typeof v === "object";
|
|
45067
45273
|
}
|
|
45068
45274
|
function resolvePath(root, path24) {
|
|
@@ -45074,7 +45280,7 @@ function resolvePath(root, path24) {
|
|
|
45074
45280
|
if (Array.isArray(cur) && Number.isInteger(idx)) {
|
|
45075
45281
|
if (idx < 0 || idx >= cur.length) return null;
|
|
45076
45282
|
cur = cur[idx];
|
|
45077
|
-
} else if (
|
|
45283
|
+
} else if (isRecord2(cur) && part in cur) {
|
|
45078
45284
|
cur = cur[part];
|
|
45079
45285
|
} else {
|
|
45080
45286
|
return null;
|
|
@@ -45086,7 +45292,7 @@ function resolvePath(root, path24) {
|
|
|
45086
45292
|
if (lastIdx < 0 || lastIdx >= cur.length) return null;
|
|
45087
45293
|
return { parent: cur, key: lastIdx };
|
|
45088
45294
|
}
|
|
45089
|
-
if (
|
|
45295
|
+
if (isRecord2(cur) && last in cur) {
|
|
45090
45296
|
return { parent: cur, key: last };
|
|
45091
45297
|
}
|
|
45092
45298
|
return null;
|
|
@@ -45095,7 +45301,7 @@ function withheldExcerptClone(artifact) {
|
|
|
45095
45301
|
const clone2 = { ...artifact };
|
|
45096
45302
|
if (Array.isArray(clone2.steps)) {
|
|
45097
45303
|
clone2.steps = clone2.steps.map((step) => {
|
|
45098
|
-
if (!
|
|
45304
|
+
if (!isRecord2(step)) return step;
|
|
45099
45305
|
const copy = { ...step };
|
|
45100
45306
|
delete copy.excerpt;
|
|
45101
45307
|
return copy;
|
|
@@ -45126,9 +45332,9 @@ async function gate(artifact, prose, ctx) {
|
|
|
45126
45332
|
} catch {
|
|
45127
45333
|
return { ok: true, gated: true, artifact: withheldExcerptClone(artifact) };
|
|
45128
45334
|
}
|
|
45129
|
-
const fields =
|
|
45335
|
+
const fields = isRecord2(parsed) && Array.isArray(parsed.fields) ? parsed.fields : null;
|
|
45130
45336
|
const isValidShape = fields !== null && fields.length === prose.length && fields.every(
|
|
45131
|
-
(entry, i) =>
|
|
45337
|
+
(entry, i) => isRecord2(entry) && typeof entry.path === "string" && typeof entry.masked === "string" && entry.path === prose[i]?.path && resolvePath(artifact, entry.path) !== null
|
|
45132
45338
|
);
|
|
45133
45339
|
if (!isValidShape || fields === null) {
|
|
45134
45340
|
return { ok: true, gated: true, artifact: withheldExcerptClone(artifact) };
|
|
@@ -49350,27 +49556,27 @@ function redact(input) {
|
|
|
49350
49556
|
|
|
49351
49557
|
// src/telemetry/projector.ts
|
|
49352
49558
|
var import_node_crypto8 = require("node:crypto");
|
|
49353
|
-
function
|
|
49559
|
+
function isRecord3(x) {
|
|
49354
49560
|
return typeof x === "object" && x !== null;
|
|
49355
49561
|
}
|
|
49356
49562
|
function buildProjection(payload, type, ms, telemetry) {
|
|
49357
|
-
if (!
|
|
49563
|
+
if (!isRecord3(payload)) return null;
|
|
49358
49564
|
const seqRaw = payload.seq;
|
|
49359
49565
|
const turnIdRaw = payload.turnId;
|
|
49360
49566
|
const maybeSeq = typeof seqRaw === "number" ? seqRaw : void 0;
|
|
49361
49567
|
const maybeTurnId = typeof turnIdRaw === "string" ? turnIdRaw : void 0;
|
|
49362
49568
|
if (type === "event") {
|
|
49363
49569
|
const rawEvent = payload.event;
|
|
49364
|
-
if (!
|
|
49570
|
+
if (!isRecord3(rawEvent) || typeof rawEvent.type !== "string") return null;
|
|
49365
49571
|
if (rawEvent.type === "tool_use") {
|
|
49366
49572
|
const tool = rawEvent.tool;
|
|
49367
|
-
if (!
|
|
49573
|
+
if (!isRecord3(tool)) return null;
|
|
49368
49574
|
const toolId = tool.id;
|
|
49369
49575
|
const toolName = tool.name;
|
|
49370
49576
|
if (typeof toolId !== "string" || typeof toolName !== "string") return null;
|
|
49371
49577
|
const input = tool.input;
|
|
49372
|
-
const argKeys =
|
|
49373
|
-
const projectSlug =
|
|
49578
|
+
const argKeys = isRecord3(input) ? Object.keys(input) : [];
|
|
49579
|
+
const projectSlug = isRecord3(input) && typeof input.projectSlug === "string" ? input.projectSlug : void 0;
|
|
49374
49580
|
const proj = { kind: "tool_use", toolId, toolName, argKeys, seq: maybeSeq ?? 0 };
|
|
49375
49581
|
if (maybeTurnId !== void 0) proj.turnId = maybeTurnId;
|
|
49376
49582
|
if (ms !== void 0) proj.ms = ms;
|
|
@@ -49379,7 +49585,7 @@ function buildProjection(payload, type, ms, telemetry) {
|
|
|
49379
49585
|
}
|
|
49380
49586
|
if (rawEvent.type === "tool_result") {
|
|
49381
49587
|
const tool = rawEvent.tool;
|
|
49382
|
-
if (!
|
|
49588
|
+
if (!isRecord3(tool)) return null;
|
|
49383
49589
|
const toolId = tool.id;
|
|
49384
49590
|
if (typeof toolId !== "string") return null;
|
|
49385
49591
|
const exitCode = tool.exitCode;
|
|
@@ -49427,7 +49633,7 @@ function buildProjection(payload, type, ms, telemetry) {
|
|
|
49427
49633
|
const proj = { kind: "plumbing", plumbingKind: "rate_limit_status", seq: maybeSeq ?? 0 };
|
|
49428
49634
|
if (maybeTurnId !== void 0) proj.turnId = maybeTurnId;
|
|
49429
49635
|
if (ms !== void 0) proj.ms = ms;
|
|
49430
|
-
if (
|
|
49636
|
+
if (isRecord3(rateLimits)) proj.rateLimitReached = rateLimits.rateLimitReachedType != null;
|
|
49431
49637
|
return proj;
|
|
49432
49638
|
}
|
|
49433
49639
|
const bracketMatch = /^<([a-z-]+)>$/.exec(type);
|
|
@@ -54680,7 +54886,7 @@ function dayFilesInWindow(now, windowMs) {
|
|
|
54680
54886
|
}
|
|
54681
54887
|
return files;
|
|
54682
54888
|
}
|
|
54683
|
-
function
|
|
54889
|
+
function isRecord4(value) {
|
|
54684
54890
|
return typeof value === "object" && value !== null;
|
|
54685
54891
|
}
|
|
54686
54892
|
function extractItems(lines2, sessionIds) {
|
|
@@ -54698,7 +54904,7 @@ function extractItems(lines2, sessionIds) {
|
|
|
54698
54904
|
skipped2++;
|
|
54699
54905
|
continue;
|
|
54700
54906
|
}
|
|
54701
|
-
if (!
|
|
54907
|
+
if (!isRecord4(parsed)) {
|
|
54702
54908
|
skipped2++;
|
|
54703
54909
|
continue;
|
|
54704
54910
|
}
|
|
@@ -54707,11 +54913,11 @@ function extractItems(lines2, sessionIds) {
|
|
|
54707
54913
|
const ts = obj.ts;
|
|
54708
54914
|
const type = obj.type;
|
|
54709
54915
|
const payload = obj.payload;
|
|
54710
|
-
if (type === "turn" &&
|
|
54916
|
+
if (type === "turn" && isRecord4(payload) && typeof payload.message === "string") {
|
|
54711
54917
|
items.push({ ts, kind: "user", text: payload.message });
|
|
54712
|
-
} else if (type === "event" &&
|
|
54918
|
+
} else if (type === "event" && isRecord4(payload) && isRecord4(payload.event) && payload.event.type === "assistant" && typeof payload.event.content === "string") {
|
|
54713
54919
|
items.push({ ts, kind: "assistant", text: payload.event.content });
|
|
54714
|
-
} else if (type === "event" &&
|
|
54920
|
+
} 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
54921
|
items.push({ ts, kind: "tool", text: payload.event.tool.name });
|
|
54716
54922
|
}
|
|
54717
54923
|
}
|
|
@@ -54804,7 +55010,7 @@ async function readSessionRows(logsDir, files, sessionIds) {
|
|
|
54804
55010
|
stream.destroy();
|
|
54805
55011
|
}
|
|
54806
55012
|
} catch (e) {
|
|
54807
|
-
if (
|
|
55013
|
+
if (isRecord4(e) && e.code === "ENOENT") {
|
|
54808
55014
|
missingFiles++;
|
|
54809
55015
|
continue;
|
|
54810
55016
|
}
|
|
@@ -57402,10 +57608,10 @@ async function startDirectorBridge() {
|
|
|
57402
57608
|
migrateRetiredApiBaseUrls();
|
|
57403
57609
|
} catch {
|
|
57404
57610
|
}
|
|
57405
|
-
const apiBaseUrl =
|
|
57611
|
+
const apiBaseUrl = readDirectorBridgeApiBaseUrl();
|
|
57406
57612
|
if (!isRemoteApiAllowed(apiBaseUrl)) {
|
|
57407
57613
|
throw new Error(
|
|
57408
|
-
`Refusing to run the director-bridge execution consumer against
|
|
57614
|
+
`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
57615
|
);
|
|
57410
57616
|
}
|
|
57411
57617
|
const envToken = process.env.MRRLIN_TOKEN?.trim() || void 0;
|
|
@@ -64607,7 +64813,7 @@ async function invokeCapability(server, toolName, payload) {
|
|
|
64607
64813
|
await client.connect(transport);
|
|
64608
64814
|
result = await client.callTool({
|
|
64609
64815
|
name: toolName,
|
|
64610
|
-
arguments:
|
|
64816
|
+
arguments: isRecord5(payload) ? payload : void 0
|
|
64611
64817
|
});
|
|
64612
64818
|
} catch (error51) {
|
|
64613
64819
|
throw classifyInvokeError(error51, server.url, toolName);
|
|
@@ -64659,7 +64865,7 @@ function classifyInvokeError(error51, url2, toolName) {
|
|
|
64659
64865
|
if (error51 instanceof TypeError) return new InvokeUnreachableError(url2, error51);
|
|
64660
64866
|
return new InvokeMalformedResultError(error51 instanceof Error ? error51.message : String(error51), error51);
|
|
64661
64867
|
}
|
|
64662
|
-
function
|
|
64868
|
+
function isRecord5(value) {
|
|
64663
64869
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
64664
64870
|
}
|
|
64665
64871
|
function extractFirstUrl(content) {
|
|
@@ -64668,7 +64874,7 @@ function extractFirstUrl(content) {
|
|
|
64668
64874
|
function extractFirstText(content) {
|
|
64669
64875
|
if (Array.isArray(content)) {
|
|
64670
64876
|
for (const block of content) {
|
|
64671
|
-
if (
|
|
64877
|
+
if (isRecord5(block) && typeof block.text === "string") return block.text;
|
|
64672
64878
|
}
|
|
64673
64879
|
}
|
|
64674
64880
|
return JSON.stringify(content);
|
|
@@ -64685,7 +64891,7 @@ function findFirstMatch(value, pattern) {
|
|
|
64685
64891
|
}
|
|
64686
64892
|
return null;
|
|
64687
64893
|
}
|
|
64688
|
-
if (
|
|
64894
|
+
if (isRecord5(value)) {
|
|
64689
64895
|
for (const nested of Object.values(value)) {
|
|
64690
64896
|
const found = findFirstMatch(nested, pattern);
|
|
64691
64897
|
if (found) return found;
|
|
@@ -64906,7 +65112,7 @@ async function recordUse(client, route, input, outcome) {
|
|
|
64906
65112
|
}
|
|
64907
65113
|
}
|
|
64908
65114
|
async function callDestination(client, projectSlug, route, payload, mcpConfigPath) {
|
|
64909
|
-
const operation =
|
|
65115
|
+
const operation = isRecord6(payload) ? payload.operation : void 0;
|
|
64910
65116
|
if (route.capability === "wiki") {
|
|
64911
65117
|
if (operation !== "create" && operation !== "update") {
|
|
64912
65118
|
throw new Error(`callDestination: payload.operation "${String(operation)}" is not valid for wiki`);
|
|
@@ -64923,7 +65129,7 @@ async function callDestination(client, projectSlug, route, payload, mcpConfigPat
|
|
|
64923
65129
|
}
|
|
64924
65130
|
throw new Error(`callDestination: no destination wired for capability "${route.capability}"`);
|
|
64925
65131
|
}
|
|
64926
|
-
function
|
|
65132
|
+
function isRecord6(value) {
|
|
64927
65133
|
return typeof value === "object" && value !== null;
|
|
64928
65134
|
}
|
|
64929
65135
|
|
|
@@ -65973,7 +66179,7 @@ var import_node_path31 = require("node:path");
|
|
|
65973
66179
|
function evidenceDedupKey(sessionId, reportId) {
|
|
65974
66180
|
return `${sessionId}:${reportId}`;
|
|
65975
66181
|
}
|
|
65976
|
-
function
|
|
66182
|
+
function isRecord7(v) {
|
|
65977
66183
|
return v !== null && typeof v === "object";
|
|
65978
66184
|
}
|
|
65979
66185
|
async function readDayLines(stateDir, day, sessionId) {
|
|
@@ -65992,7 +66198,7 @@ async function readDayLines(stateDir, day, sessionId) {
|
|
|
65992
66198
|
} catch {
|
|
65993
66199
|
continue;
|
|
65994
66200
|
}
|
|
65995
|
-
if (!
|
|
66201
|
+
if (!isRecord7(rec) || !isRecord7(rec.payload)) continue;
|
|
65996
66202
|
if (rec.payload.directorSessionId !== sessionId) continue;
|
|
65997
66203
|
kept.push(rec);
|
|
65998
66204
|
}
|
|
@@ -66062,7 +66268,7 @@ async function assembleEvidence(stateDir, sessionId, reportId, gateCtx) {
|
|
|
66062
66268
|
} catch {
|
|
66063
66269
|
continue;
|
|
66064
66270
|
}
|
|
66065
|
-
if (!
|
|
66271
|
+
if (!isRecord7(rec) || !Array.isArray(rec.daysRemoved)) continue;
|
|
66066
66272
|
for (const removedDay of rec.daysRemoved) {
|
|
66067
66273
|
if (daysRead.includes(removedDay)) {
|
|
66068
66274
|
windowPruned = true;
|
|
@@ -68115,7 +68321,7 @@ function resolveServerOptions(input = {}) {
|
|
|
68115
68321
|
const remoteAllowed = isRemoteApiAllowed(apiBaseUrl) || input.allowRemoteApi === true;
|
|
68116
68322
|
if (!remoteAllowed) {
|
|
68117
68323
|
throw new Error(
|
|
68118
|
-
`Refusing to start MCP server against non-loopback URL ${apiBaseUrl}. Set MRRLIN_API_ALLOW_REMOTE=1 or
|
|
68324
|
+
`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
68325
|
);
|
|
68120
68326
|
}
|
|
68121
68327
|
return {
|
|
@@ -68297,6 +68503,50 @@ function readExistingTelemetryDefaultProject(deps, ecoPath) {
|
|
|
68297
68503
|
return void 0;
|
|
68298
68504
|
}
|
|
68299
68505
|
}
|
|
68506
|
+
function isOwnedEcosystemConfig(text) {
|
|
68507
|
+
return /name:\s*"mrrlin-bridge"/.test(text) && /script:\s*"mrrlin-mcp"/.test(text) && /args:\s*"director-bridge"/.test(text);
|
|
68508
|
+
}
|
|
68509
|
+
function readEnvString(text, key) {
|
|
68510
|
+
const match = new RegExp(`\\b${key}:\\s*("(?:[^"\\\\]|\\\\.)*")`).exec(text);
|
|
68511
|
+
if (!match?.[1]) return void 0;
|
|
68512
|
+
try {
|
|
68513
|
+
const value = JSON.parse(match[1]);
|
|
68514
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
68515
|
+
} catch {
|
|
68516
|
+
return void 0;
|
|
68517
|
+
}
|
|
68518
|
+
}
|
|
68519
|
+
function normalizeServiceApiTarget(apiBaseUrl, allowRemote) {
|
|
68520
|
+
const normalized = retargetRetiredApiBaseUrl(apiBaseUrl).baseUrl;
|
|
68521
|
+
if (isProductionApiBaseUrl(normalized)) return {};
|
|
68522
|
+
const target = { apiBaseUrl: normalized };
|
|
68523
|
+
if (allowRemote && !isStagingApiBaseUrl(normalized) && !isLoopbackUrl(normalized)) target.apiAllowRemote = true;
|
|
68524
|
+
return target;
|
|
68525
|
+
}
|
|
68526
|
+
function readExistingServiceApiTarget(deps, ecoPath) {
|
|
68527
|
+
try {
|
|
68528
|
+
const existing = deps.readFile?.(ecoPath);
|
|
68529
|
+
if (!existing || !isOwnedEcosystemConfig(existing)) return void 0;
|
|
68530
|
+
const apiBaseUrl = readEnvString(existing, "MRRLIN_API_BASE_URL");
|
|
68531
|
+
if (apiBaseUrl) {
|
|
68532
|
+
return normalizeServiceApiTarget(apiBaseUrl, readEnvString(existing, "MRRLIN_API_ALLOW_REMOTE") === "1");
|
|
68533
|
+
}
|
|
68534
|
+
if (readEnvString(existing, "MRRLIN_STAGING") === "1") {
|
|
68535
|
+
return { apiBaseUrl: STAGING_API_BASE_URL };
|
|
68536
|
+
}
|
|
68537
|
+
return void 0;
|
|
68538
|
+
} catch {
|
|
68539
|
+
return void 0;
|
|
68540
|
+
}
|
|
68541
|
+
}
|
|
68542
|
+
function deriveServiceApiTarget(deps, ecoPath) {
|
|
68543
|
+
const apiBaseUrl = deps.env.MRRLIN_API_BASE_URL?.trim();
|
|
68544
|
+
if (apiBaseUrl) return normalizeServiceApiTarget(apiBaseUrl, deps.env.MRRLIN_API_ALLOW_REMOTE === "1");
|
|
68545
|
+
if (deps.env.MRRLIN_STAGING === "1" || bridgeUrlImpliesStaging(deps.env.MRRLIN_BRIDGE_URL)) {
|
|
68546
|
+
return { apiBaseUrl: STAGING_API_BASE_URL };
|
|
68547
|
+
}
|
|
68548
|
+
return readExistingServiceApiTarget(deps, ecoPath) ?? {};
|
|
68549
|
+
}
|
|
68300
68550
|
var SYSTEM_PATH_DIRS = ["/usr/bin", "/bin", "/usr/sbin", "/sbin"];
|
|
68301
68551
|
function defaultHomeOwnerUid(home) {
|
|
68302
68552
|
try {
|
|
@@ -68363,9 +68613,8 @@ function installService(deps) {
|
|
|
68363
68613
|
const pathEnv = Array.from(
|
|
68364
68614
|
/* @__PURE__ */ new Set([...resolvedBins.map((b) => import_node_path33.default.dirname(b)), ...SYSTEM_PATH_DIRS])
|
|
68365
68615
|
).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
68616
|
const ecoPath = import_node_path33.default.join(home, ".mrrlin", "ecosystem.config.cjs");
|
|
68617
|
+
const apiTarget = deriveServiceApiTarget(deps, ecoPath);
|
|
68369
68618
|
const telemetryDefaultProject = "MRRLIN_TELEMETRY_DEFAULT_PROJECT" in deps.env ? deps.env.MRRLIN_TELEMETRY_DEFAULT_PROJECT?.trim() || void 0 : readExistingTelemetryDefaultProject(deps, ecoPath);
|
|
68370
68619
|
const text = renderEcosystemConfig({
|
|
68371
68620
|
cwd: resolved.cwd,
|
|
@@ -68373,8 +68622,8 @@ function installService(deps) {
|
|
|
68373
68622
|
user,
|
|
68374
68623
|
codexHome: deps.env.CODEX_HOME?.trim() || void 0,
|
|
68375
68624
|
pathEnv,
|
|
68376
|
-
|
|
68377
|
-
|
|
68625
|
+
apiBaseUrl: apiTarget.apiBaseUrl,
|
|
68626
|
+
apiAllowRemote: apiTarget.apiAllowRemote,
|
|
68378
68627
|
telemetryDefaultProject
|
|
68379
68628
|
});
|
|
68380
68629
|
deps.writeFile(ecoPath, text);
|
|
@@ -68512,7 +68761,7 @@ function parseArgs(args) {
|
|
|
68512
68761
|
}
|
|
68513
68762
|
return map2;
|
|
68514
68763
|
}
|
|
68515
|
-
function
|
|
68764
|
+
function isRecord8(v) {
|
|
68516
68765
|
return v !== null && typeof v === "object";
|
|
68517
68766
|
}
|
|
68518
68767
|
async function runExtract(args, deps) {
|
|
@@ -68549,8 +68798,8 @@ Missing required flag(s): ${missing.join(
|
|
|
68549
68798
|
} catch {
|
|
68550
68799
|
continue;
|
|
68551
68800
|
}
|
|
68552
|
-
const payload =
|
|
68553
|
-
if (
|
|
68801
|
+
const payload = isRecord8(parsed) ? parsed.payload : void 0;
|
|
68802
|
+
if (isRecord8(payload) && payload.directorSessionId === sessionId) {
|
|
68554
68803
|
matchingLines.push(line);
|
|
68555
68804
|
}
|
|
68556
68805
|
}
|
|
@@ -68658,8 +68907,9 @@ Usage:
|
|
|
68658
68907
|
mrrlin-mcp serve Run the stdio MCP server. Honors
|
|
68659
68908
|
MRRLIN_API_BASE_URL (default http://127.0.0.1:8787)
|
|
68660
68909
|
and MRRLIN_TOKEN. Non-loopback API URLs require
|
|
68661
|
-
MRRLIN_API_ALLOW_REMOTE=1
|
|
68662
|
-
|
|
68910
|
+
MRRLIN_API_ALLOW_REMOTE=1 for a custom remote. Use
|
|
68911
|
+
MRRLIN_API_BASE_URL=https://api-staging.mrrlin.com
|
|
68912
|
+
to target the known staging API.
|
|
68663
68913
|
|
|
68664
68914
|
mrrlin-mcp install-browser Idempotently install the dedicated, pinned
|
|
68665
68915
|
Chromium into ~/.mrrlin/browser/browsers (its own
|
|
@@ -68695,16 +68945,16 @@ Usage:
|
|
|
68695
68945
|
Bakes API-target env vars into the block as an
|
|
68696
68946
|
inline env table so \`serve\` boots even when the
|
|
68697
68947
|
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,
|
|
68948
|
+
MRRLIN_API_BASE_URL=https://api-staging.mrrlin.com mrrlin-mcp configure bridge
|
|
68949
|
+
is the canonical staging path. For a custom remote,
|
|
68701
68950
|
pass BOTH the URL AND the allow gate:
|
|
68702
68951
|
MRRLIN_API_BASE_URL=https://... \\
|
|
68703
68952
|
MRRLIN_API_ALLOW_REMOTE=1 mrrlin-mcp configure bridge
|
|
68704
68953
|
Without an allow gate, \`serve\` still refuses any
|
|
68705
68954
|
non-loopback URL (see \`mrrlin-mcp serve\` above).
|
|
68706
|
-
URL
|
|
68707
|
-
|
|
68955
|
+
An explicit URL is the only routing selector at bake
|
|
68956
|
+
time. Production needs no routing env; a custom
|
|
68957
|
+
remote also needs MRRLIN_API_ALLOW_REMOTE=1.
|
|
68708
68958
|
Re-running BARE (no env vars) over a block you
|
|
68709
68959
|
already pinned to staging/a remote target is safe:
|
|
68710
68960
|
it keeps your env verbatim (reported as
|
|
@@ -68738,8 +68988,9 @@ Usage:
|
|
|
68738
68988
|
pending runs, read-only). Consumer env:
|
|
68739
68989
|
- MRRLIN_API_BASE_URL (default http://127.0.0.1:8787;
|
|
68740
68990
|
non-loopback requires MRRLIN_API_ALLOW_REMOTE=1,
|
|
68741
|
-
or
|
|
68742
|
-
-
|
|
68991
|
+
or an explicit staging API URL)
|
|
68992
|
+
- MRRLIN_API_BASE_URL=https://api-staging.mrrlin.com
|
|
68993
|
+
(known staging API)
|
|
68743
68994
|
- MRRLIN_TOKEN (service-principal auth to the API;
|
|
68744
68995
|
if unset, chat still works but claim-sweep /
|
|
68745
68996
|
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.55",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mrrlin-mcp": "dist/bin.cjs"
|
|
@@ -21,11 +21,11 @@
|
|
|
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
|
-
"@mrrlin/schemas": "0.0.0",
|
|
28
27
|
"@mrrlin/tsconfig": "0.0.0",
|
|
28
|
+
"@mrrlin/schemas": "0.0.0",
|
|
29
29
|
"@mrrlin/wiki": "0.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|