@mrrlin-dev/mcp 0.2.5 → 0.3.0
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 +998 -298
- package/dist/prompts/report-issue.md +32 -12
- package/package.json +4 -4
package/dist/bin.cjs
CHANGED
|
@@ -18671,11 +18671,11 @@ var require_dist = __commonJS({
|
|
|
18671
18671
|
});
|
|
18672
18672
|
|
|
18673
18673
|
// src/bin.ts
|
|
18674
|
-
var
|
|
18674
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
18675
18675
|
var import_node_child_process7 = require("node:child_process");
|
|
18676
18676
|
var import_node_fs14 = require("node:fs");
|
|
18677
|
-
var
|
|
18678
|
-
var
|
|
18677
|
+
var import_node_os11 = require("node:os");
|
|
18678
|
+
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
18679
18679
|
|
|
18680
18680
|
// src/install-codex.ts
|
|
18681
18681
|
var import_promises = __toESM(require("node:fs/promises"), 1);
|
|
@@ -18759,7 +18759,7 @@ async function installCodex(options) {
|
|
|
18759
18759
|
const configPath = import_node_path.default.join(codexHome, "config.toml");
|
|
18760
18760
|
const distribution = await detectInstallDistribution(options);
|
|
18761
18761
|
await import_promises.default.mkdir(codexHome, { recursive: true, mode: 448 });
|
|
18762
|
-
const writePrompts = async () => installPrompts(codexHome, options.prompts ?? []);
|
|
18762
|
+
const writePrompts = async () => installPrompts(codexHome, options.prompts ?? [], options.forcePrompts === true);
|
|
18763
18763
|
if (!await pathExists(configPath)) {
|
|
18764
18764
|
await import_promises.default.writeFile(configPath, renderBlock(options.binPath, distribution).replace(/^\n/, ""), { mode: 384 });
|
|
18765
18765
|
return { action: "created", configPath, prompts: await writePrompts() };
|
|
@@ -18799,7 +18799,7 @@ async function installCodex(options) {
|
|
|
18799
18799
|
await import_promises.default.writeFile(realPath, appended, { mode: 384 });
|
|
18800
18800
|
return { action: "appended", configPath: realPath, prompts: await writePrompts() };
|
|
18801
18801
|
}
|
|
18802
|
-
async function installPrompts(codexHome, prompts) {
|
|
18802
|
+
async function installPrompts(codexHome, prompts, forcePrompts) {
|
|
18803
18803
|
if (prompts.length === 0) return [];
|
|
18804
18804
|
const promptsDir2 = import_node_path.default.join(codexHome, "prompts");
|
|
18805
18805
|
await import_promises.default.mkdir(promptsDir2, { recursive: true, mode: 448 });
|
|
@@ -18812,12 +18812,21 @@ async function installPrompts(codexHome, prompts) {
|
|
|
18812
18812
|
} catch {
|
|
18813
18813
|
existing = null;
|
|
18814
18814
|
}
|
|
18815
|
+
if (existing === null) {
|
|
18816
|
+
await import_promises.default.writeFile(promptPath, prompt.content, { mode: 384 });
|
|
18817
|
+
out.push({ name: prompt.name, action: "created", promptPath });
|
|
18818
|
+
continue;
|
|
18819
|
+
}
|
|
18815
18820
|
if (existing === prompt.content) {
|
|
18816
18821
|
out.push({ name: prompt.name, action: "noop", promptPath });
|
|
18817
18822
|
continue;
|
|
18818
18823
|
}
|
|
18824
|
+
if (!forcePrompts) {
|
|
18825
|
+
out.push({ name: prompt.name, action: "skipped-modified", promptPath });
|
|
18826
|
+
continue;
|
|
18827
|
+
}
|
|
18819
18828
|
await import_promises.default.writeFile(promptPath, prompt.content, { mode: 384 });
|
|
18820
|
-
out.push({ name: prompt.name, action:
|
|
18829
|
+
out.push({ name: prompt.name, action: "updated", promptPath });
|
|
18821
18830
|
}
|
|
18822
18831
|
return out;
|
|
18823
18832
|
}
|
|
@@ -18838,11 +18847,11 @@ ${block}`;
|
|
|
18838
18847
|
}
|
|
18839
18848
|
|
|
18840
18849
|
// src/director-bridge.ts
|
|
18841
|
-
var
|
|
18850
|
+
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
18842
18851
|
var import_node_http = __toESM(require("node:http"), 1);
|
|
18843
18852
|
var import_node_os7 = __toESM(require("node:os"), 1);
|
|
18844
|
-
var
|
|
18845
|
-
var
|
|
18853
|
+
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
18854
|
+
var import_node_child_process3 = require("node:child_process");
|
|
18846
18855
|
var import_node_crypto4 = __toESM(require("node:crypto"), 1);
|
|
18847
18856
|
|
|
18848
18857
|
// ../../node_modules/.pnpm/ws@8.21.0/node_modules/ws/wrapper.mjs
|
|
@@ -19257,13 +19266,15 @@ async function acquireThread(codex, opts) {
|
|
|
19257
19266
|
}
|
|
19258
19267
|
|
|
19259
19268
|
// ../../packages/codex-client/dist/render-context-bundle.js
|
|
19269
|
+
var DEFAULT_SPEC_BUDGET_CHARS = 6e3;
|
|
19270
|
+
var DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS = 400;
|
|
19260
19271
|
function renderContextBundle(input) {
|
|
19261
19272
|
const sections = [];
|
|
19262
19273
|
sections.push(renderTaskSummary(input.task));
|
|
19263
|
-
sections.push(renderSpec(input.specMarkdown));
|
|
19274
|
+
sections.push(renderSpec(input.specMarkdown, input.compact));
|
|
19264
19275
|
sections.push(renderRunHistory(input.runs, input.openRunId));
|
|
19265
19276
|
sections.push(renderOpenRun(input.openRunId));
|
|
19266
|
-
sections.push(renderArtifacts(input.artifacts, input.omittedArtifactCount ?? 0));
|
|
19277
|
+
sections.push(renderArtifacts(input.artifacts, input.omittedArtifactCount ?? 0, input.compact));
|
|
19267
19278
|
sections.push(renderAutonomyEvents(input.autonomyEvents));
|
|
19268
19279
|
return sections.join("\n\n");
|
|
19269
19280
|
}
|
|
@@ -19281,11 +19292,55 @@ function renderTaskSummary(task) {
|
|
|
19281
19292
|
}
|
|
19282
19293
|
return lines2.join("\n");
|
|
19283
19294
|
}
|
|
19284
|
-
function renderSpec(specMarkdown) {
|
|
19295
|
+
function renderSpec(specMarkdown, compact) {
|
|
19285
19296
|
if (!specMarkdown)
|
|
19286
19297
|
return "## Spec\n(none)";
|
|
19287
|
-
|
|
19298
|
+
if (!compact)
|
|
19299
|
+
return `## Spec
|
|
19288
19300
|
${specMarkdown}`;
|
|
19301
|
+
const budget = compact.specBudgetChars ?? DEFAULT_SPEC_BUDGET_CHARS;
|
|
19302
|
+
if (specMarkdown.length <= budget)
|
|
19303
|
+
return `## Spec
|
|
19304
|
+
${specMarkdown}`;
|
|
19305
|
+
return `## Spec
|
|
19306
|
+
${compactSpecMarkdown(specMarkdown, budget, compact.projectSlug ?? null, compact.specWikiPageId ?? null)}`;
|
|
19307
|
+
}
|
|
19308
|
+
function compactSpecMarkdown(markdown, totalBudget, projectSlug, specWikiPageId) {
|
|
19309
|
+
const lines2 = markdown.split(/\r?\n/);
|
|
19310
|
+
const sections = [{ heading: "", body: [] }];
|
|
19311
|
+
for (const line of lines2) {
|
|
19312
|
+
if (/^#{1,6}\s/.test(line)) {
|
|
19313
|
+
sections.push({ heading: line, body: [] });
|
|
19314
|
+
} else {
|
|
19315
|
+
sections[sections.length - 1].body.push(line);
|
|
19316
|
+
}
|
|
19317
|
+
}
|
|
19318
|
+
const sectionCount = Math.max(1, sections.length);
|
|
19319
|
+
const perSectionBudget = Math.max(120, Math.floor(totalBudget / sectionCount));
|
|
19320
|
+
const out = [];
|
|
19321
|
+
for (const section of sections) {
|
|
19322
|
+
const bodyText = section.body.join("\n").replace(/^\s+|\s+$/g, "");
|
|
19323
|
+
if (section.heading)
|
|
19324
|
+
out.push(section.heading);
|
|
19325
|
+
if (!bodyText)
|
|
19326
|
+
continue;
|
|
19327
|
+
if (bodyText.length <= perSectionBudget) {
|
|
19328
|
+
out.push(bodyText);
|
|
19329
|
+
} else {
|
|
19330
|
+
const kept = bodyText.slice(0, perSectionBudget);
|
|
19331
|
+
const omitted = bodyText.length - perSectionBudget;
|
|
19332
|
+
out.push(`${kept}
|
|
19333
|
+
\u2026(${omitted} chars omitted; retrieve full spec for the rest)`);
|
|
19334
|
+
}
|
|
19335
|
+
}
|
|
19336
|
+
if (projectSlug && specWikiPageId) {
|
|
19337
|
+
out.push("");
|
|
19338
|
+
out.push(`> Compact spec. Source: mrrlin://wiki/${projectSlug}/${specWikiPageId} \u2014 call retrieve_context with this handle before any irreversible decision.`);
|
|
19339
|
+
} else {
|
|
19340
|
+
out.push("");
|
|
19341
|
+
out.push("> Compact spec. Retrieve the Specs/{id} wiki page via get_wiki_page before any irreversible decision.");
|
|
19342
|
+
}
|
|
19343
|
+
return out.join("\n");
|
|
19289
19344
|
}
|
|
19290
19345
|
function renderRunHistory(runs, openRunId) {
|
|
19291
19346
|
if (runs.length === 0)
|
|
@@ -19301,13 +19356,14 @@ function renderOpenRun(openRunId) {
|
|
|
19301
19356
|
return `## Open run
|
|
19302
19357
|
${openRunId ?? "(none)"}`;
|
|
19303
19358
|
}
|
|
19304
|
-
function renderArtifacts(artifacts, omittedCount) {
|
|
19359
|
+
function renderArtifacts(artifacts, omittedCount, compact) {
|
|
19305
19360
|
if (artifacts.length === 0 && omittedCount === 0)
|
|
19306
19361
|
return "## Artifacts\n(none)";
|
|
19307
19362
|
const lines2 = ["## Artifacts"];
|
|
19308
19363
|
if (omittedCount > 0) {
|
|
19309
19364
|
lines2.push(`(+${omittedCount} older artifact${omittedCount === 1 ? "" : "s"} omitted)`);
|
|
19310
19365
|
}
|
|
19366
|
+
let truncatedAny = false;
|
|
19311
19367
|
for (const artifact of artifacts) {
|
|
19312
19368
|
if (artifact.kind === "file") {
|
|
19313
19369
|
const p = artifact.payload;
|
|
@@ -19316,7 +19372,16 @@ function renderArtifacts(artifacts, omittedCount) {
|
|
|
19316
19372
|
continue;
|
|
19317
19373
|
}
|
|
19318
19374
|
}
|
|
19319
|
-
|
|
19375
|
+
const payloadText = JSON.stringify(artifact.payload);
|
|
19376
|
+
const budget = compact?.artifactPayloadBudgetChars ?? DEFAULT_ARTIFACT_PAYLOAD_BUDGET_CHARS;
|
|
19377
|
+
const willTruncate = !!compact && payloadText.length > budget;
|
|
19378
|
+
const rendered = willTruncate ? `${payloadText.slice(0, budget)}\u2026(${payloadText.length - budget} chars omitted)` : payloadText;
|
|
19379
|
+
if (willTruncate)
|
|
19380
|
+
truncatedAny = true;
|
|
19381
|
+
lines2.push(`- ${artifact.createdAt} [${artifact.kind}] run=${artifact.runId} ${rendered}`);
|
|
19382
|
+
}
|
|
19383
|
+
if (compact && (omittedCount > 0 || truncatedAny)) {
|
|
19384
|
+
lines2.push("> Compact artifacts. Use retrieve_context with a `mrrlin://run/{projectSlug}/{runId}/artifacts` handle (substitute the slug and run id from the lines above) to load a run's full artifact history.");
|
|
19320
19385
|
}
|
|
19321
19386
|
return lines2.join("\n");
|
|
19322
19387
|
}
|
|
@@ -33892,7 +33957,6 @@ var openApiOperationIds = {
|
|
|
33892
33957
|
listExecutionArtifacts: "listExecutionArtifacts",
|
|
33893
33958
|
listExecutionRuns: "listExecutionRuns",
|
|
33894
33959
|
listGithubInstallations: "listGithubInstallations",
|
|
33895
|
-
listGithubWebhookDeliveries: "listGithubWebhookDeliveries",
|
|
33896
33960
|
listGithubWorkflows: "listGithubWorkflows",
|
|
33897
33961
|
listPlans: "listPlans",
|
|
33898
33962
|
listProjects: "listProjects",
|
|
@@ -33909,7 +33973,6 @@ var openApiOperationIds = {
|
|
|
33909
33973
|
decideInboxItem: "decideInboxItem",
|
|
33910
33974
|
markInboxItemRead: "markInboxItemRead",
|
|
33911
33975
|
markAllInboxItemsRead: "markAllInboxItemsRead",
|
|
33912
|
-
receiveGithubWebhook: "receiveGithubWebhook",
|
|
33913
33976
|
searchKnowledge: "searchKnowledge",
|
|
33914
33977
|
updateExecutionRun: "updateExecutionRun",
|
|
33915
33978
|
upsertProjectSchedulerPolicy: "upsertProjectSchedulerPolicy",
|
|
@@ -33926,6 +33989,10 @@ var openApiOperationIds = {
|
|
|
33926
33989
|
listAgentCredentials: "listAgentCredentials",
|
|
33927
33990
|
revokeAgentCredential: "revokeAgentCredential",
|
|
33928
33991
|
exchangeAgentToken: "exchangeAgentToken",
|
|
33992
|
+
// ADR 0016 — operator-scoped credential routes.
|
|
33993
|
+
issueOperatorAgentCredential: "issueOperatorAgentCredential",
|
|
33994
|
+
listOperatorAgentCredentials: "listOperatorAgentCredentials",
|
|
33995
|
+
revokeOperatorAgentCredential: "revokeOperatorAgentCredential",
|
|
33929
33996
|
createArtifactFile: "createArtifactFile",
|
|
33930
33997
|
completeArtifactFile: "completeArtifactFile",
|
|
33931
33998
|
getArtifactFile: "getArtifactFile",
|
|
@@ -33949,7 +34016,12 @@ var operatorSessionSchema = external_exports.object({
|
|
|
33949
34016
|
login: external_exports.string().min(1),
|
|
33950
34017
|
projectSlug: external_exports.string().min(1),
|
|
33951
34018
|
provider: external_exports.literal("github"),
|
|
33952
|
-
repo: external_exports.string().min(1).nullable()
|
|
34019
|
+
repo: external_exports.string().min(1).nullable(),
|
|
34020
|
+
// ADR 0016: `scope` distinguishes project-pinned agent tokens (legacy, default) from
|
|
34021
|
+
// operator-scoped agent tokens. Only relevant for kind:"agent". `web` sessions ignore it.
|
|
34022
|
+
// Default 'project' for backward-compat: tokens minted before this field existed parse
|
|
34023
|
+
// cleanly into the project branch of the middleware (which mirrors today's behavior).
|
|
34024
|
+
scope: external_exports.enum(["project", "operator"]).default("project")
|
|
33953
34025
|
});
|
|
33954
34026
|
var AGENT_SESSION_TTL_MS = 1e3 * 60 * 60;
|
|
33955
34027
|
|
|
@@ -34251,15 +34323,6 @@ var mrrlinGithubWorkflowDispatchSchema = external_exports.object({
|
|
|
34251
34323
|
ref: external_exports.string().min(1),
|
|
34252
34324
|
workflowId: external_exports.string().min(1)
|
|
34253
34325
|
});
|
|
34254
|
-
var mrrlinGithubWebhookDeliverySchema = external_exports.object({
|
|
34255
|
-
action: external_exports.string().min(1).nullable(),
|
|
34256
|
-
deliveryId: external_exports.string().min(1),
|
|
34257
|
-
eventType: external_exports.string().min(1),
|
|
34258
|
-
installationId: external_exports.number().int().positive().nullable(),
|
|
34259
|
-
projectSlug: mrrlinProjectSlugSchema.nullable(),
|
|
34260
|
-
receivedAt: external_exports.string().datetime(),
|
|
34261
|
-
repositoryFullName: external_exports.string().regex(/^[^/]+\/[^/]+$/).nullable()
|
|
34262
|
-
});
|
|
34263
34326
|
var mrrlinProjectSummarySchema = external_exports.object({
|
|
34264
34327
|
displayName: external_exports.string().min(1),
|
|
34265
34328
|
emoji: mrrlinProjectEmojiSchema,
|
|
@@ -34485,14 +34548,6 @@ var githubBindProjectRepoRequestSchema = external_exports.object({
|
|
|
34485
34548
|
var githubBindProjectRepoResponseSchema = external_exports.object({
|
|
34486
34549
|
data: mrrlinProjectSummarySchema
|
|
34487
34550
|
});
|
|
34488
|
-
var githubWebhookReceiptResponseSchema = external_exports.object({
|
|
34489
|
-
data: external_exports.object({
|
|
34490
|
-
deliveryId: external_exports.string().min(1),
|
|
34491
|
-
eventType: external_exports.string().min(1),
|
|
34492
|
-
linkedProjectSlugs: external_exports.array(mrrlinProjectSlugSchema),
|
|
34493
|
-
stored: external_exports.boolean()
|
|
34494
|
-
})
|
|
34495
|
-
});
|
|
34496
34551
|
var githubCreateBranchRequestSchema = external_exports.object({
|
|
34497
34552
|
baseBranch: external_exports.string().min(1).default("main"),
|
|
34498
34553
|
branchName: external_exports.string().min(1)
|
|
@@ -34542,9 +34597,6 @@ var githubLatestDeploymentQuerySchema = external_exports.object({
|
|
|
34542
34597
|
var githubLatestDeploymentResponseSchema = external_exports.object({
|
|
34543
34598
|
data: external_exports.object({ deploymentId: external_exports.number().int(), environmentUrl: external_exports.string() }).nullable()
|
|
34544
34599
|
});
|
|
34545
|
-
var githubWebhookDeliveryListResponseSchema = external_exports.object({
|
|
34546
|
-
data: external_exports.array(mrrlinGithubWebhookDeliverySchema)
|
|
34547
|
-
});
|
|
34548
34600
|
var githubMintPushTokenRequestSchema = external_exports.object({
|
|
34549
34601
|
runId: mrrlinExecutionRunIdSchema,
|
|
34550
34602
|
leaseId: external_exports.string().min(1)
|
|
@@ -34757,13 +34809,24 @@ var agentCredentialSummarySchema = external_exports.object({
|
|
|
34757
34809
|
createdAt: external_exports.string().datetime(),
|
|
34758
34810
|
lastUsedAt: external_exports.string().datetime().nullable(),
|
|
34759
34811
|
revokedAt: external_exports.string().datetime().nullable(),
|
|
34760
|
-
expiresAt: external_exports.string().datetime().nullable()
|
|
34812
|
+
expiresAt: external_exports.string().datetime().nullable(),
|
|
34813
|
+
// ADR 0016: surface scope so UI can label rows; seedProjectSlug is audit-only ("issued from
|
|
34814
|
+
// project X"). Both default to 'project'/null for back-compat with legacy rows.
|
|
34815
|
+
scope: external_exports.enum(["project", "operator"]).default("project"),
|
|
34816
|
+
seedProjectSlug: external_exports.string().nullable().default(null)
|
|
34761
34817
|
});
|
|
34762
34818
|
var agentCredentialListResponseSchema = external_exports.object({
|
|
34763
34819
|
data: external_exports.array(agentCredentialSummarySchema)
|
|
34764
34820
|
});
|
|
34765
34821
|
var exchangeAgentTokenRequestSchema = external_exports.object({
|
|
34766
|
-
|
|
34822
|
+
// ADR 0016 §3 no-oracle: the exchange schema MUST accept any string shape for `secret` and
|
|
34823
|
+
// `projectSlug`. Empty/whitespace/missing inputs are handled by the handler's constant-work
|
|
34824
|
+
// 5-step loop and flow into the same uniform 401 path as wrong-secret. A schema-level 400
|
|
34825
|
+
// would leak (attacker can distinguish "malformed input" from "wrong secret" before the
|
|
34826
|
+
// handler runs). Validation of structural content (e.g. selector parseability for `secret`)
|
|
34827
|
+
// happens inside the handler against the dummy hash.
|
|
34828
|
+
secret: external_exports.string(),
|
|
34829
|
+
projectSlug: external_exports.string().optional()
|
|
34767
34830
|
});
|
|
34768
34831
|
var exchangeAgentTokenResponseSchema = external_exports.object({
|
|
34769
34832
|
token: external_exports.string().min(1),
|
|
@@ -34978,10 +35041,10 @@ var AgentTokenProviderError = class extends Error {
|
|
|
34978
35041
|
this.code = code;
|
|
34979
35042
|
}
|
|
34980
35043
|
};
|
|
34981
|
-
async function defaultExchange(secret, baseUrl) {
|
|
35044
|
+
async function defaultExchange(secret, baseUrl, projectSlug) {
|
|
34982
35045
|
const url2 = `${baseUrl.replace(/\/$/, "")}/auth/agent/token`;
|
|
34983
35046
|
const response = await fetch(url2, {
|
|
34984
|
-
body: JSON.stringify({ secret }),
|
|
35047
|
+
body: JSON.stringify({ secret, ...projectSlug ? { projectSlug } : {} }),
|
|
34985
35048
|
headers: { "content-type": "application/json" },
|
|
34986
35049
|
method: "POST"
|
|
34987
35050
|
});
|
|
@@ -35006,22 +35069,28 @@ function createAgentTokenProvider(opts) {
|
|
|
35006
35069
|
const { baseUrl, secret } = opts;
|
|
35007
35070
|
const exchange = opts.exchange ?? defaultExchange;
|
|
35008
35071
|
const now = opts.now ?? (() => Date.now());
|
|
35009
|
-
|
|
35010
|
-
|
|
35011
|
-
|
|
35072
|
+
const LEGACY_KEY = "__mrrlin_agent_no_slug__";
|
|
35073
|
+
const cacheByKey = /* @__PURE__ */ new Map();
|
|
35074
|
+
function cacheKey(projectSlug) {
|
|
35075
|
+
return projectSlug ?? LEGACY_KEY;
|
|
35076
|
+
}
|
|
35077
|
+
function isValid(entry) {
|
|
35078
|
+
if (!entry)
|
|
35012
35079
|
return false;
|
|
35013
|
-
const expiresAtMs = new Date(
|
|
35080
|
+
const expiresAtMs = new Date(entry.expiresAt).getTime();
|
|
35014
35081
|
return now() < expiresAtMs - REFRESH_BUFFER_MS;
|
|
35015
35082
|
}
|
|
35016
|
-
async function getSession() {
|
|
35017
|
-
|
|
35018
|
-
|
|
35083
|
+
async function getSession(projectSlug) {
|
|
35084
|
+
const key = cacheKey(projectSlug);
|
|
35085
|
+
let entry = cacheByKey.get(key);
|
|
35086
|
+
if (!isValid(entry)) {
|
|
35087
|
+
entry = await exchange(secret, baseUrl, projectSlug);
|
|
35088
|
+
cacheByKey.set(key, entry);
|
|
35019
35089
|
}
|
|
35020
|
-
|
|
35021
|
-
return { token: c.token, projectSlug: c.projectSlug, login: c.login };
|
|
35090
|
+
return { token: entry.token, projectSlug: entry.projectSlug, login: entry.login };
|
|
35022
35091
|
}
|
|
35023
|
-
async function getToken() {
|
|
35024
|
-
return (await getSession()).token;
|
|
35092
|
+
async function getToken(projectSlug) {
|
|
35093
|
+
return (await getSession(projectSlug)).token;
|
|
35025
35094
|
}
|
|
35026
35095
|
return { getToken, getSession };
|
|
35027
35096
|
}
|
|
@@ -35319,10 +35388,6 @@ function createMrrlinClient(config2) {
|
|
|
35319
35388
|
const body = await request(`${projectPath(projectSlug)}/github/workflows`);
|
|
35320
35389
|
return githubWorkflowListResponseSchema.parse(body).data;
|
|
35321
35390
|
},
|
|
35322
|
-
async listGithubWebhookDeliveries(projectSlug) {
|
|
35323
|
-
const body = await request(`${projectPath(projectSlug)}/github/webhook-deliveries`);
|
|
35324
|
-
return githubWebhookDeliveryListResponseSchema.parse(body).data;
|
|
35325
|
-
},
|
|
35326
35391
|
async listProjects() {
|
|
35327
35392
|
const body = await request("/projects");
|
|
35328
35393
|
return projectSummaryListResponseSchema.parse(body).data;
|
|
@@ -35754,6 +35819,24 @@ function createMrrlinClient(config2) {
|
|
|
35754
35819
|
method: "POST"
|
|
35755
35820
|
});
|
|
35756
35821
|
return exchangeAgentTokenResponseSchema.parse(body);
|
|
35822
|
+
},
|
|
35823
|
+
// ── ADR 0016 — operator-scoped credential client methods ─────────────────
|
|
35824
|
+
async issueOperatorAgentCredential(input) {
|
|
35825
|
+
const body = await request("/operator/agent-credentials", {
|
|
35826
|
+
body: JSON.stringify(input),
|
|
35827
|
+
headers: { "content-type": "application/json" },
|
|
35828
|
+
method: "POST"
|
|
35829
|
+
});
|
|
35830
|
+
return agentCredentialResponseSchema.parse(body.data);
|
|
35831
|
+
},
|
|
35832
|
+
async listOperatorAgentCredentials() {
|
|
35833
|
+
const body = await request("/operator/agent-credentials");
|
|
35834
|
+
return agentCredentialListResponseSchema.parse(body).data;
|
|
35835
|
+
},
|
|
35836
|
+
async revokeOperatorAgentCredential(credentialId) {
|
|
35837
|
+
await request(`/operator/agent-credentials/${encodeURIComponent(credentialId)}`, {
|
|
35838
|
+
method: "DELETE"
|
|
35839
|
+
});
|
|
35757
35840
|
}
|
|
35758
35841
|
};
|
|
35759
35842
|
}
|
|
@@ -36427,7 +36510,17 @@ async function driveRun(deps, projectSlug, runId) {
|
|
|
36427
36510
|
}
|
|
36428
36511
|
const leaseId = claimed.leaseId;
|
|
36429
36512
|
const task = await deps.client.getTask(projectSlug, claimed.taskId);
|
|
36430
|
-
const
|
|
36513
|
+
const compactEnabled = (process.env.MRRLIN_MCP_COMPACT_DISABLE ?? "").trim() !== "1";
|
|
36514
|
+
const context = await gatherContext(deps.client, projectSlug, task, runId);
|
|
36515
|
+
const bundle = renderContextBundle(
|
|
36516
|
+
compactEnabled ? {
|
|
36517
|
+
...context,
|
|
36518
|
+
compact: {
|
|
36519
|
+
projectSlug,
|
|
36520
|
+
specWikiPageId: task.specWikiPageId ?? null
|
|
36521
|
+
}
|
|
36522
|
+
} : context
|
|
36523
|
+
);
|
|
36431
36524
|
const checkout = deps.checkoutRegistry?.get(projectSlug) ?? null;
|
|
36432
36525
|
let sandbox = "read-only";
|
|
36433
36526
|
let turnCwd = deps.codexCwd ?? null;
|
|
@@ -40918,75 +41011,10 @@ var CheckoutRegistry = class {
|
|
|
40918
41011
|
}
|
|
40919
41012
|
};
|
|
40920
41013
|
|
|
40921
|
-
// src/checkout-scan.ts
|
|
40922
|
-
var import_node_fs8 = require("node:fs");
|
|
40923
|
-
var import_node_path9 = require("node:path");
|
|
40924
|
-
var import_node_child_process3 = require("node:child_process");
|
|
40925
|
-
|
|
40926
|
-
// src/git-remote-match.ts
|
|
40927
|
-
function normalizeRemote(url2) {
|
|
40928
|
-
const trimmed = url2.trim().replace(/\.git$/i, "");
|
|
40929
|
-
const scp = /^[^@]+@[^:]+:(.+)$/.exec(trimmed);
|
|
40930
|
-
const pathPart = scp ? scp[1] : (() => {
|
|
40931
|
-
try {
|
|
40932
|
-
return new URL(trimmed).pathname.replace(/^\/+/, "");
|
|
40933
|
-
} catch {
|
|
40934
|
-
return null;
|
|
40935
|
-
}
|
|
40936
|
-
})();
|
|
40937
|
-
if (!pathPart) return null;
|
|
40938
|
-
const segs = pathPart.split("/").filter(Boolean);
|
|
40939
|
-
if (segs.length < 2) return null;
|
|
40940
|
-
return `${segs[segs.length - 2]}/${segs[segs.length - 1]}`.toLowerCase();
|
|
40941
|
-
}
|
|
40942
|
-
function remoteMatchesRepo(remoteUrl, repoFullName) {
|
|
40943
|
-
const a = normalizeRemote(remoteUrl);
|
|
40944
|
-
return a !== null && a === repoFullName.trim().toLowerCase();
|
|
40945
|
-
}
|
|
40946
|
-
|
|
40947
|
-
// src/checkout-scan.ts
|
|
40948
|
-
var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".cache", "Library", ".Trash"]);
|
|
40949
|
-
function originUrl(dir) {
|
|
40950
|
-
try {
|
|
40951
|
-
return (0, import_node_child_process3.execFileSync)("git", ["-C", dir, "remote", "get-url", "origin"], {
|
|
40952
|
-
encoding: "utf8",
|
|
40953
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
40954
|
-
}).trim();
|
|
40955
|
-
} catch {
|
|
40956
|
-
return null;
|
|
40957
|
-
}
|
|
40958
|
-
}
|
|
40959
|
-
function scanForCheckouts(roots, repoFullName, opts = {}) {
|
|
40960
|
-
const maxDepth = opts.maxDepth ?? 3;
|
|
40961
|
-
const out = [];
|
|
40962
|
-
const walk = (dir, depth) => {
|
|
40963
|
-
if (depth > maxDepth) return;
|
|
40964
|
-
const url2 = originUrl(dir);
|
|
40965
|
-
if (url2) {
|
|
40966
|
-
if (remoteMatchesRepo(url2, repoFullName)) out.push(dir);
|
|
40967
|
-
return;
|
|
40968
|
-
}
|
|
40969
|
-
let entries = [];
|
|
40970
|
-
try {
|
|
40971
|
-
entries = (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SKIP.has(e.name)).map((e) => e.name);
|
|
40972
|
-
} catch {
|
|
40973
|
-
return;
|
|
40974
|
-
}
|
|
40975
|
-
for (const name of entries) walk((0, import_node_path9.join)(dir, name), depth + 1);
|
|
40976
|
-
};
|
|
40977
|
-
for (const root of roots) walk(root, 0);
|
|
40978
|
-
return out;
|
|
40979
|
-
}
|
|
40980
|
-
function defaultScanRoots(home) {
|
|
40981
|
-
const fromEnv = (process.env.MRRLIN_DIRECTOR_BRIDGE_SCAN_ROOTS ?? "").trim();
|
|
40982
|
-
if (fromEnv) return fromEnv.split(":").filter(Boolean);
|
|
40983
|
-
return ["dev", "projects", "src", "work"].map((d) => (0, import_node_path9.join)(home, d));
|
|
40984
|
-
}
|
|
40985
|
-
|
|
40986
41014
|
// src/executor-server.ts
|
|
40987
|
-
var
|
|
41015
|
+
var import_node_fs8 = require("node:fs");
|
|
40988
41016
|
var import_node_os6 = require("node:os");
|
|
40989
|
-
var
|
|
41017
|
+
var import_node_path9 = require("node:path");
|
|
40990
41018
|
var DENY_READ_GLOBS = [
|
|
40991
41019
|
"**/.ssh/**",
|
|
40992
41020
|
"**/.aws/**",
|
|
@@ -41019,15 +41047,15 @@ function buildExecutorConfig(opts) {
|
|
|
41019
41047
|
}
|
|
41020
41048
|
function operatorSecretPaths(realHome = (0, import_node_os6.homedir)()) {
|
|
41021
41049
|
return [
|
|
41022
|
-
(0,
|
|
41023
|
-
(0,
|
|
41024
|
-
(0,
|
|
41025
|
-
(0,
|
|
41026
|
-
(0,
|
|
41027
|
-
(0,
|
|
41028
|
-
(0,
|
|
41029
|
-
(0,
|
|
41030
|
-
(0,
|
|
41050
|
+
(0, import_node_path9.join)(realHome, ".ssh"),
|
|
41051
|
+
(0, import_node_path9.join)(realHome, ".aws"),
|
|
41052
|
+
(0, import_node_path9.join)(realHome, ".config", "gh"),
|
|
41053
|
+
(0, import_node_path9.join)(realHome, ".npmrc"),
|
|
41054
|
+
(0, import_node_path9.join)(realHome, ".netrc"),
|
|
41055
|
+
(0, import_node_path9.join)(realHome, ".gnupg"),
|
|
41056
|
+
(0, import_node_path9.join)(realHome, ".kube"),
|
|
41057
|
+
(0, import_node_path9.join)(realHome, ".docker", "config.json"),
|
|
41058
|
+
(0, import_node_path9.join)(realHome, ".config", "gcloud")
|
|
41031
41059
|
];
|
|
41032
41060
|
}
|
|
41033
41061
|
var SECRET_DIRECTORY_BASENAMES = /* @__PURE__ */ new Set([".ssh", ".aws", "gh", ".gnupg", ".kube", "gcloud"]);
|
|
@@ -41036,8 +41064,8 @@ function isSecretDirectoryPath(p) {
|
|
|
41036
41064
|
return SECRET_DIRECTORY_BASENAMES.has(base);
|
|
41037
41065
|
}
|
|
41038
41066
|
function writeExecutorCodexHome(scratchDir, opts) {
|
|
41039
|
-
const codexHome = (0,
|
|
41040
|
-
(0,
|
|
41067
|
+
const codexHome = (0, import_node_path9.join)(scratchDir, ".codex");
|
|
41068
|
+
(0, import_node_fs8.mkdirSync)(codexHome, { recursive: true });
|
|
41041
41069
|
const hasEgress = opts.egressDomains.length > 0;
|
|
41042
41070
|
const networkSection = hasEgress ? [
|
|
41043
41071
|
"[permissions.executor.network]",
|
|
@@ -41072,21 +41100,21 @@ function writeExecutorCodexHome(scratchDir, opts) {
|
|
|
41072
41100
|
networkSection,
|
|
41073
41101
|
""
|
|
41074
41102
|
].join("\n");
|
|
41075
|
-
(0,
|
|
41103
|
+
(0, import_node_fs8.writeFileSync)((0, import_node_path9.join)(codexHome, "config.toml"), toml3, "utf8");
|
|
41076
41104
|
return codexHome;
|
|
41077
41105
|
}
|
|
41078
41106
|
async function startExecutorServer(opts) {
|
|
41079
41107
|
const egressDomains = opts.egressDomains ?? DEFAULT_EGRESS_DOMAINS;
|
|
41080
41108
|
const realHome = (0, import_node_os6.homedir)();
|
|
41081
41109
|
const denySecretPaths = operatorSecretPaths(realHome);
|
|
41082
|
-
const scratchDir = (0,
|
|
41110
|
+
const scratchDir = (0, import_node_fs8.mkdtempSync)((0, import_node_path9.join)((0, import_node_os6.tmpdir)(), "mrrlin-executor-"));
|
|
41083
41111
|
const scratchHome = scratchDir;
|
|
41084
41112
|
let scratchCleaned = false;
|
|
41085
41113
|
const cleanScratch = () => {
|
|
41086
41114
|
if (scratchCleaned) return;
|
|
41087
41115
|
scratchCleaned = true;
|
|
41088
41116
|
try {
|
|
41089
|
-
(0,
|
|
41117
|
+
(0, import_node_fs8.rmSync)(scratchDir, { recursive: true, force: true });
|
|
41090
41118
|
} catch {
|
|
41091
41119
|
}
|
|
41092
41120
|
};
|
|
@@ -41277,17 +41305,10 @@ function extractVerificationSection(markdown) {
|
|
|
41277
41305
|
}
|
|
41278
41306
|
|
|
41279
41307
|
// src/bridge-log.ts
|
|
41280
|
-
var
|
|
41281
|
-
var
|
|
41282
|
-
|
|
41283
|
-
|
|
41284
|
-
},
|
|
41285
|
-
logOut() {
|
|
41286
|
-
},
|
|
41287
|
-
close() {
|
|
41288
|
-
}
|
|
41289
|
-
};
|
|
41290
|
-
var REDACT_DEPTH_CAP = 8;
|
|
41308
|
+
var import_node_fs9 = require("node:fs");
|
|
41309
|
+
var import_node_path10 = require("node:path");
|
|
41310
|
+
|
|
41311
|
+
// src/redact.ts
|
|
41291
41312
|
var REDACTED = "[REDACTED]";
|
|
41292
41313
|
var SECRET_PATTERNS = [
|
|
41293
41314
|
/ghp_[A-Za-z0-9]{20,}/g,
|
|
@@ -41303,17 +41324,36 @@ var SECRET_PATTERNS = [
|
|
|
41303
41324
|
/\b[A-Za-z0-9+/]{40,}={0,2}\b/g
|
|
41304
41325
|
// long base64-ish run (opaque tokens)
|
|
41305
41326
|
];
|
|
41306
|
-
function
|
|
41307
|
-
let out =
|
|
41327
|
+
function redact(input) {
|
|
41328
|
+
let out = input;
|
|
41308
41329
|
for (const re of SECRET_PATTERNS) out = out.replace(re, REDACTED);
|
|
41309
41330
|
return out;
|
|
41310
41331
|
}
|
|
41332
|
+
|
|
41333
|
+
// src/bridge-log.ts
|
|
41334
|
+
var NOOP_LOGGER = {
|
|
41335
|
+
logIn() {
|
|
41336
|
+
},
|
|
41337
|
+
logOut() {
|
|
41338
|
+
},
|
|
41339
|
+
close() {
|
|
41340
|
+
}
|
|
41341
|
+
};
|
|
41342
|
+
var REDACT_DEPTH_CAP = 8;
|
|
41343
|
+
var IMAGE_DATA_URI_RE = /^(data:image\/[a-zA-Z0-9.+-]+;base64,)([A-Za-z0-9+/=]+)$/;
|
|
41344
|
+
function redactImageDataUri(value) {
|
|
41345
|
+
const m = value.match(IMAGE_DATA_URI_RE);
|
|
41346
|
+
if (!m) return value;
|
|
41347
|
+
const prefix = m[1];
|
|
41348
|
+
const body = m[2];
|
|
41349
|
+
return `${prefix}\u2026(${body.length} bytes)`;
|
|
41350
|
+
}
|
|
41311
41351
|
function redactValue(value, depth) {
|
|
41312
|
-
if (typeof value === "string") return
|
|
41352
|
+
if (typeof value === "string") return redact(redactImageDataUri(value));
|
|
41313
41353
|
if (value === null || typeof value !== "object") return value;
|
|
41314
41354
|
if (depth >= REDACT_DEPTH_CAP) {
|
|
41315
41355
|
try {
|
|
41316
|
-
return
|
|
41356
|
+
return redact(JSON.stringify(value));
|
|
41317
41357
|
} catch {
|
|
41318
41358
|
return REDACTED;
|
|
41319
41359
|
}
|
|
@@ -41351,14 +41391,14 @@ function resolvePromptsEnabled(env) {
|
|
|
41351
41391
|
}
|
|
41352
41392
|
function createBridgeLogger(stateDir, env = process.env) {
|
|
41353
41393
|
if (!resolveBridgeLogEnabled(env)) return NOOP_LOGGER;
|
|
41354
|
-
const logsDir = (0,
|
|
41394
|
+
const logsDir = (0, import_node_path10.join)(stateDir, "logs");
|
|
41355
41395
|
try {
|
|
41356
|
-
(0,
|
|
41396
|
+
(0, import_node_fs9.mkdirSync)(logsDir, { recursive: true });
|
|
41357
41397
|
} catch {
|
|
41358
41398
|
return NOOP_LOGGER;
|
|
41359
41399
|
}
|
|
41360
41400
|
const includePrompts = resolvePromptsEnabled(env);
|
|
41361
|
-
const fileFor = () => (0,
|
|
41401
|
+
const fileFor = () => (0, import_node_path10.join)(logsDir, `bridge-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.jsonl`);
|
|
41362
41402
|
const write = (dir, rec) => {
|
|
41363
41403
|
try {
|
|
41364
41404
|
const line = JSON.stringify({
|
|
@@ -41370,7 +41410,7 @@ function createBridgeLogger(stateDir, env = process.env) {
|
|
|
41370
41410
|
...rec.ms !== void 0 ? { ms: rec.ms } : {},
|
|
41371
41411
|
payload: redactMessage(rec.payload, { includePrompts })
|
|
41372
41412
|
}) + "\n";
|
|
41373
|
-
(0,
|
|
41413
|
+
(0, import_node_fs9.appendFileSync)(fileFor(), line);
|
|
41374
41414
|
} catch {
|
|
41375
41415
|
}
|
|
41376
41416
|
};
|
|
@@ -41404,9 +41444,9 @@ function normalizeModel(value) {
|
|
|
41404
41444
|
return trimmed ? trimmed : null;
|
|
41405
41445
|
}
|
|
41406
41446
|
function neutralCheckoutCwd(stateDir) {
|
|
41407
|
-
const dir =
|
|
41447
|
+
const dir = import_node_path11.default.join(stateDir, "no-checkout");
|
|
41408
41448
|
try {
|
|
41409
|
-
|
|
41449
|
+
import_node_fs10.default.mkdirSync(dir, { recursive: true });
|
|
41410
41450
|
} catch {
|
|
41411
41451
|
}
|
|
41412
41452
|
return dir;
|
|
@@ -41597,7 +41637,7 @@ function resolveDefaultBranch(checkoutPath) {
|
|
|
41597
41637
|
if (!checkoutPath) return "main";
|
|
41598
41638
|
const runGit = (args) => {
|
|
41599
41639
|
try {
|
|
41600
|
-
return (0,
|
|
41640
|
+
return (0, import_node_child_process3.execFileSync)("git", ["-C", checkoutPath, ...args], {
|
|
41601
41641
|
encoding: "utf8",
|
|
41602
41642
|
stdio: ["ignore", "pipe", "ignore"],
|
|
41603
41643
|
timeout: 15e3,
|
|
@@ -41629,7 +41669,7 @@ async function runOrphanWorktreeSweep(client, opts) {
|
|
|
41629
41669
|
const cutoff = Date.now() - worktreeRetentionMs();
|
|
41630
41670
|
const isReapable = (worktreePath) => {
|
|
41631
41671
|
try {
|
|
41632
|
-
return
|
|
41672
|
+
return import_node_fs10.default.statSync(worktreePath).mtimeMs < cutoff;
|
|
41633
41673
|
} catch {
|
|
41634
41674
|
return false;
|
|
41635
41675
|
}
|
|
@@ -41666,8 +41706,8 @@ async function runOrphanWorktreeSweep(client, opts) {
|
|
|
41666
41706
|
}
|
|
41667
41707
|
}
|
|
41668
41708
|
function readStateDir() {
|
|
41669
|
-
if (process.env.CODEX_HOME) return
|
|
41670
|
-
return
|
|
41709
|
+
if (process.env.CODEX_HOME) return import_node_path11.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge");
|
|
41710
|
+
return import_node_path11.default.join(import_node_os7.default.homedir(), ".mrrlin", "director-bridge");
|
|
41671
41711
|
}
|
|
41672
41712
|
function readAllowedOrigins() {
|
|
41673
41713
|
const raw = (process.env.MRRLIN_DIRECTOR_BRIDGE_ALLOWED_ORIGINS ?? "").trim();
|
|
@@ -41931,53 +41971,26 @@ function createBridgeMessageHandler(deps) {
|
|
|
41931
41971
|
sendForSpan({ type: "reconfigured", directorSessionId: directorSessionId2 });
|
|
41932
41972
|
return;
|
|
41933
41973
|
}
|
|
41934
|
-
if (msg.type
|
|
41935
|
-
|
|
41936
|
-
const repo = typeof msg.repo === "string" ? msg.repo.trim() : "";
|
|
41937
|
-
if (!projectSlug || !repo) {
|
|
41938
|
-
sendForSpan({ type: "error", error: "discover-checkouts requires projectSlug and repo." });
|
|
41939
|
-
return;
|
|
41940
|
-
}
|
|
41941
|
-
const roots = deps.scanRoots ?? defaultScanRoots(import_node_os7.default.homedir());
|
|
41942
|
-
const candidates = scanForCheckouts(roots, repo);
|
|
41943
|
-
sendForSpan({ type: "checkout-candidates", projectSlug, candidates });
|
|
41974
|
+
if (msg.type !== "turn" || typeof msg.directorSessionId !== "string" || typeof msg.message !== "string") {
|
|
41975
|
+
sendForSpan({ type: "error", error: "Invalid message schema." });
|
|
41944
41976
|
return;
|
|
41945
41977
|
}
|
|
41946
|
-
|
|
41947
|
-
|
|
41948
|
-
|
|
41949
|
-
|
|
41950
|
-
|
|
41951
|
-
sendForSpan({ type: "checkout-error", projectSlug, error: "confirm-checkout requires projectSlug and path." });
|
|
41952
|
-
return;
|
|
41953
|
-
}
|
|
41954
|
-
let remoteUrl = null;
|
|
41955
|
-
try {
|
|
41956
|
-
remoteUrl = (0, import_node_child_process4.execFileSync)("git", ["-C", checkoutPath, "remote", "get-url", "origin"], {
|
|
41957
|
-
encoding: "utf8",
|
|
41958
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
41959
|
-
timeout: 15e3,
|
|
41960
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
41961
|
-
}).trim();
|
|
41962
|
-
} catch {
|
|
41963
|
-
sendForSpan({ type: "checkout-error", projectSlug, error: `Path is not a git repository or has no origin remote: ${checkoutPath}` });
|
|
41978
|
+
const rawImage = msg.imageDataUri;
|
|
41979
|
+
let imageDataUri = null;
|
|
41980
|
+
if (rawImage !== void 0 && rawImage !== null) {
|
|
41981
|
+
if (typeof rawImage !== "string") {
|
|
41982
|
+
sendForSpan({ type: "error", error: "imageDataUri must be a string." });
|
|
41964
41983
|
return;
|
|
41965
41984
|
}
|
|
41966
|
-
if (
|
|
41967
|
-
sendForSpan({ type: "
|
|
41985
|
+
if (!rawImage.startsWith("data:image/")) {
|
|
41986
|
+
sendForSpan({ type: "error", error: "imageDataUri must be a data URI for an image." });
|
|
41968
41987
|
return;
|
|
41969
41988
|
}
|
|
41970
|
-
if (
|
|
41971
|
-
sendForSpan({ type: "
|
|
41989
|
+
if (rawImage.length > 3 * 1024 * 1024) {
|
|
41990
|
+
sendForSpan({ type: "error", error: "Image too large." });
|
|
41972
41991
|
return;
|
|
41973
41992
|
}
|
|
41974
|
-
|
|
41975
|
-
sendForSpan({ type: "checkout-confirmed", projectSlug, path: checkoutPath });
|
|
41976
|
-
return;
|
|
41977
|
-
}
|
|
41978
|
-
if (msg.type !== "turn" || typeof msg.directorSessionId !== "string" || typeof msg.message !== "string") {
|
|
41979
|
-
sendForSpan({ type: "error", error: "Invalid message schema." });
|
|
41980
|
-
return;
|
|
41993
|
+
imageDataUri = rawImage;
|
|
41981
41994
|
}
|
|
41982
41995
|
const directorSessionId = msg.directorSessionId.trim();
|
|
41983
41996
|
if (!directorSessionId) {
|
|
@@ -41985,7 +41998,7 @@ function createBridgeMessageHandler(deps) {
|
|
|
41985
41998
|
return;
|
|
41986
41999
|
}
|
|
41987
42000
|
const inputText = msg.message.trim();
|
|
41988
|
-
if (!inputText) {
|
|
42001
|
+
if (!inputText && !imageDataUri) {
|
|
41989
42002
|
sendForSpan({ type: "error", directorSessionId, error: "message is required." });
|
|
41990
42003
|
return;
|
|
41991
42004
|
}
|
|
@@ -42131,11 +42144,19 @@ function createBridgeMessageHandler(deps) {
|
|
|
42131
42144
|
socket.once("close", onSocketClose);
|
|
42132
42145
|
pokeActivity = () => watchdog.poke();
|
|
42133
42146
|
const runSequence = (async () => {
|
|
42147
|
+
const turnInput = [];
|
|
42148
|
+
if (inputText.length > 0) {
|
|
42149
|
+
const composed = contextPrefix ? `${contextPrefix}
|
|
42150
|
+
|
|
42151
|
+
${inputText}` : inputText;
|
|
42152
|
+
turnInput.push({ type: "text", text: composed, text_elements: [] });
|
|
42153
|
+
}
|
|
42154
|
+
if (imageDataUri) {
|
|
42155
|
+
turnInput.push({ type: "image", url: imageDataUri });
|
|
42156
|
+
}
|
|
42134
42157
|
await codexClient.turn.start({
|
|
42135
42158
|
threadId: thread.threadId,
|
|
42136
|
-
input:
|
|
42137
|
-
|
|
42138
|
-
${inputText}` : inputText }],
|
|
42159
|
+
input: turnInput,
|
|
42139
42160
|
...thread.reasoningEffort ? { effort: thread.reasoningEffort } : {}
|
|
42140
42161
|
});
|
|
42141
42162
|
await turnFinished;
|
|
@@ -42297,8 +42318,8 @@ async function startDirectorBridge() {
|
|
|
42297
42318
|
codexCwd: config2.codexCwd,
|
|
42298
42319
|
codexExecutable: config2.codexExecutable,
|
|
42299
42320
|
checkoutRegistry,
|
|
42300
|
-
worktreeRoot:
|
|
42301
|
-
locksDir:
|
|
42321
|
+
worktreeRoot: import_node_path11.default.join(config2.stateDir, "worktrees"),
|
|
42322
|
+
locksDir: import_node_path11.default.join(config2.stateDir, "locks"),
|
|
42302
42323
|
// Wire the profile-enforced executor server for code-execution runs. Each run gets its own
|
|
42303
42324
|
// sandboxed Codex instance (scrubbed env, scratch CODEX_HOME, deny-read, egress allowlist).
|
|
42304
42325
|
// The returned handle's dispose() shuts down the spawned app-server CHILD (manager.shutdown(),
|
|
@@ -42331,13 +42352,13 @@ async function startDirectorBridge() {
|
|
|
42331
42352
|
},
|
|
42332
42353
|
runVerificationTurn: createRunVerificationTurn({
|
|
42333
42354
|
startSandbox: async (egressDomains) => {
|
|
42334
|
-
const cwd =
|
|
42355
|
+
const cwd = import_node_fs10.default.mkdtempSync(import_node_path11.default.join(import_node_os7.default.tmpdir(), "mrrlin-verify-"));
|
|
42335
42356
|
let handle;
|
|
42336
42357
|
try {
|
|
42337
42358
|
handle = await startExecutorServer({ worktree: cwd, egressDomains });
|
|
42338
42359
|
} catch (error51) {
|
|
42339
42360
|
try {
|
|
42340
|
-
|
|
42361
|
+
import_node_fs10.default.rmSync(cwd, { recursive: true, force: true });
|
|
42341
42362
|
} catch {
|
|
42342
42363
|
}
|
|
42343
42364
|
throw error51;
|
|
@@ -42348,7 +42369,7 @@ async function startDirectorBridge() {
|
|
|
42348
42369
|
dispose: () => {
|
|
42349
42370
|
handle.dispose();
|
|
42350
42371
|
try {
|
|
42351
|
-
|
|
42372
|
+
import_node_fs10.default.rmSync(cwd, { recursive: true, force: true });
|
|
42352
42373
|
} catch {
|
|
42353
42374
|
}
|
|
42354
42375
|
}
|
|
@@ -42581,8 +42602,8 @@ async function startDirectorBridge() {
|
|
|
42581
42602
|
enqueueRun(
|
|
42582
42603
|
() => runOrphanWorktreeSweep(client, {
|
|
42583
42604
|
checkoutRegistry,
|
|
42584
|
-
worktreeRoot:
|
|
42585
|
-
locksDir:
|
|
42605
|
+
worktreeRoot: import_node_path11.default.join(config2.stateDir, "worktrees"),
|
|
42606
|
+
locksDir: import_node_path11.default.join(config2.stateDir, "locks")
|
|
42586
42607
|
})
|
|
42587
42608
|
);
|
|
42588
42609
|
scheduleClaimLoop();
|
|
@@ -42739,15 +42760,15 @@ function readDispatchBody(req) {
|
|
|
42739
42760
|
function readOrCreateBridgeToken(stateDir) {
|
|
42740
42761
|
const explicit = (process.env.MRRLIN_DIRECTOR_BRIDGE_TOKEN ?? "").trim();
|
|
42741
42762
|
if (explicit) return explicit;
|
|
42742
|
-
const tokenPath =
|
|
42763
|
+
const tokenPath = import_node_path11.default.join(stateDir, "token.txt");
|
|
42743
42764
|
try {
|
|
42744
|
-
const existing =
|
|
42765
|
+
const existing = import_node_fs10.default.readFileSync(tokenPath, "utf8").trim();
|
|
42745
42766
|
if (existing) return existing;
|
|
42746
42767
|
} catch {
|
|
42747
42768
|
}
|
|
42748
|
-
|
|
42769
|
+
import_node_fs10.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
42749
42770
|
const token = import_node_crypto4.default.randomBytes(32).toString("base64url");
|
|
42750
|
-
|
|
42771
|
+
import_node_fs10.default.writeFileSync(tokenPath, `${token}
|
|
42751
42772
|
`, { mode: 384 });
|
|
42752
42773
|
return token;
|
|
42753
42774
|
}
|
|
@@ -46130,14 +46151,17 @@ var StdioServerTransport = class {
|
|
|
46130
46151
|
|
|
46131
46152
|
// src/tools.ts
|
|
46132
46153
|
var import_promises3 = require("node:fs/promises");
|
|
46133
|
-
var
|
|
46154
|
+
var import_node_fs12 = require("node:fs");
|
|
46155
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
46156
|
+
var import_node_os8 = __toESM(require("node:os"), 1);
|
|
46157
|
+
var import_node_child_process6 = require("node:child_process");
|
|
46134
46158
|
|
|
46135
46159
|
// ../../packages/wiki/dist/index.js
|
|
46136
46160
|
var import_promises2 = __toESM(require("node:fs/promises"), 1);
|
|
46137
|
-
var
|
|
46161
|
+
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
46138
46162
|
var INCLUDED_DOC_FOLDERS = /* @__PURE__ */ new Set(["_meta", "explanation", "how-to", "reference", "sdd", "tutorials"]);
|
|
46139
46163
|
function toPosixPath(input) {
|
|
46140
|
-
return input.split(
|
|
46164
|
+
return input.split(import_node_path12.default.sep).join("/");
|
|
46141
46165
|
}
|
|
46142
46166
|
function slugifyWikiSegment(input) {
|
|
46143
46167
|
return input.trim().toLowerCase().replace(/`/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -46151,12 +46175,12 @@ async function pathExists2(input) {
|
|
|
46151
46175
|
}
|
|
46152
46176
|
}
|
|
46153
46177
|
async function findWorkspaceRoot(start = process.cwd()) {
|
|
46154
|
-
let current =
|
|
46178
|
+
let current = import_node_path12.default.resolve(start);
|
|
46155
46179
|
while (true) {
|
|
46156
|
-
if (await pathExists2(
|
|
46180
|
+
if (await pathExists2(import_node_path12.default.join(current, "pnpm-workspace.yaml")) && await pathExists2(import_node_path12.default.join(current, "docs"))) {
|
|
46157
46181
|
return current;
|
|
46158
46182
|
}
|
|
46159
|
-
const parent =
|
|
46183
|
+
const parent = import_node_path12.default.dirname(current);
|
|
46160
46184
|
if (parent === current) {
|
|
46161
46185
|
throw new Error(`Unable to find Mrrlin workspace root from ${start}`);
|
|
46162
46186
|
}
|
|
@@ -46165,28 +46189,28 @@ async function findWorkspaceRoot(start = process.cwd()) {
|
|
|
46165
46189
|
}
|
|
46166
46190
|
async function resolveDocsRoot(input) {
|
|
46167
46191
|
if (input) {
|
|
46168
|
-
return
|
|
46192
|
+
return import_node_path12.default.resolve(input);
|
|
46169
46193
|
}
|
|
46170
46194
|
if (process.env.MRRLIN_DOCS_ROOT) {
|
|
46171
|
-
return
|
|
46195
|
+
return import_node_path12.default.resolve(process.env.MRRLIN_DOCS_ROOT);
|
|
46172
46196
|
}
|
|
46173
|
-
return
|
|
46197
|
+
return import_node_path12.default.join(await findWorkspaceRoot(), "docs");
|
|
46174
46198
|
}
|
|
46175
46199
|
async function resolveIndexPath(input) {
|
|
46176
46200
|
if (input) {
|
|
46177
|
-
return
|
|
46201
|
+
return import_node_path12.default.resolve(input);
|
|
46178
46202
|
}
|
|
46179
46203
|
if (process.env.MRRLIN_WIKI_INDEX_PATH) {
|
|
46180
|
-
return
|
|
46204
|
+
return import_node_path12.default.resolve(process.env.MRRLIN_WIKI_INDEX_PATH);
|
|
46181
46205
|
}
|
|
46182
46206
|
const workspaceRoot = await findWorkspaceRoot();
|
|
46183
|
-
return
|
|
46207
|
+
return import_node_path12.default.join(workspaceRoot, "apps", "web", "public", "wiki-index.json");
|
|
46184
46208
|
}
|
|
46185
46209
|
async function walkMarkdownFiles(root, dir = root) {
|
|
46186
46210
|
const entries = await import_promises2.default.readdir(dir, { withFileTypes: true });
|
|
46187
46211
|
const files = [];
|
|
46188
46212
|
for (const entry of entries) {
|
|
46189
|
-
const absolute =
|
|
46213
|
+
const absolute = import_node_path12.default.join(dir, entry.name);
|
|
46190
46214
|
if (entry.isDirectory()) {
|
|
46191
46215
|
files.push(...await walkMarkdownFiles(root, absolute));
|
|
46192
46216
|
continue;
|
|
@@ -46205,7 +46229,7 @@ function parseTitle(markdown, sourcePath) {
|
|
|
46205
46229
|
return title;
|
|
46206
46230
|
}
|
|
46207
46231
|
function documentFromFile(docsRoot, absolutePath, markdown) {
|
|
46208
|
-
const relativePath = toPosixPath(
|
|
46232
|
+
const relativePath = toPosixPath(import_node_path12.default.relative(docsRoot, absolutePath));
|
|
46209
46233
|
const [section, fileName] = relativePath.split("/");
|
|
46210
46234
|
if (!section || !fileName || !INCLUDED_DOC_FOLDERS.has(section)) {
|
|
46211
46235
|
return null;
|
|
@@ -46620,7 +46644,7 @@ async function runCodeGate(_target, deps) {
|
|
|
46620
46644
|
}
|
|
46621
46645
|
|
|
46622
46646
|
// src/consensus/codex-exec.ts
|
|
46623
|
-
var
|
|
46647
|
+
var import_node_child_process4 = require("node:child_process");
|
|
46624
46648
|
function extractFinalMessage(stdout) {
|
|
46625
46649
|
let last = null;
|
|
46626
46650
|
for (const rawLine of stdout.split(/\r?\n/)) {
|
|
@@ -46665,7 +46689,7 @@ function firstString(...values) {
|
|
|
46665
46689
|
return null;
|
|
46666
46690
|
}
|
|
46667
46691
|
function runCodexExec(input, deps) {
|
|
46668
|
-
const spawn2 = deps?.spawn ??
|
|
46692
|
+
const spawn2 = deps?.spawn ?? import_node_child_process4.spawn;
|
|
46669
46693
|
const executable = input.codexExecutable ?? "codex";
|
|
46670
46694
|
const sandbox = input.sandbox ?? "read-only";
|
|
46671
46695
|
const fullPrompt = input.developerInstructions.trim() ? `${input.developerInstructions.trim()}
|
|
@@ -46737,12 +46761,12 @@ function errMessage(err) {
|
|
|
46737
46761
|
}
|
|
46738
46762
|
|
|
46739
46763
|
// src/consensus/wiring.ts
|
|
46740
|
-
var
|
|
46741
|
-
var
|
|
46764
|
+
var import_node_fs11 = __toESM(require("node:fs"), 1);
|
|
46765
|
+
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
46742
46766
|
var import_node_url = require("node:url");
|
|
46743
46767
|
|
|
46744
46768
|
// src/consensus/code-gate-git.ts
|
|
46745
|
-
var
|
|
46769
|
+
var import_node_child_process5 = require("node:child_process");
|
|
46746
46770
|
var DIFF_MAX_BYTES = 2e5;
|
|
46747
46771
|
var FASTGATE_OUTPUT_MAX_BYTES = 16e3;
|
|
46748
46772
|
var GIT_TIMEOUT_MS = 6e4;
|
|
@@ -46758,7 +46782,7 @@ var defaultRunCmd = (cmd, args, opts) => new Promise((resolve) => {
|
|
|
46758
46782
|
};
|
|
46759
46783
|
let child;
|
|
46760
46784
|
try {
|
|
46761
|
-
child = (0,
|
|
46785
|
+
child = (0, import_node_child_process5.spawn)(cmd, args, {
|
|
46762
46786
|
cwd: opts.cwd,
|
|
46763
46787
|
env: process.env,
|
|
46764
46788
|
shell: false,
|
|
@@ -46973,15 +46997,15 @@ ${res.stdout}${res.stderr}`);
|
|
|
46973
46997
|
var import_meta = {};
|
|
46974
46998
|
function getPersonasDir() {
|
|
46975
46999
|
if (typeof __dirname !== "undefined") {
|
|
46976
|
-
return
|
|
47000
|
+
return import_node_path13.default.join(__dirname, "consensus", "personas");
|
|
46977
47001
|
}
|
|
46978
|
-
return
|
|
47002
|
+
return import_node_path13.default.join(import_node_path13.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)), "personas");
|
|
46979
47003
|
}
|
|
46980
47004
|
var personaCache = /* @__PURE__ */ new Map();
|
|
46981
47005
|
function loadPersona(name) {
|
|
46982
47006
|
const cached2 = personaCache.get(name);
|
|
46983
47007
|
if (cached2 !== void 0) return cached2;
|
|
46984
|
-
const content =
|
|
47008
|
+
const content = import_node_fs11.default.readFileSync(import_node_path13.default.join(getPersonasDir(), `${name}.md`), "utf8");
|
|
46985
47009
|
personaCache.set(name, content);
|
|
46986
47010
|
return content;
|
|
46987
47011
|
}
|
|
@@ -47290,8 +47314,28 @@ async function enqueueAsyncTool(client, descriptor2, input) {
|
|
|
47290
47314
|
}
|
|
47291
47315
|
}
|
|
47292
47316
|
|
|
47293
|
-
// src/
|
|
47294
|
-
|
|
47317
|
+
// src/git-remote-match.ts
|
|
47318
|
+
function normalizeRemote(url2) {
|
|
47319
|
+
const trimmed = url2.trim().replace(/\.git$/i, "");
|
|
47320
|
+
const scp = /^[^@]+@[^:]+:(.+)$/.exec(trimmed);
|
|
47321
|
+
const pathPart = scp ? scp[1] : (() => {
|
|
47322
|
+
try {
|
|
47323
|
+
return new URL(trimmed).pathname.replace(/^\/+/, "");
|
|
47324
|
+
} catch {
|
|
47325
|
+
return null;
|
|
47326
|
+
}
|
|
47327
|
+
})();
|
|
47328
|
+
if (!pathPart) return null;
|
|
47329
|
+
const segs = pathPart.split("/").filter(Boolean);
|
|
47330
|
+
if (segs.length < 2) return null;
|
|
47331
|
+
return `${segs[segs.length - 2]}/${segs[segs.length - 1]}`.toLowerCase();
|
|
47332
|
+
}
|
|
47333
|
+
function remoteMatchesRepo(remoteUrl, repoFullName) {
|
|
47334
|
+
const a = normalizeRemote(remoteUrl);
|
|
47335
|
+
return a !== null && a === repoFullName.trim().toLowerCase();
|
|
47336
|
+
}
|
|
47337
|
+
|
|
47338
|
+
// src/tool-names.ts
|
|
47295
47339
|
var mcpToolNames = {
|
|
47296
47340
|
appendExecutionArtifact: "append_execution_artifact",
|
|
47297
47341
|
archivePlan: "archive_plan",
|
|
@@ -47327,7 +47371,6 @@ var mcpToolNames = {
|
|
|
47327
47371
|
listExecutionArtifacts: "list_execution_artifacts",
|
|
47328
47372
|
listExecutionRuns: "list_execution_runs",
|
|
47329
47373
|
listInboxItems: "list_inbox_items",
|
|
47330
|
-
listGithubWebhookDeliveries: "list_github_webhook_deliveries",
|
|
47331
47374
|
listGithubInstallations: "list_github_installations",
|
|
47332
47375
|
listGithubWorkflows: "list_github_workflows",
|
|
47333
47376
|
listPlans: "list_plans",
|
|
@@ -47350,8 +47393,500 @@ var mcpToolNames = {
|
|
|
47350
47393
|
runCodeReviewerGate: "run_code_reviewer_gate",
|
|
47351
47394
|
uploadArtifact: "upload_artifact",
|
|
47352
47395
|
listArtifactFiles: "list_artifact_files",
|
|
47353
|
-
resolveHandoff: "resolve_handoff"
|
|
47396
|
+
resolveHandoff: "resolve_handoff",
|
|
47397
|
+
registerLocalCheckout: "register_local_checkout",
|
|
47398
|
+
/** Compact-context retrieval: rehydrate a mrrlin:// handle into the canonical full record. */
|
|
47399
|
+
retrieveContext: "retrieve_context"
|
|
47400
|
+
};
|
|
47401
|
+
|
|
47402
|
+
// src/context-compact/policy.ts
|
|
47403
|
+
var DEFAULT_THRESHOLD_CHARS = 12e3;
|
|
47404
|
+
var DEFAULT_MAX_ITEMS = 25;
|
|
47405
|
+
function readPositiveInt(value, fallback) {
|
|
47406
|
+
const raw = (value ?? "").trim();
|
|
47407
|
+
if (!raw) return fallback;
|
|
47408
|
+
const parsed = Number.parseInt(raw, 10);
|
|
47409
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
47410
|
+
}
|
|
47411
|
+
function readCompactPolicy(env = process.env) {
|
|
47412
|
+
const disabled = (env.MRRLIN_MCP_COMPACT_DISABLE ?? "").trim() === "1";
|
|
47413
|
+
return {
|
|
47414
|
+
enabled: !disabled,
|
|
47415
|
+
thresholdChars: readPositiveInt(env.MRRLIN_MCP_COMPACT_THRESHOLD_CHARS, DEFAULT_THRESHOLD_CHARS),
|
|
47416
|
+
maxItems: readPositiveInt(env.MRRLIN_MCP_COMPACT_MAX_ITEMS, DEFAULT_MAX_ITEMS),
|
|
47417
|
+
debug: (env.MRRLIN_MCP_COMPACT_DEBUG ?? "").trim() === "1"
|
|
47418
|
+
};
|
|
47419
|
+
}
|
|
47420
|
+
|
|
47421
|
+
// src/context-compact/handles.ts
|
|
47422
|
+
var MRRLIN_HANDLE_SCHEME = "mrrlin://";
|
|
47423
|
+
var SLUG_RE = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
|
47424
|
+
var TASK_ID_RE = /^GT-[A-Za-z0-9-]+$/;
|
|
47425
|
+
var WIKI_ID_RE = /^WK-[A-Za-z0-9-]+$/;
|
|
47426
|
+
var RUN_ID_RE = /^ER-[A-Za-z0-9-]+$/;
|
|
47427
|
+
function isProjectSlug(value) {
|
|
47428
|
+
return SLUG_RE.test(value);
|
|
47429
|
+
}
|
|
47430
|
+
function formatTaskHandle(projectSlug, taskId) {
|
|
47431
|
+
return `${MRRLIN_HANDLE_SCHEME}task/${projectSlug}/${taskId}`;
|
|
47432
|
+
}
|
|
47433
|
+
function formatTaskEventsHandle(projectSlug, taskId) {
|
|
47434
|
+
return `${MRRLIN_HANDLE_SCHEME}task/${projectSlug}/${taskId}/events`;
|
|
47435
|
+
}
|
|
47436
|
+
function formatWikiHandle(projectSlug, pageId) {
|
|
47437
|
+
return `${MRRLIN_HANDLE_SCHEME}wiki/${projectSlug}/${pageId}`;
|
|
47438
|
+
}
|
|
47439
|
+
function formatProjectSnapshotHandle(projectSlug) {
|
|
47440
|
+
return `${MRRLIN_HANDLE_SCHEME}project/${projectSlug}/snapshot`;
|
|
47441
|
+
}
|
|
47442
|
+
function formatRunArtifactsHandle(projectSlug, runId) {
|
|
47443
|
+
return `${MRRLIN_HANDLE_SCHEME}run/${projectSlug}/${runId}/artifacts`;
|
|
47444
|
+
}
|
|
47445
|
+
function parseMrrlinHandle(value) {
|
|
47446
|
+
if (typeof value !== "string") return null;
|
|
47447
|
+
if (!value.startsWith(MRRLIN_HANDLE_SCHEME)) return null;
|
|
47448
|
+
const rest = value.slice(MRRLIN_HANDLE_SCHEME.length);
|
|
47449
|
+
if (!rest) return null;
|
|
47450
|
+
if (rest.includes("?") || rest.includes("#")) return null;
|
|
47451
|
+
const parts = rest.split("/");
|
|
47452
|
+
if (parts.length < 2) return null;
|
|
47453
|
+
const [kind, slug, ...tail2] = parts;
|
|
47454
|
+
if (!kind || !slug) return null;
|
|
47455
|
+
if (!isProjectSlug(slug)) return null;
|
|
47456
|
+
switch (kind) {
|
|
47457
|
+
case "task": {
|
|
47458
|
+
if (tail2.length < 1 || tail2.length > 2) return null;
|
|
47459
|
+
const taskId = tail2[0] ?? "";
|
|
47460
|
+
if (!TASK_ID_RE.test(taskId)) return null;
|
|
47461
|
+
if (tail2.length === 1) return { kind: "task", projectSlug: slug, taskId };
|
|
47462
|
+
if (tail2[1] !== "events") return null;
|
|
47463
|
+
return { kind: "task-events", projectSlug: slug, taskId };
|
|
47464
|
+
}
|
|
47465
|
+
case "wiki": {
|
|
47466
|
+
if (tail2.length !== 1) return null;
|
|
47467
|
+
const pageId = tail2[0] ?? "";
|
|
47468
|
+
if (!WIKI_ID_RE.test(pageId)) return null;
|
|
47469
|
+
return { kind: "wiki", projectSlug: slug, pageId };
|
|
47470
|
+
}
|
|
47471
|
+
case "project": {
|
|
47472
|
+
if (tail2.length !== 1) return null;
|
|
47473
|
+
const sub = tail2[0];
|
|
47474
|
+
if (sub === "snapshot") return { kind: "project-snapshot", projectSlug: slug };
|
|
47475
|
+
if (sub === "tasks") return { kind: "project-tasks", projectSlug: slug };
|
|
47476
|
+
return null;
|
|
47477
|
+
}
|
|
47478
|
+
case "run": {
|
|
47479
|
+
if (tail2.length < 1 || tail2.length > 2) return null;
|
|
47480
|
+
const runId = tail2[0] ?? "";
|
|
47481
|
+
if (!RUN_ID_RE.test(runId)) return null;
|
|
47482
|
+
if (tail2.length === 1) return { kind: "run", projectSlug: slug, runId };
|
|
47483
|
+
if (tail2[1] !== "artifacts") return null;
|
|
47484
|
+
return { kind: "run-artifacts", projectSlug: slug, runId };
|
|
47485
|
+
}
|
|
47486
|
+
default:
|
|
47487
|
+
return null;
|
|
47488
|
+
}
|
|
47489
|
+
}
|
|
47490
|
+
|
|
47491
|
+
// src/context-compact/compactors.ts
|
|
47492
|
+
var NOTES_EXCERPT_CHARS = 200;
|
|
47493
|
+
var TITLE_TRUNCATE_CHARS = 160;
|
|
47494
|
+
var EXCERPT_TRUNCATE_CHARS = 240;
|
|
47495
|
+
function truncate(value, maxChars) {
|
|
47496
|
+
if (value.length <= maxChars) return value;
|
|
47497
|
+
return `${value.slice(0, Math.max(0, maxChars - 1))}\u2026`;
|
|
47498
|
+
}
|
|
47499
|
+
function firstHeadingLine(markdown) {
|
|
47500
|
+
for (const line of markdown.split(/\r?\n/, 32)) {
|
|
47501
|
+
if (line.startsWith("#")) return line.replace(/\s+$/u, "");
|
|
47502
|
+
}
|
|
47503
|
+
return null;
|
|
47504
|
+
}
|
|
47505
|
+
function compactTaskList(projectSlug, tasks, maxItems) {
|
|
47506
|
+
const total = tasks.length;
|
|
47507
|
+
const included = Math.min(total, maxItems);
|
|
47508
|
+
const slice = tasks.slice(0, included);
|
|
47509
|
+
const items = slice.map((t) => ({
|
|
47510
|
+
id: t.id,
|
|
47511
|
+
title: truncate(t.title, TITLE_TRUNCATE_CHARS),
|
|
47512
|
+
status: t.status,
|
|
47513
|
+
type: t.type,
|
|
47514
|
+
category: t.category,
|
|
47515
|
+
subcategory: t.subcategory,
|
|
47516
|
+
impact: t.impact,
|
|
47517
|
+
autonomy: t.autonomyLevel,
|
|
47518
|
+
tags: t.tags,
|
|
47519
|
+
assignee: t.assignee,
|
|
47520
|
+
planId: t.planId,
|
|
47521
|
+
specWikiPageId: t.specWikiPageId,
|
|
47522
|
+
notesExcerpt: t.notes ? truncate(t.notes, NOTES_EXCERPT_CHARS) : null,
|
|
47523
|
+
latestJudgementExcerpt: t.latestJudgement ? truncate(t.latestJudgement, NOTES_EXCERPT_CHARS) : null,
|
|
47524
|
+
handle: formatTaskHandle(projectSlug, t.id)
|
|
47525
|
+
}));
|
|
47526
|
+
const handles = slice.map((t) => ({
|
|
47527
|
+
handle: formatTaskHandle(projectSlug, t.id),
|
|
47528
|
+
retrieveTool: mcpToolNames.getTask,
|
|
47529
|
+
retrieveArgs: { projectSlug, taskId: t.id }
|
|
47530
|
+
}));
|
|
47531
|
+
return {
|
|
47532
|
+
summary: `${total} task${total === 1 ? "" : "s"} in project ${projectSlug}; ${included} included in this compact view.`,
|
|
47533
|
+
items,
|
|
47534
|
+
handles,
|
|
47535
|
+
counts: { total, included, omitted: total - included }
|
|
47536
|
+
};
|
|
47537
|
+
}
|
|
47538
|
+
function compactWikiPageList(projectSlug, pages, maxItems) {
|
|
47539
|
+
const total = pages.length;
|
|
47540
|
+
const included = Math.min(total, maxItems);
|
|
47541
|
+
const slice = pages.slice(0, included);
|
|
47542
|
+
const items = slice.map((p) => ({
|
|
47543
|
+
id: p.id,
|
|
47544
|
+
title: truncate(p.title, TITLE_TRUNCATE_CHARS),
|
|
47545
|
+
folder: p.folder,
|
|
47546
|
+
path: p.path,
|
|
47547
|
+
taskId: p.taskId,
|
|
47548
|
+
specReady: p.specReady,
|
|
47549
|
+
firstHeading: firstHeadingLine(p.markdownBody),
|
|
47550
|
+
handle: formatWikiHandle(projectSlug, p.id)
|
|
47551
|
+
}));
|
|
47552
|
+
const handles = slice.map((p) => ({
|
|
47553
|
+
handle: formatWikiHandle(projectSlug, p.id),
|
|
47554
|
+
retrieveTool: mcpToolNames.getWikiPage,
|
|
47555
|
+
retrieveArgs: { projectSlug, pageId: p.id }
|
|
47556
|
+
}));
|
|
47557
|
+
return {
|
|
47558
|
+
summary: `${total} wiki page${total === 1 ? "" : "s"} in project ${projectSlug}; ${included} included in this compact view.`,
|
|
47559
|
+
items,
|
|
47560
|
+
handles,
|
|
47561
|
+
counts: { total, included, omitted: total - included }
|
|
47562
|
+
};
|
|
47563
|
+
}
|
|
47564
|
+
function compactWikiSearch(projectSlug, results, maxItems) {
|
|
47565
|
+
const total = results.length;
|
|
47566
|
+
const included = Math.min(total, maxItems);
|
|
47567
|
+
const slice = results.slice(0, included);
|
|
47568
|
+
const items = slice.map((r) => ({
|
|
47569
|
+
pageId: r.id,
|
|
47570
|
+
title: truncate(r.title, TITLE_TRUNCATE_CHARS),
|
|
47571
|
+
folder: r.folder,
|
|
47572
|
+
path: r.path,
|
|
47573
|
+
rank: r.rank,
|
|
47574
|
+
snippet: truncate(r.snippet, EXCERPT_TRUNCATE_CHARS),
|
|
47575
|
+
handle: formatWikiHandle(projectSlug, r.id)
|
|
47576
|
+
}));
|
|
47577
|
+
const handles = slice.map((r) => ({
|
|
47578
|
+
handle: formatWikiHandle(projectSlug, r.id),
|
|
47579
|
+
retrieveTool: mcpToolNames.getWikiPage,
|
|
47580
|
+
retrieveArgs: { projectSlug, pageId: r.id }
|
|
47581
|
+
}));
|
|
47582
|
+
return {
|
|
47583
|
+
summary: `${total} wiki search hit${total === 1 ? "" : "s"} in project ${projectSlug}; ${included} included.`,
|
|
47584
|
+
items,
|
|
47585
|
+
handles,
|
|
47586
|
+
counts: { total, included, omitted: total - included }
|
|
47587
|
+
};
|
|
47588
|
+
}
|
|
47589
|
+
function compactProjectSnapshot(projectSlug, snapshot, maxItems) {
|
|
47590
|
+
const taskBody = compactTaskList(projectSlug, snapshot.tasks, maxItems);
|
|
47591
|
+
const wikiBody = compactWikiPageList(projectSlug, snapshot.wikiPages, maxItems);
|
|
47592
|
+
const statusCounts = {};
|
|
47593
|
+
for (const t of snapshot.tasks) {
|
|
47594
|
+
statusCounts[t.status] = (statusCounts[t.status] ?? 0) + 1;
|
|
47595
|
+
}
|
|
47596
|
+
const categoryCounts = {};
|
|
47597
|
+
for (const t of snapshot.tasks) {
|
|
47598
|
+
const key = `${t.category}/${t.subcategory}`;
|
|
47599
|
+
categoryCounts[key] = (categoryCounts[key] ?? 0) + 1;
|
|
47600
|
+
}
|
|
47601
|
+
const compactPlans = snapshot.plans.map((p) => ({
|
|
47602
|
+
id: p.id,
|
|
47603
|
+
name: truncate(p.name, TITLE_TRUNCATE_CHARS),
|
|
47604
|
+
status: p.status,
|
|
47605
|
+
taskCount: p.taskCount,
|
|
47606
|
+
closedTaskCount: p.closedTaskCount,
|
|
47607
|
+
progress: p.progress,
|
|
47608
|
+
dueDate: p.dueDate
|
|
47609
|
+
}));
|
|
47610
|
+
const items = [
|
|
47611
|
+
{
|
|
47612
|
+
project: snapshot.project,
|
|
47613
|
+
github: {
|
|
47614
|
+
installationState: snapshot.github.appInstallationState,
|
|
47615
|
+
installationId: snapshot.github.installationId,
|
|
47616
|
+
operator: snapshot.github.operator
|
|
47617
|
+
},
|
|
47618
|
+
taskCount: snapshot.tasks.length,
|
|
47619
|
+
wikiCount: snapshot.wikiPages.length,
|
|
47620
|
+
planCount: snapshot.plans.length,
|
|
47621
|
+
statusCounts,
|
|
47622
|
+
categoryCounts,
|
|
47623
|
+
plans: compactPlans,
|
|
47624
|
+
tasks: taskBody.items,
|
|
47625
|
+
wikiPages: wikiBody.items
|
|
47626
|
+
}
|
|
47627
|
+
];
|
|
47628
|
+
const handles = [
|
|
47629
|
+
{
|
|
47630
|
+
handle: formatProjectSnapshotHandle(projectSlug),
|
|
47631
|
+
retrieveTool: mcpToolNames.getProjectSnapshot,
|
|
47632
|
+
retrieveArgs: { projectSlug }
|
|
47633
|
+
},
|
|
47634
|
+
...taskBody.handles,
|
|
47635
|
+
...wikiBody.handles
|
|
47636
|
+
];
|
|
47637
|
+
const total = snapshot.tasks.length + snapshot.wikiPages.length;
|
|
47638
|
+
const included = taskBody.counts.included + wikiBody.counts.included;
|
|
47639
|
+
return {
|
|
47640
|
+
summary: `Snapshot for ${projectSlug}: ${snapshot.tasks.length} tasks, ${snapshot.wikiPages.length} wiki pages, ${snapshot.plans.length} plans.`,
|
|
47641
|
+
items,
|
|
47642
|
+
handles,
|
|
47643
|
+
counts: { total, included, omitted: total - included }
|
|
47644
|
+
};
|
|
47645
|
+
}
|
|
47646
|
+
var ARTIFACT_PAYLOAD_BUDGET = 400;
|
|
47647
|
+
var COMMAND_OUTPUT_TAIL_LINES = 10;
|
|
47648
|
+
function compactArtifactPayload(artifact) {
|
|
47649
|
+
const payload = artifact.payload ?? {};
|
|
47650
|
+
switch (artifact.kind) {
|
|
47651
|
+
case "file": {
|
|
47652
|
+
return {
|
|
47653
|
+
filename: payload.filename ?? null,
|
|
47654
|
+
contentType: payload.contentType ?? null,
|
|
47655
|
+
sizeBytes: payload.sizeBytes ?? null,
|
|
47656
|
+
class: payload.class ?? null,
|
|
47657
|
+
stableUrl: payload.stableUrl ?? null,
|
|
47658
|
+
description: payload.description ?? null
|
|
47659
|
+
};
|
|
47660
|
+
}
|
|
47661
|
+
case "command_output": {
|
|
47662
|
+
const event = payload.event ?? void 0;
|
|
47663
|
+
const command = payload.command ?? event?.tool?.input?.command ?? null;
|
|
47664
|
+
const cwd = payload.cwd ?? event?.tool?.input?.cwd ?? null;
|
|
47665
|
+
const exitCode = payload.exitCode ?? null;
|
|
47666
|
+
const rawOutput = payload.stdout ?? payload.output ?? event?.content ?? "";
|
|
47667
|
+
const lines2 = rawOutput.split(/\r?\n/);
|
|
47668
|
+
const tail2 = lines2.slice(-COMMAND_OUTPUT_TAIL_LINES).join("\n");
|
|
47669
|
+
const firstError = lines2.find((l) => /error|fail/i.test(l)) ?? null;
|
|
47670
|
+
return {
|
|
47671
|
+
command,
|
|
47672
|
+
cwd,
|
|
47673
|
+
exitCode,
|
|
47674
|
+
firstError,
|
|
47675
|
+
tail: truncate(tail2, ARTIFACT_PAYLOAD_BUDGET),
|
|
47676
|
+
totalLines: lines2.length
|
|
47677
|
+
};
|
|
47678
|
+
}
|
|
47679
|
+
case "test_result": {
|
|
47680
|
+
return {
|
|
47681
|
+
status: payload.status ?? null,
|
|
47682
|
+
command: payload.command ?? null,
|
|
47683
|
+
summary: typeof payload.summary === "string" ? truncate(payload.summary, ARTIFACT_PAYLOAD_BUDGET) : null,
|
|
47684
|
+
failed: Array.isArray(payload.failedTests) ? payload.failedTests.slice(0, 25) : null
|
|
47685
|
+
};
|
|
47686
|
+
}
|
|
47687
|
+
case "diff_summary": {
|
|
47688
|
+
return {
|
|
47689
|
+
files: Array.isArray(payload.files) ? payload.files.slice(0, 50) : null,
|
|
47690
|
+
added: payload.added ?? null,
|
|
47691
|
+
removed: payload.removed ?? null,
|
|
47692
|
+
renamed: payload.renamed ?? null
|
|
47693
|
+
};
|
|
47694
|
+
}
|
|
47695
|
+
case "handoff_judgement":
|
|
47696
|
+
case "self_review":
|
|
47697
|
+
case "error":
|
|
47698
|
+
case "checkpoint":
|
|
47699
|
+
default: {
|
|
47700
|
+
const flattened = JSON.stringify(payload);
|
|
47701
|
+
return { excerpt: truncate(flattened, ARTIFACT_PAYLOAD_BUDGET) };
|
|
47702
|
+
}
|
|
47703
|
+
}
|
|
47704
|
+
}
|
|
47705
|
+
function compactExecutionArtifacts(projectSlug, artifacts, maxItems) {
|
|
47706
|
+
const total = artifacts.length;
|
|
47707
|
+
const included = Math.min(total, maxItems);
|
|
47708
|
+
const slice = total > included ? artifacts.slice(total - included) : artifacts;
|
|
47709
|
+
const items = slice.map((a) => ({
|
|
47710
|
+
id: a.id,
|
|
47711
|
+
runId: a.runId,
|
|
47712
|
+
kind: a.kind,
|
|
47713
|
+
createdAt: a.createdAt,
|
|
47714
|
+
payload: compactArtifactPayload(a)
|
|
47715
|
+
}));
|
|
47716
|
+
const runIds = Array.from(new Set(slice.map((a) => a.runId)));
|
|
47717
|
+
const handles = runIds.map((runId) => ({
|
|
47718
|
+
handle: formatRunArtifactsHandle(projectSlug, runId),
|
|
47719
|
+
retrieveTool: mcpToolNames.listExecutionArtifacts,
|
|
47720
|
+
retrieveArgs: { projectSlug, runId }
|
|
47721
|
+
}));
|
|
47722
|
+
return {
|
|
47723
|
+
summary: `${total} execution artifact${total === 1 ? "" : "s"}; ${included} included (most recent first dropped older).`,
|
|
47724
|
+
items,
|
|
47725
|
+
handles,
|
|
47726
|
+
counts: { total, included, omitted: total - included }
|
|
47727
|
+
};
|
|
47728
|
+
}
|
|
47729
|
+
function compactTaskEvents(projectSlug, taskId, events, maxItems) {
|
|
47730
|
+
const total = events.length;
|
|
47731
|
+
const included = Math.min(total, maxItems);
|
|
47732
|
+
const slice = total > included ? events.slice(total - included) : events;
|
|
47733
|
+
const items = slice.map((e) => ({
|
|
47734
|
+
id: e.id,
|
|
47735
|
+
eventType: e.eventType,
|
|
47736
|
+
actor: e.actor,
|
|
47737
|
+
createdAt: e.createdAt,
|
|
47738
|
+
autonomyLevel: e.autonomyLevel,
|
|
47739
|
+
autonomyReason: e.autonomyReason,
|
|
47740
|
+
bodyExcerpt: e.body ? truncate(e.body, NOTES_EXCERPT_CHARS) : null
|
|
47741
|
+
}));
|
|
47742
|
+
const handles = [
|
|
47743
|
+
{
|
|
47744
|
+
handle: formatTaskEventsHandle(projectSlug, taskId),
|
|
47745
|
+
retrieveTool: mcpToolNames.listTaskEvents,
|
|
47746
|
+
retrieveArgs: { projectSlug, taskId }
|
|
47747
|
+
}
|
|
47748
|
+
];
|
|
47749
|
+
return {
|
|
47750
|
+
summary: `${total} task event${total === 1 ? "" : "s"} for ${taskId}; ${included} most recent included.`,
|
|
47751
|
+
items,
|
|
47752
|
+
handles,
|
|
47753
|
+
counts: { total, included, omitted: total - included }
|
|
47754
|
+
};
|
|
47755
|
+
}
|
|
47756
|
+
function compactGeneric(value, maxItems, projectSlug) {
|
|
47757
|
+
if (Array.isArray(value)) {
|
|
47758
|
+
const total = value.length;
|
|
47759
|
+
const included = Math.min(total, maxItems);
|
|
47760
|
+
const slice = value.slice(0, included).map((entry) => {
|
|
47761
|
+
const text2 = typeof entry === "string" ? entry : JSON.stringify(entry);
|
|
47762
|
+
return truncate(text2, ARTIFACT_PAYLOAD_BUDGET);
|
|
47763
|
+
});
|
|
47764
|
+
return {
|
|
47765
|
+
summary: `Compact array view: ${total} entries, ${included} included.`,
|
|
47766
|
+
items: slice,
|
|
47767
|
+
handles: [],
|
|
47768
|
+
counts: { total, included, omitted: total - included }
|
|
47769
|
+
};
|
|
47770
|
+
}
|
|
47771
|
+
if (value && typeof value === "object") {
|
|
47772
|
+
const obj = value;
|
|
47773
|
+
const keys = Object.keys(obj);
|
|
47774
|
+
return {
|
|
47775
|
+
summary: `Compact object view (project=${projectSlug}): keys=[${keys.join(", ")}].`,
|
|
47776
|
+
items: [
|
|
47777
|
+
Object.fromEntries(
|
|
47778
|
+
keys.map((k) => {
|
|
47779
|
+
const entry = obj[k];
|
|
47780
|
+
if (Array.isArray(entry)) return [k, `[Array(${entry.length})]`];
|
|
47781
|
+
if (entry && typeof entry === "object") return [k, "[Object]"];
|
|
47782
|
+
return [k, entry];
|
|
47783
|
+
})
|
|
47784
|
+
)
|
|
47785
|
+
],
|
|
47786
|
+
handles: [],
|
|
47787
|
+
counts: { total: 1, included: 1, omitted: 0 }
|
|
47788
|
+
};
|
|
47789
|
+
}
|
|
47790
|
+
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
47791
|
+
return {
|
|
47792
|
+
summary: "Compact scalar view.",
|
|
47793
|
+
items: [truncate(text, ARTIFACT_PAYLOAD_BUDGET)],
|
|
47794
|
+
handles: [],
|
|
47795
|
+
counts: { total: 1, included: 1, omitted: 0 }
|
|
47796
|
+
};
|
|
47797
|
+
}
|
|
47798
|
+
|
|
47799
|
+
// src/context-compact/index.ts
|
|
47800
|
+
var COMPACT_WARNING = "Compact result. Source of truth lives in the Mrrlin API; call `retrieve_context` with one of the embedded mrrlin:// handles to load the full record before any irreversible decision.";
|
|
47801
|
+
function serializedChars(value) {
|
|
47802
|
+
return JSON.stringify({ data: value }, null, 2).length;
|
|
47803
|
+
}
|
|
47804
|
+
function buildEnvelope(dispatch, body, originalChars) {
|
|
47805
|
+
const partial2 = {
|
|
47806
|
+
compact: true,
|
|
47807
|
+
kind: dispatch.kind,
|
|
47808
|
+
toolName: dispatch.toolName,
|
|
47809
|
+
projectSlug: dispatch.projectSlug,
|
|
47810
|
+
summary: body.summary,
|
|
47811
|
+
counts: body.counts,
|
|
47812
|
+
items: body.items,
|
|
47813
|
+
handles: body.handles,
|
|
47814
|
+
warning: COMPACT_WARNING
|
|
47815
|
+
};
|
|
47816
|
+
const compactNoStats = JSON.stringify({ data: partial2 }, null, 2).length;
|
|
47817
|
+
const statsOverhead = 220;
|
|
47818
|
+
const compactChars = compactNoStats + statsOverhead;
|
|
47819
|
+
const savedChars = originalChars - compactChars;
|
|
47820
|
+
const savedRatio = originalChars > 0 ? Math.max(0, savedChars / originalChars) : 0;
|
|
47821
|
+
const envelope = {
|
|
47822
|
+
...partial2,
|
|
47823
|
+
stats: {
|
|
47824
|
+
originalChars,
|
|
47825
|
+
compactChars,
|
|
47826
|
+
savedChars,
|
|
47827
|
+
savedRatio: Number(savedRatio.toFixed(4))
|
|
47828
|
+
}
|
|
47829
|
+
};
|
|
47830
|
+
return { envelope, shrunk: compactChars < originalChars };
|
|
47831
|
+
}
|
|
47832
|
+
function runCompactor(dispatch, policy) {
|
|
47833
|
+
switch (dispatch.kind) {
|
|
47834
|
+
case "task-list":
|
|
47835
|
+
return compactTaskList(dispatch.projectSlug, dispatch.rawValue, policy.maxItems);
|
|
47836
|
+
case "wiki-list":
|
|
47837
|
+
return compactWikiPageList(dispatch.projectSlug, dispatch.rawValue, policy.maxItems);
|
|
47838
|
+
case "wiki-search":
|
|
47839
|
+
return compactWikiSearch(
|
|
47840
|
+
dispatch.projectSlug,
|
|
47841
|
+
dispatch.rawValue,
|
|
47842
|
+
policy.maxItems
|
|
47843
|
+
);
|
|
47844
|
+
case "project-snapshot":
|
|
47845
|
+
return compactProjectSnapshot(
|
|
47846
|
+
dispatch.projectSlug,
|
|
47847
|
+
dispatch.rawValue,
|
|
47848
|
+
policy.maxItems
|
|
47849
|
+
);
|
|
47850
|
+
case "execution-artifacts":
|
|
47851
|
+
return compactExecutionArtifacts(
|
|
47852
|
+
dispatch.projectSlug,
|
|
47853
|
+
dispatch.rawValue,
|
|
47854
|
+
policy.maxItems
|
|
47855
|
+
);
|
|
47856
|
+
case "task-events": {
|
|
47857
|
+
if (!dispatch.taskId) throw new Error("task-events compact requires taskId");
|
|
47858
|
+
return compactTaskEvents(dispatch.projectSlug, dispatch.taskId, dispatch.rawValue, policy.maxItems);
|
|
47859
|
+
}
|
|
47860
|
+
case "generic-json":
|
|
47861
|
+
return compactGeneric(dispatch.rawValue, policy.maxItems, dispatch.projectSlug);
|
|
47862
|
+
}
|
|
47863
|
+
}
|
|
47864
|
+
function maybeCompact(dispatch, policy = readCompactPolicy()) {
|
|
47865
|
+
if (!policy.enabled) return null;
|
|
47866
|
+
const originalChars = serializedChars(dispatch.rawValue);
|
|
47867
|
+
if (originalChars <= policy.thresholdChars) return null;
|
|
47868
|
+
const body = runCompactor(dispatch, policy);
|
|
47869
|
+
const result = buildEnvelope(dispatch, body, originalChars);
|
|
47870
|
+
if (policy.debug) {
|
|
47871
|
+
process.stderr.write(
|
|
47872
|
+
`[mrrlin-mcp compact] tool=${dispatch.toolName} kind=${dispatch.kind} originalChars=${originalChars} compactChars=${result.envelope.stats.compactChars} savedRatio=${result.envelope.stats.savedRatio}
|
|
47873
|
+
`
|
|
47874
|
+
);
|
|
47875
|
+
}
|
|
47876
|
+
if (!result.shrunk) return null;
|
|
47877
|
+
return result.envelope;
|
|
47878
|
+
}
|
|
47879
|
+
var COMPACT_OPT_IN = {
|
|
47880
|
+
[mcpToolNames.listTasks]: "task-list",
|
|
47881
|
+
[mcpToolNames.listWikiPages]: "wiki-list",
|
|
47882
|
+
[mcpToolNames.searchWikiPages]: "wiki-search",
|
|
47883
|
+
[mcpToolNames.getProjectSnapshot]: "project-snapshot",
|
|
47884
|
+
[mcpToolNames.listExecutionArtifacts]: "execution-artifacts",
|
|
47885
|
+
[mcpToolNames.listTaskEvents]: "task-events"
|
|
47354
47886
|
};
|
|
47887
|
+
|
|
47888
|
+
// src/tools.ts
|
|
47889
|
+
registerAsyncTool(consensusDescriptor);
|
|
47355
47890
|
var projectScopedSchema = external_exports.object({ projectSlug: mrrlinProjectSlugSchema });
|
|
47356
47891
|
var taskScopedSchema = projectScopedSchema.extend({ taskId: mrrlinTaskIdSchema });
|
|
47357
47892
|
var planScopedSchema = projectScopedSchema.extend({ planId: mrrlinPlanIdSchema });
|
|
@@ -47403,7 +47938,6 @@ var runCodeReviewerGateInputSchema = external_exports.object({
|
|
|
47403
47938
|
var searchKnowledgeInputSchema = wikiKnowledgeSearchInputSchema;
|
|
47404
47939
|
var createGithubBranchInputSchema = projectScopedSchema.merge(githubCreateBranchRequestSchema);
|
|
47405
47940
|
var createGithubPullRequestInputSchema = projectScopedSchema.merge(githubCreatePullRequestRequestSchema);
|
|
47406
|
-
var listGithubWebhookDeliveriesInputSchema = projectScopedSchema;
|
|
47407
47941
|
var listGithubWorkflowsInputSchema = projectScopedSchema;
|
|
47408
47942
|
var dispatchGithubWorkflowInputSchema = projectScopedSchema.extend({ workflowId: external_exports.string().min(1) }).merge(githubWorkflowDispatchRequestSchema);
|
|
47409
47943
|
var dispatchTaskDeployInputSchema = taskScopedSchema.extend({
|
|
@@ -47448,6 +47982,18 @@ var listInboxItemsInputSchema = projectScopedSchema.merge(mrrlinInboxItemListFil
|
|
|
47448
47982
|
var createInboxItemInputSchema = projectScopedSchema.merge(mrrlinInboxItemCreateSchema);
|
|
47449
47983
|
var decideInboxItemInputSchema = projectScopedSchema.extend({ itemId: mrrlinInboxItemIdSchema }).merge(mrrlinInboxItemDecideSchema);
|
|
47450
47984
|
var resolveHandoffInputSchema = projectScopedSchema.extend({ runId: mrrlinExecutionRunIdSchema }).merge(mrrlinResolveHandoffSchema);
|
|
47985
|
+
var registerLocalCheckoutInputSchema = projectScopedSchema.extend({
|
|
47986
|
+
path: external_exports.string().min(1).describe("Absolute path to the local git checkout (must be a git repo whose origin matches `repo`)."),
|
|
47987
|
+
repo: external_exports.string().min(1).describe("The project's bound GitHub repo full name in `owner/repo` form.")
|
|
47988
|
+
});
|
|
47989
|
+
var retrieveContextInputSchema = external_exports.object({
|
|
47990
|
+
handle: external_exports.string().min(1).describe(
|
|
47991
|
+
"A mrrlin:// handle emitted in a compact envelope. Examples: mrrlin://task/{slug}/GT-123, mrrlin://wiki/{slug}/WK-1, mrrlin://project/{slug}/snapshot, mrrlin://run/{slug}/ER-9, mrrlin://run/{slug}/ER-9/artifacts."
|
|
47992
|
+
),
|
|
47993
|
+
mode: external_exports.enum(["full", "summary", "metadata"]).optional().describe(
|
|
47994
|
+
"Reserved. v1 always returns the full record regardless; future versions will use this to scope rehydration."
|
|
47995
|
+
)
|
|
47996
|
+
});
|
|
47451
47997
|
var mcpToolInputSchemas = {
|
|
47452
47998
|
[mcpToolNames.appendExecutionArtifact]: appendExecutionArtifactInputSchema,
|
|
47453
47999
|
[mcpToolNames.archivePlan]: archivePlanInputSchema,
|
|
@@ -47480,7 +48026,6 @@ var mcpToolInputSchemas = {
|
|
|
47480
48026
|
[mcpToolNames.getGithubInstallationRepositories]: getGithubInstallationRepositoriesInputSchema,
|
|
47481
48027
|
[mcpToolNames.listExecutionArtifacts]: listExecutionArtifactsInputSchema,
|
|
47482
48028
|
[mcpToolNames.listExecutionRuns]: listExecutionRunsInputSchema,
|
|
47483
|
-
[mcpToolNames.listGithubWebhookDeliveries]: listGithubWebhookDeliveriesInputSchema,
|
|
47484
48029
|
[mcpToolNames.listGithubInstallations]: listGithubInstallationsInputSchema,
|
|
47485
48030
|
[mcpToolNames.listGithubWorkflows]: listGithubWorkflowsInputSchema,
|
|
47486
48031
|
[mcpToolNames.listPlans]: listPlansInputSchema,
|
|
@@ -47506,7 +48051,9 @@ var mcpToolInputSchemas = {
|
|
|
47506
48051
|
[mcpToolNames.decideInboxItem]: decideInboxItemInputSchema,
|
|
47507
48052
|
[mcpToolNames.uploadArtifact]: uploadArtifactInputSchema,
|
|
47508
48053
|
[mcpToolNames.listArtifactFiles]: listArtifactFilesInputSchema,
|
|
47509
|
-
[mcpToolNames.resolveHandoff]: resolveHandoffInputSchema
|
|
48054
|
+
[mcpToolNames.resolveHandoff]: resolveHandoffInputSchema,
|
|
48055
|
+
[mcpToolNames.registerLocalCheckout]: registerLocalCheckoutInputSchema,
|
|
48056
|
+
[mcpToolNames.retrieveContext]: retrieveContextInputSchema
|
|
47510
48057
|
};
|
|
47511
48058
|
var mcpToolOperationIds = {
|
|
47512
48059
|
[mcpToolNames.appendExecutionArtifact]: openApiOperationIds.appendExecutionArtifact,
|
|
@@ -47540,7 +48087,6 @@ var mcpToolOperationIds = {
|
|
|
47540
48087
|
[mcpToolNames.getGithubInstallationRepositories]: openApiOperationIds.getGithubInstallationRepositories,
|
|
47541
48088
|
[mcpToolNames.listExecutionArtifacts]: openApiOperationIds.listExecutionArtifacts,
|
|
47542
48089
|
[mcpToolNames.listExecutionRuns]: openApiOperationIds.listExecutionRuns,
|
|
47543
|
-
[mcpToolNames.listGithubWebhookDeliveries]: openApiOperationIds.listGithubWebhookDeliveries,
|
|
47544
48090
|
[mcpToolNames.listGithubInstallations]: openApiOperationIds.listGithubInstallations,
|
|
47545
48091
|
[mcpToolNames.listGithubWorkflows]: openApiOperationIds.listGithubWorkflows,
|
|
47546
48092
|
[mcpToolNames.listPlans]: openApiOperationIds.listPlans,
|
|
@@ -47590,7 +48136,6 @@ var mcpToolDescriptions = {
|
|
|
47590
48136
|
[mcpToolNames.dispatchGithubWorkflow]: "Dispatch a GitHub Actions workflow by workflow file/id against a ref visible to the linked installation.",
|
|
47591
48137
|
[mcpToolNames.dispatchTaskDeploy]: "Dispatch a task's dev or prod deploy; caller supplies workflowId chosen for this run (ADR 0012). Subject to auto_deploy policy and prod dev-verification gate.",
|
|
47592
48138
|
[mcpToolNames.getGithubInstallationRepositories]: "List repositories visible to a GitHub App installation id.",
|
|
47593
|
-
[mcpToolNames.listGithubWebhookDeliveries]: "List recent persisted GitHub webhook deliveries for the project so install/webhook smoke can be verified without raw DB access.",
|
|
47594
48139
|
[mcpToolNames.listGithubInstallations]: "List persisted GitHub App installations.",
|
|
47595
48140
|
[mcpToolNames.listGithubWorkflows]: "List the bound repository's GitHub Actions workflows live through the linked installation. No Mrrlin-side cache.",
|
|
47596
48141
|
[mcpToolNames.listProjects]: "List every project visible to the operator.",
|
|
@@ -47624,7 +48169,9 @@ var mcpToolDescriptions = {
|
|
|
47624
48169
|
[mcpToolNames.decideInboxItem]: "Approve, reject, or acknowledge an operator-inbox item. Rejection requires a reason.",
|
|
47625
48170
|
[mcpToolNames.uploadArtifact]: "Upload a local file (md/txt/html/csv/json/pdf/png/jpg/webp/gif, <=5MB) as a project artifact. Returns a stable markdown link to embed in chat/spec/handoff text. Use class=temp for one-off hand-offs, class=durable for anything referenced by specs or evidence. Pass runId to record the file in that run's artifact history; pass taskId to surface it on the task's Artifacts list.",
|
|
47626
48171
|
[mcpToolNames.listArtifactFiles]: "List uploaded artifact files for the project (optionally filtered by taskId/runId), newest first.",
|
|
47627
|
-
[mcpToolNames.resolveHandoff]: "Resolve a tool_assisted handoff for an execution run (outcome success|failure). Success returns the task to the board; failure opens a new handoff cycle. Idempotent."
|
|
48172
|
+
[mcpToolNames.resolveHandoff]: "Resolve a tool_assisted handoff for an execution run (outcome success|failure). Success returns the task to the board; failure opens a new handoff cycle. Idempotent.",
|
|
48173
|
+
[mcpToolNames.registerLocalCheckout]: "Register the operator-local git checkout path for `projectSlug` so execution-run workers run code on this machine instead of the cloud. Validates the path is a git repo whose `origin` remote matches `repo`, then writes the (slug -> absolute path) entry to the operator-local checkout registry. Call this after locating the checkout on disk (e.g. via filesystem search) and confirming the choice with the user in chat \u2014 they should NOT have to paste the path into Settings. Returns `{ projectSlug, path, confirmedAt }`. Idempotent: re-registering overwrites the prior entry.",
|
|
48174
|
+
[mcpToolNames.retrieveContext]: "Rehydrate a mrrlin:// handle emitted by the compact-context layer into the full canonical Mrrlin record (task/wiki page/project snapshot/execution run/artifacts). Use whenever you need the full source \u2014 irreversible decisions (closing tasks, dispatching deploys, writing specs) MUST call this first; never act on a compact envelope alone. Returns `{ kind, data }`. Invalid or unsupported handle \u2192 structured `HANDLE_INVALID` error."
|
|
47628
48175
|
};
|
|
47629
48176
|
var ARTIFACT_EXTENSION_TYPES = {
|
|
47630
48177
|
".md": "text/markdown",
|
|
@@ -47728,6 +48275,41 @@ function makeTool(client, name, fallbackCode, fallbackMessage, handler) {
|
|
|
47728
48275
|
}
|
|
47729
48276
|
};
|
|
47730
48277
|
}
|
|
48278
|
+
function structuredOkCompactEnvelope(envelope) {
|
|
48279
|
+
return {
|
|
48280
|
+
content: [{ text: JSON.stringify({ data: envelope }, null, 2), type: "text" }],
|
|
48281
|
+
isError: false,
|
|
48282
|
+
structuredContent: { data: envelope }
|
|
48283
|
+
};
|
|
48284
|
+
}
|
|
48285
|
+
function structuredOkMaybeCompact(name, projectSlug, value, context = {}) {
|
|
48286
|
+
const kind = COMPACT_OPT_IN[name];
|
|
48287
|
+
if (!kind) return structuredOk(value);
|
|
48288
|
+
const envelope = maybeCompact(
|
|
48289
|
+
{ toolName: name, projectSlug, kind, rawValue: value, taskId: context.taskId },
|
|
48290
|
+
readCompactPolicy()
|
|
48291
|
+
);
|
|
48292
|
+
if (!envelope) return structuredOk(value);
|
|
48293
|
+
return structuredOkCompactEnvelope(envelope);
|
|
48294
|
+
}
|
|
48295
|
+
function makeReadTool(client, name, fallbackCode, fallbackMessage, handler, projectSlugFrom, taskIdFrom) {
|
|
48296
|
+
return {
|
|
48297
|
+
description: mcpToolDescriptions[name],
|
|
48298
|
+
inputSchema: mcpToolInputSchemas[name],
|
|
48299
|
+
name,
|
|
48300
|
+
openApiOperationId: mcpToolOperationIds[name],
|
|
48301
|
+
async invoke(input) {
|
|
48302
|
+
try {
|
|
48303
|
+
const value = await handler(client, input);
|
|
48304
|
+
return structuredOkMaybeCompact(name, projectSlugFrom(input), value, {
|
|
48305
|
+
taskId: taskIdFrom ? taskIdFrom(input) : void 0
|
|
48306
|
+
});
|
|
48307
|
+
} catch (error51) {
|
|
48308
|
+
return structuredErr(classifyClientError(fallbackCode, fallbackMessage, error51));
|
|
48309
|
+
}
|
|
48310
|
+
}
|
|
48311
|
+
};
|
|
48312
|
+
}
|
|
47731
48313
|
function createMrrlinTools(options) {
|
|
47732
48314
|
const client = createMrrlinClient(withAuth(options));
|
|
47733
48315
|
return {
|
|
@@ -47864,12 +48446,13 @@ function createMrrlinTools(options) {
|
|
|
47864
48446
|
"Unable to read project.",
|
|
47865
48447
|
async (c, { projectSlug }) => await c.getProject(projectSlug)
|
|
47866
48448
|
),
|
|
47867
|
-
[mcpToolNames.getProjectSnapshot]:
|
|
48449
|
+
[mcpToolNames.getProjectSnapshot]: makeReadTool(
|
|
47868
48450
|
client,
|
|
47869
48451
|
mcpToolNames.getProjectSnapshot,
|
|
47870
48452
|
"PROJECT_SNAPSHOT_READ_FAILED",
|
|
47871
48453
|
"Unable to read project snapshot.",
|
|
47872
|
-
async (c, { projectSlug }) => await c.getProjectSnapshot(projectSlug)
|
|
48454
|
+
async (c, { projectSlug }) => await c.getProjectSnapshot(projectSlug),
|
|
48455
|
+
({ projectSlug }) => projectSlug
|
|
47873
48456
|
),
|
|
47874
48457
|
[mcpToolNames.getTask]: makeTool(
|
|
47875
48458
|
client,
|
|
@@ -47899,13 +48482,6 @@ function createMrrlinTools(options) {
|
|
|
47899
48482
|
"Unable to dispatch the task deploy workflow.",
|
|
47900
48483
|
async (c, { projectSlug, taskId, workflowId, ...rest }) => await c.dispatchTaskDeploy(projectSlug, taskId, { ...rest, workflowId })
|
|
47901
48484
|
),
|
|
47902
|
-
[mcpToolNames.listGithubWebhookDeliveries]: makeTool(
|
|
47903
|
-
client,
|
|
47904
|
-
mcpToolNames.listGithubWebhookDeliveries,
|
|
47905
|
-
"GITHUB_WEBHOOK_DELIVERY_LIST_FAILED",
|
|
47906
|
-
"Unable to list GitHub webhook deliveries.",
|
|
47907
|
-
async (c, { projectSlug }) => await c.listGithubWebhookDeliveries(projectSlug)
|
|
47908
|
-
),
|
|
47909
48485
|
[mcpToolNames.listGithubWorkflows]: makeTool(
|
|
47910
48486
|
client,
|
|
47911
48487
|
mcpToolNames.listGithubWorkflows,
|
|
@@ -47934,19 +48510,22 @@ function createMrrlinTools(options) {
|
|
|
47934
48510
|
"Unable to list task dependencies.",
|
|
47935
48511
|
async (c, { projectSlug }) => await c.listTaskDependencies(projectSlug)
|
|
47936
48512
|
),
|
|
47937
|
-
[mcpToolNames.listTaskEvents]:
|
|
48513
|
+
[mcpToolNames.listTaskEvents]: makeReadTool(
|
|
47938
48514
|
client,
|
|
47939
48515
|
mcpToolNames.listTaskEvents,
|
|
47940
48516
|
"TASK_EVENT_LIST_FAILED",
|
|
47941
48517
|
"Unable to list task events.",
|
|
47942
|
-
async (c, { projectSlug, taskId }) => await c.listTaskEvents(projectSlug, taskId)
|
|
48518
|
+
async (c, { projectSlug, taskId }) => await c.listTaskEvents(projectSlug, taskId),
|
|
48519
|
+
({ projectSlug }) => projectSlug,
|
|
48520
|
+
({ taskId }) => taskId
|
|
47943
48521
|
),
|
|
47944
|
-
[mcpToolNames.listTasks]:
|
|
48522
|
+
[mcpToolNames.listTasks]: makeReadTool(
|
|
47945
48523
|
client,
|
|
47946
48524
|
mcpToolNames.listTasks,
|
|
47947
48525
|
"TASK_LIST_FAILED",
|
|
47948
48526
|
"Unable to list tasks.",
|
|
47949
|
-
async (c, { projectSlug, ...filters }) => await c.listTasks(projectSlug, filters)
|
|
48527
|
+
async (c, { projectSlug, ...filters }) => await c.listTasks(projectSlug, filters),
|
|
48528
|
+
({ projectSlug }) => projectSlug
|
|
47950
48529
|
),
|
|
47951
48530
|
[mcpToolNames.listTaxonomy]: makeTool(
|
|
47952
48531
|
client,
|
|
@@ -47955,19 +48534,21 @@ function createMrrlinTools(options) {
|
|
|
47955
48534
|
"Unable to list taxonomy.",
|
|
47956
48535
|
async (c, { projectSlug }) => await c.listTaxonomy(projectSlug)
|
|
47957
48536
|
),
|
|
47958
|
-
[mcpToolNames.listWikiPages]:
|
|
48537
|
+
[mcpToolNames.listWikiPages]: makeReadTool(
|
|
47959
48538
|
client,
|
|
47960
48539
|
mcpToolNames.listWikiPages,
|
|
47961
48540
|
"WIKI_PAGE_LIST_FAILED",
|
|
47962
48541
|
"Unable to list wiki pages.",
|
|
47963
|
-
async (c, { projectSlug, ...filters }) => await c.listWikiPages(projectSlug, filters)
|
|
48542
|
+
async (c, { projectSlug, ...filters }) => await c.listWikiPages(projectSlug, filters),
|
|
48543
|
+
({ projectSlug }) => projectSlug
|
|
47964
48544
|
),
|
|
47965
|
-
[mcpToolNames.searchWikiPages]:
|
|
48545
|
+
[mcpToolNames.searchWikiPages]: makeReadTool(
|
|
47966
48546
|
client,
|
|
47967
48547
|
mcpToolNames.searchWikiPages,
|
|
47968
48548
|
"WIKI_PAGE_SEARCH_FAILED",
|
|
47969
48549
|
"Unable to search wiki pages.",
|
|
47970
|
-
async (c, { projectSlug, ...query }) => await c.searchWikiPages(projectSlug, query)
|
|
48550
|
+
async (c, { projectSlug, ...query }) => await c.searchWikiPages(projectSlug, query),
|
|
48551
|
+
({ projectSlug }) => projectSlug
|
|
47971
48552
|
),
|
|
47972
48553
|
[mcpToolNames.searchKnowledge]: makeTool(
|
|
47973
48554
|
client,
|
|
@@ -48114,12 +48695,13 @@ function createMrrlinTools(options) {
|
|
|
48114
48695
|
"Unable to update execution run.",
|
|
48115
48696
|
async (c, { projectSlug, runId, ...patch }) => await c.updateExecutionRun(projectSlug, runId, patch)
|
|
48116
48697
|
),
|
|
48117
|
-
[mcpToolNames.listExecutionArtifacts]:
|
|
48698
|
+
[mcpToolNames.listExecutionArtifacts]: makeReadTool(
|
|
48118
48699
|
client,
|
|
48119
48700
|
mcpToolNames.listExecutionArtifacts,
|
|
48120
48701
|
"EXECUTION_ARTIFACTS_LIST_FAILED",
|
|
48121
48702
|
"Unable to list execution artifacts.",
|
|
48122
|
-
async (c, { projectSlug, runId }) => await c.listExecutionArtifacts(projectSlug, runId)
|
|
48703
|
+
async (c, { projectSlug, runId }) => await c.listExecutionArtifacts(projectSlug, runId),
|
|
48704
|
+
({ projectSlug }) => projectSlug
|
|
48123
48705
|
),
|
|
48124
48706
|
[mcpToolNames.appendExecutionArtifact]: makeTool(
|
|
48125
48707
|
client,
|
|
@@ -48186,7 +48768,7 @@ function createMrrlinTools(options) {
|
|
|
48186
48768
|
"ARTIFACT_FILE_UPLOAD_FAILED",
|
|
48187
48769
|
"Unable to upload artifact file.",
|
|
48188
48770
|
async (c, { projectSlug, path: filePath, class: artifactClass, taskId, runId, description }) => {
|
|
48189
|
-
const extension2 =
|
|
48771
|
+
const extension2 = import_node_path14.default.extname(filePath).toLowerCase();
|
|
48190
48772
|
const contentType = ARTIFACT_EXTENSION_TYPES[extension2];
|
|
48191
48773
|
if (!contentType) {
|
|
48192
48774
|
throw new McpToolCodedError(
|
|
@@ -48214,7 +48796,7 @@ function createMrrlinTools(options) {
|
|
|
48214
48796
|
"Artifact exceeds 5MB; split or summarize instead."
|
|
48215
48797
|
);
|
|
48216
48798
|
}
|
|
48217
|
-
const filename =
|
|
48799
|
+
const filename = import_node_path14.default.basename(filePath);
|
|
48218
48800
|
const created = await c.createArtifactFile(projectSlug, {
|
|
48219
48801
|
class: artifactClass,
|
|
48220
48802
|
contentType,
|
|
@@ -48289,9 +48871,98 @@ function createMrrlinTools(options) {
|
|
|
48289
48871
|
"HANDOFF_RESOLVE_FAILED",
|
|
48290
48872
|
"Unable to resolve handoff.",
|
|
48291
48873
|
async (c, { projectSlug, runId, ...input }) => await c.resolveHandoff(projectSlug, runId, input)
|
|
48874
|
+
),
|
|
48875
|
+
[mcpToolNames.registerLocalCheckout]: makeTool(
|
|
48876
|
+
client,
|
|
48877
|
+
mcpToolNames.registerLocalCheckout,
|
|
48878
|
+
"LOCAL_CHECKOUT_REGISTER_FAILED",
|
|
48879
|
+
"Unable to register local checkout.",
|
|
48880
|
+
async (_c, { projectSlug, path: checkoutPath, repo }) => {
|
|
48881
|
+
const trimmedPath = checkoutPath.trim();
|
|
48882
|
+
if (!import_node_path14.default.isAbsolute(trimmedPath)) {
|
|
48883
|
+
throw new McpToolCodedError(
|
|
48884
|
+
"LOCAL_CHECKOUT_PATH_NOT_ABSOLUTE",
|
|
48885
|
+
`Path must be absolute, got: ${trimmedPath}`
|
|
48886
|
+
);
|
|
48887
|
+
}
|
|
48888
|
+
let resolvedPath;
|
|
48889
|
+
try {
|
|
48890
|
+
resolvedPath = (0, import_node_fs12.realpathSync)(trimmedPath);
|
|
48891
|
+
} catch {
|
|
48892
|
+
throw new McpToolCodedError(
|
|
48893
|
+
"LOCAL_CHECKOUT_PATH_NOT_FOUND",
|
|
48894
|
+
`Path does not exist or is not accessible: ${trimmedPath}`
|
|
48895
|
+
);
|
|
48896
|
+
}
|
|
48897
|
+
let remoteUrl;
|
|
48898
|
+
try {
|
|
48899
|
+
remoteUrl = (0, import_node_child_process6.execFileSync)("git", ["-C", resolvedPath, "remote", "get-url", "origin"], {
|
|
48900
|
+
encoding: "utf8",
|
|
48901
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
48902
|
+
timeout: 15e3,
|
|
48903
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
48904
|
+
}).trim();
|
|
48905
|
+
} catch {
|
|
48906
|
+
throw new McpToolCodedError(
|
|
48907
|
+
"LOCAL_CHECKOUT_NOT_A_GIT_REPO",
|
|
48908
|
+
`Path is not a git repository or has no origin remote: ${resolvedPath}`
|
|
48909
|
+
);
|
|
48910
|
+
}
|
|
48911
|
+
if (!remoteMatchesRepo(remoteUrl, repo)) {
|
|
48912
|
+
throw new McpToolCodedError(
|
|
48913
|
+
"LOCAL_CHECKOUT_REMOTE_MISMATCH",
|
|
48914
|
+
`Origin remote ${remoteUrl} does not match repo ${repo}.`
|
|
48915
|
+
);
|
|
48916
|
+
}
|
|
48917
|
+
const stateDir = process.env.CODEX_HOME ? import_node_path14.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge") : import_node_path14.default.join(import_node_os8.default.homedir(), ".mrrlin", "director-bridge");
|
|
48918
|
+
const registry3 = new CheckoutRegistry(stateDir);
|
|
48919
|
+
const confirmedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
48920
|
+
registry3.confirm(projectSlug, resolvedPath, confirmedAt);
|
|
48921
|
+
return { projectSlug, path: resolvedPath, confirmedAt };
|
|
48922
|
+
}
|
|
48923
|
+
),
|
|
48924
|
+
[mcpToolNames.retrieveContext]: makeTool(
|
|
48925
|
+
client,
|
|
48926
|
+
mcpToolNames.retrieveContext,
|
|
48927
|
+
"HANDLE_INVALID",
|
|
48928
|
+
"Unable to retrieve handle.",
|
|
48929
|
+
async (c, { handle }) => {
|
|
48930
|
+
const parsed = parseMrrlinHandle(handle);
|
|
48931
|
+
if (!parsed) {
|
|
48932
|
+
throw new McpToolCodedError(
|
|
48933
|
+
"HANDLE_INVALID",
|
|
48934
|
+
`Unrecognized mrrlin:// handle: ${handle}. Valid kinds: task | wiki | project/{slug}/snapshot | project/{slug}/tasks | run/{slug}/{runId} | run/{slug}/{runId}/artifacts.`
|
|
48935
|
+
);
|
|
48936
|
+
}
|
|
48937
|
+
return await dispatchHandle(c, parsed);
|
|
48938
|
+
}
|
|
48292
48939
|
)
|
|
48293
48940
|
};
|
|
48294
48941
|
}
|
|
48942
|
+
async function dispatchHandle(client, handle) {
|
|
48943
|
+
switch (handle.kind) {
|
|
48944
|
+
case "task":
|
|
48945
|
+
return { kind: handle.kind, data: await client.getTask(handle.projectSlug, handle.taskId) };
|
|
48946
|
+
case "task-events":
|
|
48947
|
+
return {
|
|
48948
|
+
kind: handle.kind,
|
|
48949
|
+
data: await client.listTaskEvents(handle.projectSlug, handle.taskId)
|
|
48950
|
+
};
|
|
48951
|
+
case "wiki":
|
|
48952
|
+
return { kind: handle.kind, data: await client.getWikiPage(handle.projectSlug, handle.pageId) };
|
|
48953
|
+
case "project-snapshot":
|
|
48954
|
+
return { kind: handle.kind, data: await client.getProjectSnapshot(handle.projectSlug) };
|
|
48955
|
+
case "project-tasks":
|
|
48956
|
+
return { kind: handle.kind, data: await client.listTasks(handle.projectSlug, {}) };
|
|
48957
|
+
case "run":
|
|
48958
|
+
return { kind: handle.kind, data: await client.getExecutionRun(handle.projectSlug, handle.runId) };
|
|
48959
|
+
case "run-artifacts":
|
|
48960
|
+
return {
|
|
48961
|
+
kind: handle.kind,
|
|
48962
|
+
data: await client.listExecutionArtifacts(handle.projectSlug, handle.runId)
|
|
48963
|
+
};
|
|
48964
|
+
}
|
|
48965
|
+
}
|
|
48295
48966
|
|
|
48296
48967
|
// src/server.ts
|
|
48297
48968
|
function redactUrlForLog(value) {
|
|
@@ -48450,13 +49121,13 @@ function runSetCredential(args) {
|
|
|
48450
49121
|
}
|
|
48451
49122
|
|
|
48452
49123
|
// src/install-service.ts
|
|
48453
|
-
var
|
|
48454
|
-
var
|
|
49124
|
+
var import_node_os9 = __toESM(require("node:os"), 1);
|
|
49125
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
48455
49126
|
|
|
48456
49127
|
// src/service-paths.ts
|
|
48457
|
-
var
|
|
49128
|
+
var import_node_path15 = __toESM(require("node:path"), 1);
|
|
48458
49129
|
function isWorktreePath(p) {
|
|
48459
|
-
const norm =
|
|
49130
|
+
const norm = import_node_path15.default.normalize(p).split(import_node_path15.default.sep).join("/");
|
|
48460
49131
|
return norm.includes("/.claude/worktrees/");
|
|
48461
49132
|
}
|
|
48462
49133
|
function resolveServiceCwd(opts) {
|
|
@@ -48509,7 +49180,7 @@ function installService(deps) {
|
|
|
48509
49180
|
deps.log(resolved.reason);
|
|
48510
49181
|
return { ok: false };
|
|
48511
49182
|
}
|
|
48512
|
-
const home = deps.env.HOME ??
|
|
49183
|
+
const home = deps.env.HOME ?? import_node_os9.default.homedir();
|
|
48513
49184
|
const bins = ["node", "codex", "mrrlin-mcp"].map((b) => deps.which(b));
|
|
48514
49185
|
if (bins.some((b) => !b)) {
|
|
48515
49186
|
deps.log("Could not resolve absolute paths for node/codex/mrrlin-mcp on PATH. Install them or fix PATH.");
|
|
@@ -48520,7 +49191,7 @@ function installService(deps) {
|
|
|
48520
49191
|
return { ok: false };
|
|
48521
49192
|
}
|
|
48522
49193
|
const resolvedBins = bins.filter((b) => b !== null);
|
|
48523
|
-
const pathEnv = Array.from(new Set(resolvedBins.map((b) =>
|
|
49194
|
+
const pathEnv = Array.from(new Set(resolvedBins.map((b) => import_node_path16.default.dirname(b)))).join(":");
|
|
48524
49195
|
const text = renderEcosystemConfig({
|
|
48525
49196
|
cwd: resolved.cwd,
|
|
48526
49197
|
home,
|
|
@@ -48529,7 +49200,7 @@ function installService(deps) {
|
|
|
48529
49200
|
staging: deps.env.MRRLIN_STAGING === "1",
|
|
48530
49201
|
apiBaseUrl: deps.env.MRRLIN_API_BASE_URL?.trim() || void 0
|
|
48531
49202
|
});
|
|
48532
|
-
const ecoPath =
|
|
49203
|
+
const ecoPath = import_node_path16.default.join(home, ".mrrlin", "ecosystem.config.cjs");
|
|
48533
49204
|
deps.writeFile(ecoPath, text);
|
|
48534
49205
|
const start = deps.runPm2(["startOrReload", ecoPath]);
|
|
48535
49206
|
if (start.code !== 0) {
|
|
@@ -48560,13 +49231,13 @@ function uninstallService(deps) {
|
|
|
48560
49231
|
|
|
48561
49232
|
// src/uninstall-codex.ts
|
|
48562
49233
|
var import_promises4 = __toESM(require("node:fs/promises"), 1);
|
|
48563
|
-
var
|
|
48564
|
-
var
|
|
49234
|
+
var import_node_os10 = __toESM(require("node:os"), 1);
|
|
49235
|
+
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
48565
49236
|
var toml2 = __toESM(require_toml(), 1);
|
|
48566
49237
|
function resolveCodexHome2(options) {
|
|
48567
49238
|
if (options.codexHome) return options.codexHome;
|
|
48568
49239
|
if (process.env.CODEX_HOME) return process.env.CODEX_HOME;
|
|
48569
|
-
return
|
|
49240
|
+
return import_node_path17.default.join(options.homeDir ?? import_node_os10.default.homedir(), ".codex");
|
|
48570
49241
|
}
|
|
48571
49242
|
async function pathExists3(target) {
|
|
48572
49243
|
try {
|
|
@@ -48579,7 +49250,7 @@ async function pathExists3(target) {
|
|
|
48579
49250
|
var MRRLIN_BLOCK_RE2 = /(^|\n)\[mcp_servers\.mrrlin(?:\]|\.[^\]\n]*\])[\s\S]*?(?=\n\[|$)/g;
|
|
48580
49251
|
async function uninstallCodex(options = {}) {
|
|
48581
49252
|
const codexHome = resolveCodexHome2(options);
|
|
48582
|
-
const configPath =
|
|
49253
|
+
const configPath = import_node_path17.default.join(codexHome, "config.toml");
|
|
48583
49254
|
const removePrompts = async () => uninstallPrompts(codexHome, options.promptNames ?? []);
|
|
48584
49255
|
if (!await pathExists3(configPath)) {
|
|
48585
49256
|
return { action: "missing", configPath, prompts: await removePrompts() };
|
|
@@ -48607,10 +49278,10 @@ async function uninstallCodex(options = {}) {
|
|
|
48607
49278
|
}
|
|
48608
49279
|
async function uninstallPrompts(codexHome, names) {
|
|
48609
49280
|
if (names.length === 0) return [];
|
|
48610
|
-
const promptsDir2 =
|
|
49281
|
+
const promptsDir2 = import_node_path17.default.join(codexHome, "prompts");
|
|
48611
49282
|
const out = [];
|
|
48612
49283
|
for (const name of names) {
|
|
48613
|
-
const promptPath =
|
|
49284
|
+
const promptPath = import_node_path17.default.join(promptsDir2, `${name}.md`);
|
|
48614
49285
|
try {
|
|
48615
49286
|
await import_promises4.default.unlink(promptPath);
|
|
48616
49287
|
out.push({ name, action: "removed", promptPath });
|
|
@@ -48628,15 +49299,15 @@ async function uninstallPrompts(codexHome, names) {
|
|
|
48628
49299
|
|
|
48629
49300
|
// src/report-issue-prompt.ts
|
|
48630
49301
|
var import_node_fs13 = __toESM(require("node:fs"), 1);
|
|
48631
|
-
var
|
|
49302
|
+
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
48632
49303
|
var import_node_url2 = require("node:url");
|
|
48633
49304
|
var import_meta2 = {};
|
|
48634
49305
|
function promptsDir() {
|
|
48635
|
-
if (typeof __dirname !== "undefined") return
|
|
48636
|
-
return
|
|
49306
|
+
if (typeof __dirname !== "undefined") return import_node_path18.default.join(__dirname, "prompts");
|
|
49307
|
+
return import_node_path18.default.join(import_node_path18.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url)), "prompts");
|
|
48637
49308
|
}
|
|
48638
49309
|
function readReportIssuePrompt() {
|
|
48639
|
-
return import_node_fs13.default.readFileSync(
|
|
49310
|
+
return import_node_fs13.default.readFileSync(import_node_path18.default.join(promptsDir(), "report-issue.md"), "utf8");
|
|
48640
49311
|
}
|
|
48641
49312
|
|
|
48642
49313
|
// src/bin.ts
|
|
@@ -48662,14 +49333,18 @@ Usage:
|
|
|
48662
49333
|
|
|
48663
49334
|
mrrlin-mcp install-codex Idempotently register Mrrlin in
|
|
48664
49335
|
[--force] ~/.codex/config.toml (or CODEX_HOME). Adds an
|
|
48665
|
-
|
|
49336
|
+
[--force-prompts] [mcp_servers.mrrlin] block AND drops the bundled
|
|
48666
49337
|
slash-command prompts (currently: /report-issue)
|
|
48667
49338
|
into <CODEX_HOME>/prompts/. Development
|
|
48668
49339
|
checkouts register the local dist/bin.cjs;
|
|
48669
49340
|
published npm installs register
|
|
48670
49341
|
\`mrrlin-mcp serve\`.
|
|
48671
|
-
--force replaces
|
|
48672
|
-
|
|
49342
|
+
--force replaces an existing conflicting block.
|
|
49343
|
+
--force-prompts overwrites prompt files that
|
|
49344
|
+
already exist with different content. WITHOUT it,
|
|
49345
|
+
local edits to <CODEX_HOME>/prompts/<name>.md are
|
|
49346
|
+
preserved across patch releases (reported as
|
|
49347
|
+
'skipped-modified').
|
|
48673
49348
|
|
|
48674
49349
|
mrrlin-mcp director-bridge Run a local ws://127.0.0.1 (plain HTTP +
|
|
48675
49350
|
WebSocket) bridge that lets the web Director
|
|
@@ -48747,6 +49422,14 @@ Usage:
|
|
|
48747
49422
|
binary) and the Settings credential revoke. Leaves
|
|
48748
49423
|
global pm2 alone (it may be used elsewhere).
|
|
48749
49424
|
|
|
49425
|
+
mrrlin-mcp redact Scrub known secret shapes (GitHub PATs,
|
|
49426
|
+
Bearer/JWT tokens, long hex / base64-ish runs)
|
|
49427
|
+
from stdin and write the result to stdout. Same
|
|
49428
|
+
regex set as the bridge logger. Empty input ->
|
|
49429
|
+
empty output, exit 0. Used by the /report-issue
|
|
49430
|
+
prompt to ensure no raw text reaches Telegram:
|
|
49431
|
+
printf %s "$HINT" | mrrlin-mcp redact
|
|
49432
|
+
|
|
48750
49433
|
mrrlin-mcp report-issue Print the bundled support-report prompt to
|
|
48751
49434
|
stdout. Normal users don't need this \u2014 install-codex
|
|
48752
49435
|
already drops it as a /report-issue slash command.
|
|
@@ -48785,10 +49468,12 @@ async function main() {
|
|
|
48785
49468
|
}
|
|
48786
49469
|
case "install-codex": {
|
|
48787
49470
|
const force = rest.includes("--force");
|
|
49471
|
+
const forcePrompts = rest.includes("--force-prompts");
|
|
48788
49472
|
const binPath = resolveSelfBinPath();
|
|
48789
49473
|
const result = await installCodex({
|
|
48790
49474
|
binPath,
|
|
48791
49475
|
force,
|
|
49476
|
+
forcePrompts,
|
|
48792
49477
|
prompts: [{ name: "report-issue", content: readReportIssuePrompt() }]
|
|
48793
49478
|
});
|
|
48794
49479
|
process.stderr.write(`[mrrlin-mcp install-codex] ${result.action} ${result.configPath}
|
|
@@ -48796,7 +49481,22 @@ async function main() {
|
|
|
48796
49481
|
for (const p of result.prompts) {
|
|
48797
49482
|
process.stderr.write(`[mrrlin-mcp install-codex] prompt ${p.name}: ${p.action} ${p.promptPath}
|
|
48798
49483
|
`);
|
|
49484
|
+
if (p.action === "skipped-modified") {
|
|
49485
|
+
process.stderr.write(
|
|
49486
|
+
`[mrrlin-mcp install-codex] prompt ${p.name}: kept your local edits; run with --force-prompts to overwrite (or delete ${p.promptPath} to take the new bundled version)
|
|
49487
|
+
`
|
|
49488
|
+
);
|
|
49489
|
+
}
|
|
49490
|
+
}
|
|
49491
|
+
return;
|
|
49492
|
+
}
|
|
49493
|
+
case "redact": {
|
|
49494
|
+
const chunks = [];
|
|
49495
|
+
for await (const chunk of process.stdin) {
|
|
49496
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
48799
49497
|
}
|
|
49498
|
+
const input = Buffer.concat(chunks).toString("utf8");
|
|
49499
|
+
process.stdout.write(redact(input));
|
|
48800
49500
|
return;
|
|
48801
49501
|
}
|
|
48802
49502
|
case "report-issue": {
|
|
@@ -48819,7 +49519,7 @@ async function main() {
|
|
|
48819
49519
|
env: process.env,
|
|
48820
49520
|
cwd: process.cwd(),
|
|
48821
49521
|
writeFile: (p, c) => {
|
|
48822
|
-
(0, import_node_fs14.mkdirSync)(
|
|
49522
|
+
(0, import_node_fs14.mkdirSync)(import_node_path20.default.dirname(p), { recursive: true, mode: 448 });
|
|
48823
49523
|
(0, import_node_fs14.writeFileSync)(p, c, { mode: 384 });
|
|
48824
49524
|
},
|
|
48825
49525
|
runPm2: pm2Runner,
|
|
@@ -48844,14 +49544,14 @@ async function main() {
|
|
|
48844
49544
|
purgeSecret: purge,
|
|
48845
49545
|
secretPath: agentCredentialPath(),
|
|
48846
49546
|
tokenPath: operatorTokenPath(),
|
|
48847
|
-
ecoPath:
|
|
49547
|
+
ecoPath: import_node_path20.default.join(process.env.HOME ?? (0, import_node_os11.homedir)(), ".mrrlin", "ecosystem.config.cjs"),
|
|
48848
49548
|
log: (m) => process.stderr.write(`[mrrlin-mcp uninstall-service] ${m}
|
|
48849
49549
|
`)
|
|
48850
49550
|
});
|
|
48851
49551
|
return;
|
|
48852
49552
|
}
|
|
48853
49553
|
case "uninstall": {
|
|
48854
|
-
const home = process.env.HOME ?? (0,
|
|
49554
|
+
const home = process.env.HOME ?? (0, import_node_os11.homedir)();
|
|
48855
49555
|
const log = (m) => process.stderr.write(`[mrrlin-mcp uninstall] ${m}
|
|
48856
49556
|
`);
|
|
48857
49557
|
uninstallService({
|
|
@@ -48865,7 +49565,7 @@ async function main() {
|
|
|
48865
49565
|
purgeSecret: true,
|
|
48866
49566
|
secretPath: agentCredentialPath(),
|
|
48867
49567
|
tokenPath: operatorTokenPath(),
|
|
48868
|
-
ecoPath:
|
|
49568
|
+
ecoPath: import_node_path20.default.join(home, ".mrrlin", "ecosystem.config.cjs"),
|
|
48869
49569
|
log
|
|
48870
49570
|
});
|
|
48871
49571
|
let codexOk = true;
|
|
@@ -48885,7 +49585,7 @@ async function main() {
|
|
|
48885
49585
|
log(`codex config NOT modified: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
48886
49586
|
}
|
|
48887
49587
|
try {
|
|
48888
|
-
(0, import_node_fs14.rmSync)(
|
|
49588
|
+
(0, import_node_fs14.rmSync)(import_node_path20.default.join(home, ".mrrlin"), { recursive: true, force: true });
|
|
48889
49589
|
} catch {
|
|
48890
49590
|
}
|
|
48891
49591
|
log("removed ~/.mrrlin");
|
|
@@ -48926,7 +49626,7 @@ ${HELP_TEXT}`);
|
|
|
48926
49626
|
}
|
|
48927
49627
|
}
|
|
48928
49628
|
function resolveSelfBinPath() {
|
|
48929
|
-
return
|
|
49629
|
+
return import_node_path19.default.resolve(process.argv[1] ?? process.execPath);
|
|
48930
49630
|
}
|
|
48931
49631
|
main().catch((error51) => {
|
|
48932
49632
|
process.stderr.write(`mrrlin-mcp fatal error: ${error51 instanceof Error ? error51.message : String(error51)}
|