@mrrlin-dev/mcp 0.4.53 → 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 +367 -86
- 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
|
}
|
|
@@ -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";
|
|
@@ -38200,6 +38247,10 @@ var mrrlinTaskCreateShapeSchema = external_exports.object({
|
|
|
38200
38247
|
title: external_exports.string().min(1),
|
|
38201
38248
|
type: mrrlinTaskTypeSchema.default("task"),
|
|
38202
38249
|
verificationDueAt: external_exports.string().datetime().nullable().optional()
|
|
38250
|
+
// `declaredOutcome` is deliberately ABSENT — «required at spec approval, never at creation», as
|
|
38251
|
+
// the comment beside the READ schema's own field says. It is refused with a 400 at the route
|
|
38252
|
+
// rather than declared-and-rejected here (`OQ-084`); see `rejectsDeclaredOutcomeAtCreation` in
|
|
38253
|
+
// `apps/api/src/index.ts` for why the refusal cannot live in this schema.
|
|
38203
38254
|
});
|
|
38204
38255
|
function refineTaskCreateDomain(data, ctx) {
|
|
38205
38256
|
if (data.domain === void 0 && data.parentTaskId == null) {
|
|
@@ -42437,7 +42488,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
|
|
|
42437
42488
|
}
|
|
42438
42489
|
|
|
42439
42490
|
// src/_generated/version.ts
|
|
42440
|
-
var PKG_VERSION = "0.4.
|
|
42491
|
+
var PKG_VERSION = "0.4.55";
|
|
42441
42492
|
|
|
42442
42493
|
// src/api-base-url-migrate.ts
|
|
42443
42494
|
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
@@ -42467,11 +42518,17 @@ function renderEcosystemConfig(opts) {
|
|
|
42467
42518
|
PATH: opts.pathEnv,
|
|
42468
42519
|
MRRLIN_DIRECTOR_BRIDGE_LOG: "1"
|
|
42469
42520
|
};
|
|
42521
|
+
const telemetryProject = opts.telemetryDefaultProject?.trim();
|
|
42522
|
+
if (telemetryProject) env.MRRLIN_TELEMETRY_DEFAULT_PROJECT = telemetryProject;
|
|
42470
42523
|
if (opts.user) env.USER = opts.user;
|
|
42471
42524
|
if (opts.codexHome) env.CODEX_HOME = opts.codexHome;
|
|
42472
|
-
const
|
|
42473
|
-
if (
|
|
42474
|
-
|
|
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
|
+
}
|
|
42475
42532
|
const envLines = Object.entries(env).map(([k, v]) => ` ${k}: ${JSON.stringify(v)},`).join("\n");
|
|
42476
42533
|
return `module.exports = {
|
|
42477
42534
|
apps: [{
|
|
@@ -42515,6 +42572,10 @@ function defaultDeps(deps) {
|
|
|
42515
42572
|
function findRetiredHost(text) {
|
|
42516
42573
|
return retiredApiHostnames().find((host) => text.toLowerCase().includes(host));
|
|
42517
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
|
+
}
|
|
42518
42579
|
function isRetiredApiUrl(value) {
|
|
42519
42580
|
if (!value?.trim()) return false;
|
|
42520
42581
|
try {
|
|
@@ -42523,6 +42584,19 @@ function isRetiredApiUrl(value) {
|
|
|
42523
42584
|
return false;
|
|
42524
42585
|
}
|
|
42525
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
|
+
}
|
|
42526
42600
|
function backupOnce(file2, text, deps) {
|
|
42527
42601
|
const bak = `${file2}.bak-retired-api-url`;
|
|
42528
42602
|
if (!deps.exists(bak)) deps.writeFile(bak, text);
|
|
@@ -42532,7 +42606,8 @@ function migrateEcosystemConfig(deps = {}) {
|
|
|
42532
42606
|
const file2 = import_node_path8.default.join(d.home, ".mrrlin", "ecosystem.config.cjs");
|
|
42533
42607
|
if (!d.exists(file2)) return { file: file2, status: "absent" };
|
|
42534
42608
|
const text = d.readFile(file2);
|
|
42535
|
-
if (!findRetiredHost(text)
|
|
42609
|
+
if (!findRetiredHost(text) && !hasLiveLegacyStagingFlag(text, "js"))
|
|
42610
|
+
return { file: file2, status: "clean" };
|
|
42536
42611
|
const fields = {
|
|
42537
42612
|
cwd: /cwd:\s*"((?:[^"\\]|\\.)*)"/.exec(text)?.[1],
|
|
42538
42613
|
home: /HOME:\s*"((?:[^"\\]|\\.)*)"/.exec(text)?.[1],
|
|
@@ -42541,14 +42616,48 @@ function migrateEcosystemConfig(deps = {}) {
|
|
|
42541
42616
|
};
|
|
42542
42617
|
if (fields.cwd && fields.home && fields.pathEnv) {
|
|
42543
42618
|
const retiredUrl = /MRRLIN_API_BASE_URL:\s*"((?:[^"\\]|\\.)*)"/.exec(text)?.[1];
|
|
42544
|
-
const
|
|
42619
|
+
const legacyUrlShape = retiredUrl && isRetiredApiUrl(retiredUrl) ? renderEcosystemConfigLegacy({
|
|
42545
42620
|
cwd: fields.cwd,
|
|
42546
42621
|
home: fields.home,
|
|
42547
42622
|
pathEnv: fields.pathEnv,
|
|
42548
42623
|
codexHome: fields.codexHome,
|
|
42549
42624
|
apiBaseUrl: retiredUrl
|
|
42550
42625
|
}) : void 0;
|
|
42551
|
-
|
|
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) {
|
|
42552
42661
|
backupOnce(file2, text, d);
|
|
42553
42662
|
d.writeFile(
|
|
42554
42663
|
file2,
|
|
@@ -42557,8 +42666,7 @@ function migrateEcosystemConfig(deps = {}) {
|
|
|
42557
42666
|
home: fields.home,
|
|
42558
42667
|
pathEnv: fields.pathEnv,
|
|
42559
42668
|
codexHome: fields.codexHome,
|
|
42560
|
-
|
|
42561
|
-
apiBaseUrl: void 0
|
|
42669
|
+
apiBaseUrl: STAGING_API_BASE_URL
|
|
42562
42670
|
})
|
|
42563
42671
|
);
|
|
42564
42672
|
return { file: file2, status: "migrated", detail: "regenerated from the canonical template" };
|
|
@@ -42568,16 +42676,49 @@ function migrateEcosystemConfig(deps = {}) {
|
|
|
42568
42676
|
const matches = (text.match(lineRe) ?? []).filter((line) => !line.trimStart().startsWith("//"));
|
|
42569
42677
|
const target = matches.length === 1 ? matches[0] : void 0;
|
|
42570
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
|
+
}
|
|
42571
42687
|
backupOnce(file2, text, d);
|
|
42572
42688
|
const indent = /^[ \t]*/.exec(target)?.[0] ?? " ";
|
|
42573
|
-
|
|
42574
|
-
|
|
42575
|
-
|
|
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
|
+
};
|
|
42576
42717
|
}
|
|
42577
42718
|
return {
|
|
42578
42719
|
file: file2,
|
|
42579
42720
|
status: "skipped-drifted",
|
|
42580
|
-
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`
|
|
42581
42722
|
};
|
|
42582
42723
|
}
|
|
42583
42724
|
function renderEcosystemConfigLegacy(opts) {
|
|
@@ -42616,7 +42757,7 @@ function migrateCodexConfig(deps = {}) {
|
|
|
42616
42757
|
if (!d.exists(file2)) return { file: file2, status: "absent" };
|
|
42617
42758
|
const text = d.readFile(file2);
|
|
42618
42759
|
const host = findRetiredHost(text);
|
|
42619
|
-
if (!host) return { file: file2, status: "clean" };
|
|
42760
|
+
if (!host && !hasLiveLegacyStagingFlag(text, "toml")) return { file: file2, status: "clean" };
|
|
42620
42761
|
const managedBlockRe = /^\[mcp_servers\.mrrlin\][^\n]*\n(?:(?!\[)[^\n]*\n?)*/gm;
|
|
42621
42762
|
const blocks = text.match(managedBlockRe) ?? [];
|
|
42622
42763
|
if (blocks.length === 0) {
|
|
@@ -42627,21 +42768,39 @@ function migrateCodexConfig(deps = {}) {
|
|
|
42627
42768
|
};
|
|
42628
42769
|
}
|
|
42629
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;
|
|
42630
42773
|
let out = text;
|
|
42631
42774
|
let changed = 0;
|
|
42632
42775
|
for (const block of blocks) {
|
|
42633
42776
|
let blockChanged = false;
|
|
42634
42777
|
const healedBlock = block.split("\n").map((line) => {
|
|
42635
42778
|
if (line.trimStart().startsWith("#")) return line;
|
|
42636
|
-
|
|
42637
|
-
|
|
42638
|
-
|
|
42639
|
-
|
|
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
|
+
}
|
|
42640
42796
|
if (healedLine !== line) blockChanged = true;
|
|
42641
42797
|
return healedLine;
|
|
42642
42798
|
}).join("\n");
|
|
42643
42799
|
if (!blockChanged) continue;
|
|
42644
|
-
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
|
+
);
|
|
42645
42804
|
out = out.replace(block, () => deduped);
|
|
42646
42805
|
changed += 1;
|
|
42647
42806
|
}
|
|
@@ -42654,14 +42813,67 @@ function migrateCodexConfig(deps = {}) {
|
|
|
42654
42813
|
}
|
|
42655
42814
|
backupOnce(file2, text, d);
|
|
42656
42815
|
d.writeFile(file2, out);
|
|
42657
|
-
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 };
|
|
42658
42869
|
}
|
|
42659
42870
|
function migrateRetiredApiBaseUrls(deps = {}) {
|
|
42660
42871
|
const d = defaultDeps(deps);
|
|
42661
42872
|
const results = [];
|
|
42662
42873
|
const steps = [
|
|
42663
42874
|
{ run: migrateEcosystemConfig, file: import_node_path8.default.join(d.home, ".mrrlin", "ecosystem.config.cjs") },
|
|
42664
|
-
{ 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") }
|
|
42665
42877
|
];
|
|
42666
42878
|
for (const step of steps) {
|
|
42667
42879
|
try {
|
|
@@ -45056,7 +45268,7 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
|
|
|
45056
45268
|
}
|
|
45057
45269
|
|
|
45058
45270
|
// src/telemetry/pii-gate.ts
|
|
45059
|
-
function
|
|
45271
|
+
function isRecord2(v) {
|
|
45060
45272
|
return v !== null && typeof v === "object";
|
|
45061
45273
|
}
|
|
45062
45274
|
function resolvePath(root, path24) {
|
|
@@ -45068,7 +45280,7 @@ function resolvePath(root, path24) {
|
|
|
45068
45280
|
if (Array.isArray(cur) && Number.isInteger(idx)) {
|
|
45069
45281
|
if (idx < 0 || idx >= cur.length) return null;
|
|
45070
45282
|
cur = cur[idx];
|
|
45071
|
-
} else if (
|
|
45283
|
+
} else if (isRecord2(cur) && part in cur) {
|
|
45072
45284
|
cur = cur[part];
|
|
45073
45285
|
} else {
|
|
45074
45286
|
return null;
|
|
@@ -45080,7 +45292,7 @@ function resolvePath(root, path24) {
|
|
|
45080
45292
|
if (lastIdx < 0 || lastIdx >= cur.length) return null;
|
|
45081
45293
|
return { parent: cur, key: lastIdx };
|
|
45082
45294
|
}
|
|
45083
|
-
if (
|
|
45295
|
+
if (isRecord2(cur) && last in cur) {
|
|
45084
45296
|
return { parent: cur, key: last };
|
|
45085
45297
|
}
|
|
45086
45298
|
return null;
|
|
@@ -45089,7 +45301,7 @@ function withheldExcerptClone(artifact) {
|
|
|
45089
45301
|
const clone2 = { ...artifact };
|
|
45090
45302
|
if (Array.isArray(clone2.steps)) {
|
|
45091
45303
|
clone2.steps = clone2.steps.map((step) => {
|
|
45092
|
-
if (!
|
|
45304
|
+
if (!isRecord2(step)) return step;
|
|
45093
45305
|
const copy = { ...step };
|
|
45094
45306
|
delete copy.excerpt;
|
|
45095
45307
|
return copy;
|
|
@@ -45120,9 +45332,9 @@ async function gate(artifact, prose, ctx) {
|
|
|
45120
45332
|
} catch {
|
|
45121
45333
|
return { ok: true, gated: true, artifact: withheldExcerptClone(artifact) };
|
|
45122
45334
|
}
|
|
45123
|
-
const fields =
|
|
45335
|
+
const fields = isRecord2(parsed) && Array.isArray(parsed.fields) ? parsed.fields : null;
|
|
45124
45336
|
const isValidShape = fields !== null && fields.length === prose.length && fields.every(
|
|
45125
|
-
(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
|
|
45126
45338
|
);
|
|
45127
45339
|
if (!isValidShape || fields === null) {
|
|
45128
45340
|
return { ok: true, gated: true, artifact: withheldExcerptClone(artifact) };
|
|
@@ -49344,27 +49556,27 @@ function redact(input) {
|
|
|
49344
49556
|
|
|
49345
49557
|
// src/telemetry/projector.ts
|
|
49346
49558
|
var import_node_crypto8 = require("node:crypto");
|
|
49347
|
-
function
|
|
49559
|
+
function isRecord3(x) {
|
|
49348
49560
|
return typeof x === "object" && x !== null;
|
|
49349
49561
|
}
|
|
49350
49562
|
function buildProjection(payload, type, ms, telemetry) {
|
|
49351
|
-
if (!
|
|
49563
|
+
if (!isRecord3(payload)) return null;
|
|
49352
49564
|
const seqRaw = payload.seq;
|
|
49353
49565
|
const turnIdRaw = payload.turnId;
|
|
49354
49566
|
const maybeSeq = typeof seqRaw === "number" ? seqRaw : void 0;
|
|
49355
49567
|
const maybeTurnId = typeof turnIdRaw === "string" ? turnIdRaw : void 0;
|
|
49356
49568
|
if (type === "event") {
|
|
49357
49569
|
const rawEvent = payload.event;
|
|
49358
|
-
if (!
|
|
49570
|
+
if (!isRecord3(rawEvent) || typeof rawEvent.type !== "string") return null;
|
|
49359
49571
|
if (rawEvent.type === "tool_use") {
|
|
49360
49572
|
const tool = rawEvent.tool;
|
|
49361
|
-
if (!
|
|
49573
|
+
if (!isRecord3(tool)) return null;
|
|
49362
49574
|
const toolId = tool.id;
|
|
49363
49575
|
const toolName = tool.name;
|
|
49364
49576
|
if (typeof toolId !== "string" || typeof toolName !== "string") return null;
|
|
49365
49577
|
const input = tool.input;
|
|
49366
|
-
const argKeys =
|
|
49367
|
-
const projectSlug =
|
|
49578
|
+
const argKeys = isRecord3(input) ? Object.keys(input) : [];
|
|
49579
|
+
const projectSlug = isRecord3(input) && typeof input.projectSlug === "string" ? input.projectSlug : void 0;
|
|
49368
49580
|
const proj = { kind: "tool_use", toolId, toolName, argKeys, seq: maybeSeq ?? 0 };
|
|
49369
49581
|
if (maybeTurnId !== void 0) proj.turnId = maybeTurnId;
|
|
49370
49582
|
if (ms !== void 0) proj.ms = ms;
|
|
@@ -49373,7 +49585,7 @@ function buildProjection(payload, type, ms, telemetry) {
|
|
|
49373
49585
|
}
|
|
49374
49586
|
if (rawEvent.type === "tool_result") {
|
|
49375
49587
|
const tool = rawEvent.tool;
|
|
49376
|
-
if (!
|
|
49588
|
+
if (!isRecord3(tool)) return null;
|
|
49377
49589
|
const toolId = tool.id;
|
|
49378
49590
|
if (typeof toolId !== "string") return null;
|
|
49379
49591
|
const exitCode = tool.exitCode;
|
|
@@ -49421,7 +49633,7 @@ function buildProjection(payload, type, ms, telemetry) {
|
|
|
49421
49633
|
const proj = { kind: "plumbing", plumbingKind: "rate_limit_status", seq: maybeSeq ?? 0 };
|
|
49422
49634
|
if (maybeTurnId !== void 0) proj.turnId = maybeTurnId;
|
|
49423
49635
|
if (ms !== void 0) proj.ms = ms;
|
|
49424
|
-
if (
|
|
49636
|
+
if (isRecord3(rateLimits)) proj.rateLimitReached = rateLimits.rateLimitReachedType != null;
|
|
49425
49637
|
return proj;
|
|
49426
49638
|
}
|
|
49427
49639
|
const bracketMatch = /^<([a-z-]+)>$/.exec(type);
|
|
@@ -54674,7 +54886,7 @@ function dayFilesInWindow(now, windowMs) {
|
|
|
54674
54886
|
}
|
|
54675
54887
|
return files;
|
|
54676
54888
|
}
|
|
54677
|
-
function
|
|
54889
|
+
function isRecord4(value) {
|
|
54678
54890
|
return typeof value === "object" && value !== null;
|
|
54679
54891
|
}
|
|
54680
54892
|
function extractItems(lines2, sessionIds) {
|
|
@@ -54692,7 +54904,7 @@ function extractItems(lines2, sessionIds) {
|
|
|
54692
54904
|
skipped2++;
|
|
54693
54905
|
continue;
|
|
54694
54906
|
}
|
|
54695
|
-
if (!
|
|
54907
|
+
if (!isRecord4(parsed)) {
|
|
54696
54908
|
skipped2++;
|
|
54697
54909
|
continue;
|
|
54698
54910
|
}
|
|
@@ -54701,11 +54913,11 @@ function extractItems(lines2, sessionIds) {
|
|
|
54701
54913
|
const ts = obj.ts;
|
|
54702
54914
|
const type = obj.type;
|
|
54703
54915
|
const payload = obj.payload;
|
|
54704
|
-
if (type === "turn" &&
|
|
54916
|
+
if (type === "turn" && isRecord4(payload) && typeof payload.message === "string") {
|
|
54705
54917
|
items.push({ ts, kind: "user", text: payload.message });
|
|
54706
|
-
} else if (type === "event" &&
|
|
54918
|
+
} else if (type === "event" && isRecord4(payload) && isRecord4(payload.event) && payload.event.type === "assistant" && typeof payload.event.content === "string") {
|
|
54707
54919
|
items.push({ ts, kind: "assistant", text: payload.event.content });
|
|
54708
|
-
} 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") {
|
|
54709
54921
|
items.push({ ts, kind: "tool", text: payload.event.tool.name });
|
|
54710
54922
|
}
|
|
54711
54923
|
}
|
|
@@ -54798,7 +55010,7 @@ async function readSessionRows(logsDir, files, sessionIds) {
|
|
|
54798
55010
|
stream.destroy();
|
|
54799
55011
|
}
|
|
54800
55012
|
} catch (e) {
|
|
54801
|
-
if (
|
|
55013
|
+
if (isRecord4(e) && e.code === "ENOENT") {
|
|
54802
55014
|
missingFiles++;
|
|
54803
55015
|
continue;
|
|
54804
55016
|
}
|
|
@@ -57396,10 +57608,10 @@ async function startDirectorBridge() {
|
|
|
57396
57608
|
migrateRetiredApiBaseUrls();
|
|
57397
57609
|
} catch {
|
|
57398
57610
|
}
|
|
57399
|
-
const apiBaseUrl =
|
|
57611
|
+
const apiBaseUrl = readDirectorBridgeApiBaseUrl();
|
|
57400
57612
|
if (!isRemoteApiAllowed(apiBaseUrl)) {
|
|
57401
57613
|
throw new Error(
|
|
57402
|
-
`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.`
|
|
57403
57615
|
);
|
|
57404
57616
|
}
|
|
57405
57617
|
const envToken = process.env.MRRLIN_TOKEN?.trim() || void 0;
|
|
@@ -64601,7 +64813,7 @@ async function invokeCapability(server, toolName, payload) {
|
|
|
64601
64813
|
await client.connect(transport);
|
|
64602
64814
|
result = await client.callTool({
|
|
64603
64815
|
name: toolName,
|
|
64604
|
-
arguments:
|
|
64816
|
+
arguments: isRecord5(payload) ? payload : void 0
|
|
64605
64817
|
});
|
|
64606
64818
|
} catch (error51) {
|
|
64607
64819
|
throw classifyInvokeError(error51, server.url, toolName);
|
|
@@ -64653,7 +64865,7 @@ function classifyInvokeError(error51, url2, toolName) {
|
|
|
64653
64865
|
if (error51 instanceof TypeError) return new InvokeUnreachableError(url2, error51);
|
|
64654
64866
|
return new InvokeMalformedResultError(error51 instanceof Error ? error51.message : String(error51), error51);
|
|
64655
64867
|
}
|
|
64656
|
-
function
|
|
64868
|
+
function isRecord5(value) {
|
|
64657
64869
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
64658
64870
|
}
|
|
64659
64871
|
function extractFirstUrl(content) {
|
|
@@ -64662,7 +64874,7 @@ function extractFirstUrl(content) {
|
|
|
64662
64874
|
function extractFirstText(content) {
|
|
64663
64875
|
if (Array.isArray(content)) {
|
|
64664
64876
|
for (const block of content) {
|
|
64665
|
-
if (
|
|
64877
|
+
if (isRecord5(block) && typeof block.text === "string") return block.text;
|
|
64666
64878
|
}
|
|
64667
64879
|
}
|
|
64668
64880
|
return JSON.stringify(content);
|
|
@@ -64679,7 +64891,7 @@ function findFirstMatch(value, pattern) {
|
|
|
64679
64891
|
}
|
|
64680
64892
|
return null;
|
|
64681
64893
|
}
|
|
64682
|
-
if (
|
|
64894
|
+
if (isRecord5(value)) {
|
|
64683
64895
|
for (const nested of Object.values(value)) {
|
|
64684
64896
|
const found = findFirstMatch(nested, pattern);
|
|
64685
64897
|
if (found) return found;
|
|
@@ -64900,7 +65112,7 @@ async function recordUse(client, route, input, outcome) {
|
|
|
64900
65112
|
}
|
|
64901
65113
|
}
|
|
64902
65114
|
async function callDestination(client, projectSlug, route, payload, mcpConfigPath) {
|
|
64903
|
-
const operation =
|
|
65115
|
+
const operation = isRecord6(payload) ? payload.operation : void 0;
|
|
64904
65116
|
if (route.capability === "wiki") {
|
|
64905
65117
|
if (operation !== "create" && operation !== "update") {
|
|
64906
65118
|
throw new Error(`callDestination: payload.operation "${String(operation)}" is not valid for wiki`);
|
|
@@ -64917,7 +65129,7 @@ async function callDestination(client, projectSlug, route, payload, mcpConfigPat
|
|
|
64917
65129
|
}
|
|
64918
65130
|
throw new Error(`callDestination: no destination wired for capability "${route.capability}"`);
|
|
64919
65131
|
}
|
|
64920
|
-
function
|
|
65132
|
+
function isRecord6(value) {
|
|
64921
65133
|
return typeof value === "object" && value !== null;
|
|
64922
65134
|
}
|
|
64923
65135
|
|
|
@@ -65967,7 +66179,7 @@ var import_node_path31 = require("node:path");
|
|
|
65967
66179
|
function evidenceDedupKey(sessionId, reportId) {
|
|
65968
66180
|
return `${sessionId}:${reportId}`;
|
|
65969
66181
|
}
|
|
65970
|
-
function
|
|
66182
|
+
function isRecord7(v) {
|
|
65971
66183
|
return v !== null && typeof v === "object";
|
|
65972
66184
|
}
|
|
65973
66185
|
async function readDayLines(stateDir, day, sessionId) {
|
|
@@ -65986,7 +66198,7 @@ async function readDayLines(stateDir, day, sessionId) {
|
|
|
65986
66198
|
} catch {
|
|
65987
66199
|
continue;
|
|
65988
66200
|
}
|
|
65989
|
-
if (!
|
|
66201
|
+
if (!isRecord7(rec) || !isRecord7(rec.payload)) continue;
|
|
65990
66202
|
if (rec.payload.directorSessionId !== sessionId) continue;
|
|
65991
66203
|
kept.push(rec);
|
|
65992
66204
|
}
|
|
@@ -66056,7 +66268,7 @@ async function assembleEvidence(stateDir, sessionId, reportId, gateCtx) {
|
|
|
66056
66268
|
} catch {
|
|
66057
66269
|
continue;
|
|
66058
66270
|
}
|
|
66059
|
-
if (!
|
|
66271
|
+
if (!isRecord7(rec) || !Array.isArray(rec.daysRemoved)) continue;
|
|
66060
66272
|
for (const removedDay of rec.daysRemoved) {
|
|
66061
66273
|
if (daysRead.includes(removedDay)) {
|
|
66062
66274
|
windowPruned = true;
|
|
@@ -68109,7 +68321,7 @@ function resolveServerOptions(input = {}) {
|
|
|
68109
68321
|
const remoteAllowed = isRemoteApiAllowed(apiBaseUrl) || input.allowRemoteApi === true;
|
|
68110
68322
|
if (!remoteAllowed) {
|
|
68111
68323
|
throw new Error(
|
|
68112
|
-
`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.`
|
|
68113
68325
|
);
|
|
68114
68326
|
}
|
|
68115
68327
|
return {
|
|
@@ -68279,6 +68491,62 @@ function runSetCredential(args) {
|
|
|
68279
68491
|
var import_node_os16 = __toESM(require("node:os"), 1);
|
|
68280
68492
|
var import_node_fs22 = require("node:fs");
|
|
68281
68493
|
var import_node_path33 = __toESM(require("node:path"), 1);
|
|
68494
|
+
function readExistingTelemetryDefaultProject(deps, ecoPath) {
|
|
68495
|
+
try {
|
|
68496
|
+
const existing = deps.readFile?.(ecoPath);
|
|
68497
|
+
if (!existing) return void 0;
|
|
68498
|
+
const match = existing.match(/\bMRRLIN_TELEMETRY_DEFAULT_PROJECT:\s*("(?:[^"\\]|\\.)*")/);
|
|
68499
|
+
if (!match?.[1]) return void 0;
|
|
68500
|
+
const value = JSON.parse(match[1]);
|
|
68501
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
68502
|
+
} catch {
|
|
68503
|
+
return void 0;
|
|
68504
|
+
}
|
|
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
|
+
}
|
|
68282
68550
|
var SYSTEM_PATH_DIRS = ["/usr/bin", "/bin", "/usr/sbin", "/sbin"];
|
|
68283
68551
|
function defaultHomeOwnerUid(home) {
|
|
68284
68552
|
try {
|
|
@@ -68345,18 +68613,19 @@ function installService(deps) {
|
|
|
68345
68613
|
const pathEnv = Array.from(
|
|
68346
68614
|
/* @__PURE__ */ new Set([...resolvedBins.map((b) => import_node_path33.default.dirname(b)), ...SYSTEM_PATH_DIRS])
|
|
68347
68615
|
).join(":");
|
|
68348
|
-
const
|
|
68349
|
-
const
|
|
68616
|
+
const ecoPath = import_node_path33.default.join(home, ".mrrlin", "ecosystem.config.cjs");
|
|
68617
|
+
const apiTarget = deriveServiceApiTarget(deps, ecoPath);
|
|
68618
|
+
const telemetryDefaultProject = "MRRLIN_TELEMETRY_DEFAULT_PROJECT" in deps.env ? deps.env.MRRLIN_TELEMETRY_DEFAULT_PROJECT?.trim() || void 0 : readExistingTelemetryDefaultProject(deps, ecoPath);
|
|
68350
68619
|
const text = renderEcosystemConfig({
|
|
68351
68620
|
cwd: resolved.cwd,
|
|
68352
68621
|
home,
|
|
68353
68622
|
user,
|
|
68354
68623
|
codexHome: deps.env.CODEX_HOME?.trim() || void 0,
|
|
68355
68624
|
pathEnv,
|
|
68356
|
-
|
|
68357
|
-
|
|
68625
|
+
apiBaseUrl: apiTarget.apiBaseUrl,
|
|
68626
|
+
apiAllowRemote: apiTarget.apiAllowRemote,
|
|
68627
|
+
telemetryDefaultProject
|
|
68358
68628
|
});
|
|
68359
|
-
const ecoPath = import_node_path33.default.join(home, ".mrrlin", "ecosystem.config.cjs");
|
|
68360
68629
|
deps.writeFile(ecoPath, text);
|
|
68361
68630
|
const start = deps.runPm2(["startOrReload", ecoPath]);
|
|
68362
68631
|
if (start.code !== 0) {
|
|
@@ -68492,7 +68761,7 @@ function parseArgs(args) {
|
|
|
68492
68761
|
}
|
|
68493
68762
|
return map2;
|
|
68494
68763
|
}
|
|
68495
|
-
function
|
|
68764
|
+
function isRecord8(v) {
|
|
68496
68765
|
return v !== null && typeof v === "object";
|
|
68497
68766
|
}
|
|
68498
68767
|
async function runExtract(args, deps) {
|
|
@@ -68529,8 +68798,8 @@ Missing required flag(s): ${missing.join(
|
|
|
68529
68798
|
} catch {
|
|
68530
68799
|
continue;
|
|
68531
68800
|
}
|
|
68532
|
-
const payload =
|
|
68533
|
-
if (
|
|
68801
|
+
const payload = isRecord8(parsed) ? parsed.payload : void 0;
|
|
68802
|
+
if (isRecord8(payload) && payload.directorSessionId === sessionId) {
|
|
68534
68803
|
matchingLines.push(line);
|
|
68535
68804
|
}
|
|
68536
68805
|
}
|
|
@@ -68638,8 +68907,9 @@ Usage:
|
|
|
68638
68907
|
mrrlin-mcp serve Run the stdio MCP server. Honors
|
|
68639
68908
|
MRRLIN_API_BASE_URL (default http://127.0.0.1:8787)
|
|
68640
68909
|
and MRRLIN_TOKEN. Non-loopback API URLs require
|
|
68641
|
-
MRRLIN_API_ALLOW_REMOTE=1
|
|
68642
|
-
|
|
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.
|
|
68643
68913
|
|
|
68644
68914
|
mrrlin-mcp install-browser Idempotently install the dedicated, pinned
|
|
68645
68915
|
Chromium into ~/.mrrlin/browser/browsers (its own
|
|
@@ -68675,16 +68945,16 @@ Usage:
|
|
|
68675
68945
|
Bakes API-target env vars into the block as an
|
|
68676
68946
|
inline env table so \`serve\` boots even when the
|
|
68677
68947
|
local 127.0.0.1:8787 is not running. Setup guide:
|
|
68678
|
-
|
|
68679
|
-
is the canonical path
|
|
68680
|
-
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,
|
|
68681
68950
|
pass BOTH the URL AND the allow gate:
|
|
68682
68951
|
MRRLIN_API_BASE_URL=https://... \\
|
|
68683
68952
|
MRRLIN_API_ALLOW_REMOTE=1 mrrlin-mcp configure bridge
|
|
68684
68953
|
Without an allow gate, \`serve\` still refuses any
|
|
68685
68954
|
non-loopback URL (see \`mrrlin-mcp serve\` above).
|
|
68686
|
-
URL
|
|
68687
|
-
|
|
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.
|
|
68688
68958
|
Re-running BARE (no env vars) over a block you
|
|
68689
68959
|
already pinned to staging/a remote target is safe:
|
|
68690
68960
|
it keeps your env verbatim (reported as
|
|
@@ -68718,8 +68988,9 @@ Usage:
|
|
|
68718
68988
|
pending runs, read-only). Consumer env:
|
|
68719
68989
|
- MRRLIN_API_BASE_URL (default http://127.0.0.1:8787;
|
|
68720
68990
|
non-loopback requires MRRLIN_API_ALLOW_REMOTE=1,
|
|
68721
|
-
or
|
|
68722
|
-
-
|
|
68991
|
+
or an explicit staging API URL)
|
|
68992
|
+
- MRRLIN_API_BASE_URL=https://api-staging.mrrlin.com
|
|
68993
|
+
(known staging API)
|
|
68723
68994
|
- MRRLIN_TOKEN (service-principal auth to the API;
|
|
68724
68995
|
if unset, chat still works but claim-sweep /
|
|
68725
68996
|
autonomous execution stays disabled)
|
|
@@ -68970,6 +69241,16 @@ async function main() {
|
|
|
68970
69241
|
(0, import_node_fs23.mkdirSync)(import_node_path37.default.dirname(p), { recursive: true, mode: 448 });
|
|
68971
69242
|
(0, import_node_fs23.writeFileSync)(p, c, { mode: 384 });
|
|
68972
69243
|
},
|
|
69244
|
+
// Swallows every error on purpose — the only caller uses this to recover settings from the
|
|
69245
|
+
// config it is about to overwrite, and a missing or unreadable file means "no previous
|
|
69246
|
+
// value", never a failed install. See readExistingTelemetryDefaultProject.
|
|
69247
|
+
readFile: (p) => {
|
|
69248
|
+
try {
|
|
69249
|
+
return (0, import_node_fs23.readFileSync)(p, "utf8");
|
|
69250
|
+
} catch {
|
|
69251
|
+
return null;
|
|
69252
|
+
}
|
|
69253
|
+
},
|
|
68973
69254
|
runPm2: pm2Runner,
|
|
68974
69255
|
which: whichBin,
|
|
68975
69256
|
hasSecret: () => Boolean(readAgentSecret()),
|
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"
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"@mrrlin/client": "0.0.0",
|
|
25
25
|
"@mrrlin/agent-client": "0.0.0",
|
|
26
26
|
"@mrrlin/director-e2e": "0.0.0",
|
|
27
|
-
"@mrrlin/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": {
|