@mrrlin-dev/mcp 0.3.10 → 0.3.12

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.
Files changed (2) hide show
  1. package/dist/bin.cjs +203 -233
  2. package/package.json +1 -1
package/dist/bin.cjs CHANGED
@@ -20644,11 +20644,11 @@ var require_dist = __commonJS({
20644
20644
  });
20645
20645
 
20646
20646
  // src/bin.ts
20647
- var import_node_path20 = __toESM(require("node:path"), 1);
20647
+ var import_node_path19 = __toESM(require("node:path"), 1);
20648
20648
  var import_node_child_process8 = require("node:child_process");
20649
- var import_node_fs15 = require("node:fs");
20650
- var import_node_os12 = require("node:os");
20651
- var import_node_path21 = __toESM(require("node:path"), 1);
20649
+ var import_node_fs14 = require("node:fs");
20650
+ var import_node_os11 = require("node:os");
20651
+ var import_node_path20 = __toESM(require("node:path"), 1);
20652
20652
 
20653
20653
  // src/install-codex.ts
20654
20654
  var import_promises = __toESM(require("node:fs/promises"), 1);
@@ -20933,10 +20933,10 @@ ${block}`;
20933
20933
  }
20934
20934
 
20935
20935
  // src/director-bridge.ts
20936
- var import_node_fs11 = __toESM(require("node:fs"), 1);
20936
+ var import_node_fs10 = __toESM(require("node:fs"), 1);
20937
20937
  var import_node_http = __toESM(require("node:http"), 1);
20938
- var import_node_os8 = __toESM(require("node:os"), 1);
20939
- var import_node_path12 = __toESM(require("node:path"), 1);
20938
+ var import_node_os7 = __toESM(require("node:os"), 1);
20939
+ var import_node_path11 = __toESM(require("node:path"), 1);
20940
20940
  var import_node_child_process4 = require("node:child_process");
20941
20941
  var import_node_crypto5 = __toESM(require("node:crypto"), 1);
20942
20942
 
@@ -21326,6 +21326,8 @@ var CodexServerManager = class {
21326
21326
  rpcClient.onServerRequest((id, method) => {
21327
21327
  if (method === "requestApproval") {
21328
21328
  rpcClient.respond(id, { decision: "accept" });
21329
+ } else if (method === "mcpServer/elicitation/request") {
21330
+ rpcClient.respond(id, { action: "accept" });
21329
21331
  }
21330
21332
  });
21331
21333
  const notificationHandlers = /* @__PURE__ */ new Set();
@@ -21398,6 +21400,8 @@ var CodexBridgeEventMapper = class {
21398
21400
  return this.handleOutputDelta(notification);
21399
21401
  case "codex/event/error":
21400
21402
  return this.handleServerError(notification);
21403
+ case "error":
21404
+ return this.handleStreamError(notification);
21401
21405
  default:
21402
21406
  return [];
21403
21407
  }
@@ -21490,9 +21494,10 @@ var CodexBridgeEventMapper = class {
21490
21494
  }
21491
21495
  handleTurnCompleted(notification) {
21492
21496
  const events = this.flushPendingText();
21493
- const status = notification.params?.status ?? "completed";
21497
+ const turn = notification.params?.turn;
21498
+ const status = (typeof turn?.status === "string" ? turn.status : void 0) ?? notification.params?.status ?? "completed";
21494
21499
  if (status === "failed") {
21495
- const error51 = notification.params?.error;
21500
+ const error51 = turn?.error ?? notification.params?.error;
21496
21501
  const content = typeof error51 === "string" ? error51 : typeof error51?.message === "string" ? String(error51.message) : "Unknown error";
21497
21502
  events.push({ type: "error", content });
21498
21503
  return events;
@@ -21504,6 +21509,18 @@ var CodexBridgeEventMapper = class {
21504
21509
  events.push({ type: "result", subtype: "success" });
21505
21510
  return events;
21506
21511
  }
21512
+ /** Bare `error` notifications carry model-stream failures ({ error: { message }, willRetry }).
21513
+ * Retryable ones (willRetry=true) are transport noise — codex is still driving the turn — so
21514
+ * only terminal failures surface as error events. */
21515
+ handleStreamError(notification) {
21516
+ if (notification.params?.willRetry === true)
21517
+ return [];
21518
+ const events = this.flushPendingText();
21519
+ const error51 = notification.params?.error;
21520
+ const content = (typeof error51?.message === "string" ? error51.message : void 0) ?? (typeof error51?.additionalDetails === "string" ? error51.additionalDetails : void 0) ?? "Unknown Codex error";
21521
+ events.push({ type: "error", content });
21522
+ return events;
21523
+ }
21507
21524
  handleServerError(notification) {
21508
21525
  const events = this.flushPendingText();
21509
21526
  const msg = notification.params?.msg;
@@ -37108,6 +37125,10 @@ var mrrlinExecutionArtifactCreateSchema = external_exports.object({
37108
37125
  kind: mrrlinExecutionArtifactKindSchema,
37109
37126
  payload: external_exports.record(external_exports.string(), external_exports.unknown()).default({})
37110
37127
  });
37128
+ var executionArtifactListFiltersSchema = external_exports.object({
37129
+ limit: external_exports.coerce.number().int().min(1).max(100).optional(),
37130
+ order: external_exports.enum(["asc", "desc"]).optional()
37131
+ });
37111
37132
  var executionRunResponseSchema = external_exports.object({
37112
37133
  data: mrrlinExecutionRunSchema
37113
37134
  });
@@ -37244,7 +37265,9 @@ var executionRunListFiltersSchema = external_exports.object({
37244
37265
  return value;
37245
37266
  }, external_exports.array(mrrlinExecutionRunStatusSchema)).optional()
37246
37267
  });
37268
+ var mrrlinAutoSpecApprovalSchema = external_exports.enum(["manual", "after_consensus"]);
37247
37269
  var mrrlinProjectSchedulerPolicySchema = external_exports.object({
37270
+ autoSpecApproval: mrrlinAutoSpecApprovalSchema,
37248
37271
  cadenceSeconds: external_exports.number().int().positive().nullable(),
37249
37272
  createdAt: external_exports.string().datetime(),
37250
37273
  eligibleAutonomyLevels: external_exports.array(mrrlinAutonomyLevelSchema).min(1),
@@ -37253,6 +37276,7 @@ var mrrlinProjectSchedulerPolicySchema = external_exports.object({
37253
37276
  wakeFloor: external_exports.string().datetime().nullable()
37254
37277
  });
37255
37278
  var mrrlinProjectSchedulerPolicyUpsertSchema = external_exports.object({
37279
+ autoSpecApproval: mrrlinAutoSpecApprovalSchema.optional(),
37256
37280
  cadenceSeconds: external_exports.number().int().positive().nullable().optional(),
37257
37281
  eligibleAutonomyLevels: external_exports.array(mrrlinAutonomyLevelSchema).min(1).optional(),
37258
37282
  wakeFloor: external_exports.string().datetime().nullable().optional()
@@ -37313,7 +37337,10 @@ var mrrlinInboxItemIdSchema = external_exports.string().regex(/^II-[A-Za-z0-9._-
37313
37337
  var mrrlinInboxItemActionPayloadSchema = external_exports.object({
37314
37338
  actor: external_exports.string().min(1),
37315
37339
  provider: external_exports.string().min(1),
37316
- runId: mrrlinExecutionRunIdSchema
37340
+ runId: mrrlinExecutionRunIdSchema,
37341
+ // Optional (additive): when true, approving the proposal creates a full code-execution run
37342
+ // (worktree + push/PR path). Scheduler proposals set it; legacy payloads omit it.
37343
+ codeExecution: external_exports.boolean().optional()
37317
37344
  });
37318
37345
  var mrrlinInboxItemSchema = external_exports.object({
37319
37346
  actionPayload: mrrlinInboxItemActionPayloadSchema.nullable(),
@@ -38067,8 +38094,12 @@ function createMrrlinClient(config2) {
38067
38094
  });
38068
38095
  return executionRunResponseSchema.parse(body).data;
38069
38096
  },
38070
- async listExecutionArtifacts(projectSlug, runId) {
38071
- const body = await request(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/artifacts`);
38097
+ async listExecutionArtifacts(projectSlug, runId, filters = {}) {
38098
+ const url2 = appendQuery(`${projectPath(projectSlug)}/execution-runs/${encodeURIComponent(runId)}/artifacts`, {
38099
+ limit: filters.limit !== void 0 ? String(filters.limit) : void 0,
38100
+ order: filters.order === "desc" ? "desc" : void 0
38101
+ });
38102
+ const body = await request(url2);
38072
38103
  return executionArtifactListResponseSchema.parse(body).data;
38073
38104
  },
38074
38105
  async appendExecutionArtifact(projectSlug, runId, input) {
@@ -38514,7 +38545,7 @@ Do NOT derail the current task \u2014 if the operator has an in-flight question,
38514
38545
  }
38515
38546
 
38516
38547
  // src/_generated/version.ts
38517
- var PKG_VERSION = "0.3.10";
38548
+ var PKG_VERSION = "0.3.12";
38518
38549
 
38519
38550
  // src/api-base-url.ts
38520
38551
  var DEFAULT_API_BASE_URL = "http://127.0.0.1:8787";
@@ -38994,10 +39025,12 @@ var CONSUMER_BASE_INSTRUCTIONS = [
38994
39025
  ].join("\n");
38995
39026
  var EXECUTOR_BASE_INSTRUCTIONS = [
38996
39027
  ...BASE_INSTRUCTIONS_PREFIX,
38997
- "You run inside an isolated, write-authorized git worktree: you MAY create, edit, and",
38998
- "delete files and run git commands within this worktree to make progress. Writes outside",
38999
- "the worktree and reads of operator secrets are blocked by the sandbox. Be concise. When",
39000
- "you have made the next increment of progress, stop the turn."
39028
+ "You run inside a git worktree dedicated to this run, with full read/write/network access to",
39029
+ "the host (there is NO filesystem sandbox). Do your work by creating, editing, and deleting",
39030
+ "files and running git/build/test commands WITHIN this worktree. Keep all changes inside the",
39031
+ "worktree and do not read the operator's personal secrets or files outside it \u2014 this is a rule",
39032
+ "you must follow, not something the environment enforces. Be concise. When you have made the",
39033
+ "next increment of progress, stop the turn."
39001
39034
  ].join("\n");
39002
39035
  function artifactKindForEvent(event) {
39003
39036
  switch (event.type) {
@@ -39117,12 +39150,25 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
39117
39150
  };
39118
39151
  return deps.runExclusive(async () => {
39119
39152
  const pending = [];
39153
+ let resolveTurnEnded = null;
39154
+ const turnEnded = new Promise((resolve) => {
39155
+ resolveTurnEnded = resolve;
39156
+ });
39157
+ let acceptedTurnId = null;
39120
39158
  const unsub = codex.onNotification((method, params) => {
39121
39159
  const events = mapper.mapNotification(
39122
39160
  { method, params },
39123
39161
  threadId
39124
39162
  );
39125
39163
  for (const event of events) pending.push(persist(event));
39164
+ if (method === "turn/completed") {
39165
+ const p = params;
39166
+ const notifThreadId = p?.threadId;
39167
+ const notifTurnId = p?.turn?.id;
39168
+ const threadMatches = !notifThreadId || notifThreadId === threadId;
39169
+ const turnMatches = !acceptedTurnId || !notifTurnId || notifTurnId === acceptedTurnId;
39170
+ if (threadMatches && turnMatches) resolveTurnEnded?.();
39171
+ }
39126
39172
  });
39127
39173
  const touchTimer = setInterval(() => {
39128
39174
  void deps.client.touchExecutionRun(projectSlug, runId, leaseId).catch(() => {
@@ -39132,7 +39178,14 @@ async function driveTurn(deps, codex, projectSlug, runId, leaseId, threadId, inp
39132
39178
  try {
39133
39179
  const timeoutMs = execTurnTimeoutMs();
39134
39180
  await awaitTurnWithDeadline({
39135
- run: () => codex.turn.start({ threadId, input: [{ type: "text", text: inputText }] }),
39181
+ run: async () => {
39182
+ const accepted = await codex.turn.start({
39183
+ threadId,
39184
+ input: [{ type: "text", text: inputText }]
39185
+ });
39186
+ acceptedTurnId = accepted?.turn?.id ?? null;
39187
+ await turnEnded;
39188
+ },
39136
39189
  interrupt: () => codex.turn.interrupt({ threadId }),
39137
39190
  timeoutMs,
39138
39191
  timeoutMessage: `Execution turn did not complete within ${timeoutMs}ms.`,
@@ -39202,8 +39255,11 @@ async function driveRun(deps, projectSlug, runId) {
39202
39255
  runId,
39203
39256
  defaultBranch: await getDefaultBranch(projectSlug)
39204
39257
  });
39205
- await deps.client.updateExecutionRun(projectSlug, runId, { worktreeId: worktreePath });
39206
- sandbox = null;
39258
+ await deps.client.updateExecutionRun(projectSlug, runId, {
39259
+ worktreeId: worktreePath,
39260
+ branch: worktreeBranch(runId)
39261
+ });
39262
+ sandbox = "danger-full-access";
39207
39263
  turnCwd = worktreePath;
39208
39264
  useExecutor = true;
39209
39265
  if (deps.acquireExecutorCodex) {
@@ -43687,127 +43743,13 @@ var CheckoutRegistry = class {
43687
43743
  };
43688
43744
 
43689
43745
  // src/executor-server.ts
43690
- var import_node_fs9 = require("node:fs");
43691
- var import_node_os7 = require("node:os");
43692
- var import_node_path10 = require("node:path");
43693
- var DENY_READ_GLOBS = [
43694
- "**/.ssh/**",
43695
- "**/.aws/**",
43696
- "**/.config/gh/**",
43697
- "**/.netrc",
43698
- "**/.npmrc",
43699
- "**/.env*",
43700
- "**/.gnupg/**"
43701
- ];
43702
- var ENV_ALLOWLIST = ["PATH", "LANG", "LC_ALL", "TERM", "TMPDIR", "GIT_SSH_COMMAND"];
43703
- var DEFAULT_EGRESS_DOMAINS = [
43704
- "registry.npmjs.org",
43705
- "github.com",
43706
- "api.github.com",
43707
- "codeload.github.com"
43708
- ];
43709
- function buildExecutorConfig(opts) {
43710
- const env = { HOME: opts.scratchHome };
43711
- for (const k of ENV_ALLOWLIST) {
43712
- if (process.env[k]) env[k] = process.env[k];
43713
- }
43714
- return {
43715
- cwd: opts.worktree,
43716
- env,
43717
- denyReadGlobs: DENY_READ_GLOBS,
43718
- denyReadResolvesSymlinks: true,
43719
- network: { allowedDomains: opts.egressDomains },
43720
- trustWorktreeConfig: false
43721
- };
43722
- }
43723
- function operatorSecretPaths(realHome = (0, import_node_os7.homedir)()) {
43724
- return [
43725
- (0, import_node_path10.join)(realHome, ".ssh"),
43726
- (0, import_node_path10.join)(realHome, ".aws"),
43727
- (0, import_node_path10.join)(realHome, ".config", "gh"),
43728
- (0, import_node_path10.join)(realHome, ".npmrc"),
43729
- (0, import_node_path10.join)(realHome, ".netrc"),
43730
- (0, import_node_path10.join)(realHome, ".gnupg"),
43731
- (0, import_node_path10.join)(realHome, ".kube"),
43732
- (0, import_node_path10.join)(realHome, ".docker", "config.json"),
43733
- (0, import_node_path10.join)(realHome, ".config", "gcloud")
43734
- ];
43735
- }
43736
- var SECRET_DIRECTORY_BASENAMES = /* @__PURE__ */ new Set([".ssh", ".aws", "gh", ".gnupg", ".kube", "gcloud"]);
43737
- function isSecretDirectoryPath(p) {
43738
- const base = p.split(/[/\\]/).filter(Boolean).pop() ?? "";
43739
- return SECRET_DIRECTORY_BASENAMES.has(base);
43740
- }
43741
- function writeExecutorCodexHome(scratchDir, opts) {
43742
- const codexHome = (0, import_node_path10.join)(scratchDir, ".codex");
43743
- (0, import_node_fs9.mkdirSync)(codexHome, { recursive: true });
43744
- const hasEgress = opts.egressDomains.length > 0;
43745
- const networkSection = hasEgress ? [
43746
- "[permissions.executor.network]",
43747
- "enabled = true",
43748
- 'mode = "limited"',
43749
- "",
43750
- "[permissions.executor.network.domains]",
43751
- ...opts.egressDomains.map((d) => `"${d}" = "allow"`)
43752
- ].join("\n") : [
43753
- "[permissions.executor.network]",
43754
- "enabled = false"
43755
- ].join("\n");
43756
- const absoluteDenyLines = opts.denySecretPaths.flatMap(
43757
- (p) => isSecretDirectoryPath(p) ? [`"${p}" = "deny"`, `"${p}/**" = "deny"`] : [`"${p}" = "deny"`]
43758
- ).join("\n");
43759
- const workspaceRootsDenyLines = [
43760
- '[permissions.executor.filesystem.":workspace_roots"]',
43761
- '"**/.env*" = "deny"'
43762
- ].join("\n");
43763
- const toml3 = [
43764
- 'default_permissions = "executor"',
43765
- "",
43766
- "[permissions.executor]",
43767
- 'extends = ":workspace"',
43768
- "",
43769
- "[permissions.executor.filesystem]",
43770
- "glob_scan_max_depth = 8",
43771
- absoluteDenyLines,
43772
- "",
43773
- workspaceRootsDenyLines,
43774
- "",
43775
- networkSection,
43776
- ""
43777
- ].join("\n");
43778
- (0, import_node_fs9.writeFileSync)((0, import_node_path10.join)(codexHome, "config.toml"), toml3, "utf8");
43779
- return codexHome;
43780
- }
43781
43746
  async function startExecutorServer(opts) {
43782
- const egressDomains = opts.egressDomains ?? DEFAULT_EGRESS_DOMAINS;
43783
- const realHome = (0, import_node_os7.homedir)();
43784
- const denySecretPaths = operatorSecretPaths(realHome);
43785
- const scratchDir = (0, import_node_fs9.mkdtempSync)((0, import_node_path10.join)((0, import_node_os7.tmpdir)(), "mrrlin-executor-"));
43786
- const scratchHome = scratchDir;
43787
- let scratchCleaned = false;
43788
- const cleanScratch = () => {
43789
- if (scratchCleaned) return;
43790
- scratchCleaned = true;
43791
- try {
43792
- (0, import_node_fs9.rmSync)(scratchDir, { recursive: true, force: true });
43793
- } catch {
43794
- }
43795
- };
43796
43747
  const manager = new CodexServerManager();
43797
43748
  try {
43798
- const codexHome = writeExecutorCodexHome(scratchDir, {
43799
- worktree: opts.worktree,
43800
- egressDomains,
43801
- denySecretPaths
43802
- });
43803
- const cfg = buildExecutorConfig({ worktree: opts.worktree, scratchHome, egressDomains });
43804
- const env = { ...cfg.env, CODEX_HOME: codexHome };
43805
43749
  const client = await manager.acquire({
43806
- cwd: cfg.cwd,
43807
- env,
43750
+ cwd: opts.worktree,
43808
43751
  approvalPolicy: "never",
43809
- ...opts.spawn ? { spawn: opts.spawn } : {},
43810
- ...opts.sandbox != null ? { sandbox: opts.sandbox } : {}
43752
+ ...opts.spawn ? { spawn: opts.spawn } : {}
43811
43753
  });
43812
43754
  let disposed = false;
43813
43755
  const dispose = () => {
@@ -43817,7 +43759,6 @@ async function startExecutorServer(opts) {
43817
43759
  manager.shutdown();
43818
43760
  } catch {
43819
43761
  }
43820
- cleanScratch();
43821
43762
  };
43822
43763
  return { client, dispose };
43823
43764
  } catch (error51) {
@@ -43825,7 +43766,6 @@ async function startExecutorServer(opts) {
43825
43766
  manager.shutdown();
43826
43767
  } catch {
43827
43768
  }
43828
- cleanScratch();
43829
43769
  throw error51;
43830
43770
  }
43831
43771
  }
@@ -43835,8 +43775,10 @@ function buildVerificationPrompt(input) {
43835
43775
  return [
43836
43776
  `You are verifying a deployment. The application is live at: ${input.environmentUrl}`,
43837
43777
  `Run ONLY read-only HTTP checks against that URL to evaluate the verification steps below.`,
43838
- `You have NO repository, filesystem, or credential access and you must not attempt to mutate`,
43839
- `anything \u2014 only observe the deployed app over HTTP.`,
43778
+ `Behave as if you had no repository, filesystem, or credential access: do not read or write`,
43779
+ `files, do not use any credentials, do not contact any other host, and do not attempt to`,
43780
+ `mutate anything \u2014 only observe the deployed app over HTTP. (This is a rule you must follow,`,
43781
+ `not something the environment enforces.)`,
43840
43782
  ``,
43841
43783
  `Verification steps for environment "${input.env}":`,
43842
43784
  input.verificationSection,
@@ -43980,8 +43922,8 @@ function extractVerificationSection(markdown) {
43980
43922
  }
43981
43923
 
43982
43924
  // src/bridge-log.ts
43983
- var import_node_fs10 = require("node:fs");
43984
- var import_node_path11 = require("node:path");
43925
+ var import_node_fs9 = require("node:fs");
43926
+ var import_node_path10 = require("node:path");
43985
43927
 
43986
43928
  // src/redact.ts
43987
43929
  var REDACTED = "[REDACTED]";
@@ -44066,14 +44008,14 @@ function resolvePromptsEnabled(env) {
44066
44008
  }
44067
44009
  function createBridgeLogger(stateDir, env = process.env) {
44068
44010
  if (!resolveBridgeLogEnabled(env)) return NOOP_LOGGER;
44069
- const logsDir = (0, import_node_path11.join)(stateDir, "logs");
44011
+ const logsDir = (0, import_node_path10.join)(stateDir, "logs");
44070
44012
  try {
44071
- (0, import_node_fs10.mkdirSync)(logsDir, { recursive: true });
44013
+ (0, import_node_fs9.mkdirSync)(logsDir, { recursive: true });
44072
44014
  } catch {
44073
44015
  return NOOP_LOGGER;
44074
44016
  }
44075
44017
  const includePrompts = resolvePromptsEnabled(env);
44076
- const fileFor = () => (0, import_node_path11.join)(logsDir, `bridge-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.jsonl`);
44018
+ const fileFor = () => (0, import_node_path10.join)(logsDir, `bridge-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.jsonl`);
44077
44019
  const write = (dir, rec) => {
44078
44020
  try {
44079
44021
  const line = JSON.stringify({
@@ -44085,7 +44027,7 @@ function createBridgeLogger(stateDir, env = process.env) {
44085
44027
  ...rec.ms !== void 0 ? { ms: rec.ms } : {},
44086
44028
  payload: redactMessage(rec.payload, { includePrompts })
44087
44029
  }) + "\n";
44088
- (0, import_node_fs10.appendFileSync)(fileFor(), line);
44030
+ (0, import_node_fs9.appendFileSync)(fileFor(), line);
44089
44031
  } catch {
44090
44032
  }
44091
44033
  };
@@ -44119,9 +44061,9 @@ function normalizeModel(value) {
44119
44061
  return trimmed ? trimmed : null;
44120
44062
  }
44121
44063
  function neutralCheckoutCwd(stateDir) {
44122
- const dir = import_node_path12.default.join(stateDir, "no-checkout");
44064
+ const dir = import_node_path11.default.join(stateDir, "no-checkout");
44123
44065
  try {
44124
- import_node_fs11.default.mkdirSync(dir, { recursive: true });
44066
+ import_node_fs10.default.mkdirSync(dir, { recursive: true });
44125
44067
  } catch {
44126
44068
  }
44127
44069
  return dir;
@@ -44278,10 +44220,11 @@ function tokenRefreshIntervalMs() {
44278
44220
  var COLLECTED_TURN_TIMEOUT_MS = 3e5;
44279
44221
  var DEFAULT_IMAGE_ONLY_PROMPT = "Look at the attached image.";
44280
44222
  var VERIFICATION_BASE_INSTRUCTIONS = [
44281
- "You are a deployment verification agent. You may ONLY make read-only HTTP requests to the",
44282
- "single allowlisted environment URL to confirm a deploy succeeded. You have NO repository,",
44283
- "filesystem, or credential access and must not attempt to modify anything. Follow the",
44284
- "verification steps and end with a single VERDICT line exactly as instructed."
44223
+ "You are a deployment verification agent. Make ONLY read-only HTTP requests to the single",
44224
+ "environment URL given in the prompt to confirm a deploy succeeded. Do not read or write any",
44225
+ "files, do not use repository or credential access, do not contact any other host, and do not",
44226
+ "modify anything \u2014 these are rules you must follow, not limits the environment enforces. Follow",
44227
+ "the verification steps and end with a single VERDICT line exactly as instructed."
44285
44228
  ].join("\n");
44286
44229
  var SELF_REVIEW_BASE_INSTRUCTIONS = [
44287
44230
  "You are a code self-review agent. You MAY run read-only git/shell commands (e.g. `git log`,",
@@ -44292,11 +44235,14 @@ var SELF_REVIEW_BASE_INSTRUCTIONS = [
44292
44235
  async function runCollectedCodexTurn(client, prompt, cwd, baseInstructions) {
44293
44236
  const { threadId } = await acquireThread(client, {
44294
44237
  threadId: null,
44238
+ // danger-full-access: collected turns (self-review reads the worktree diff; deploy
44239
+ // verification hits the deployed env URL over the network) run unrestricted, matching
44240
+ // the owner decision that the executor is not sandboxed. The turn's base instructions
44241
+ // (review-only / observe-only) bound what it actually does.
44295
44242
  startParams: {
44296
44243
  cwd,
44297
44244
  approvalPolicy: "never",
44298
- sandbox: null,
44299
- // the executor permissions profile governs; an override would discard it
44245
+ sandbox: "danger-full-access",
44300
44246
  model: null,
44301
44247
  baseInstructions,
44302
44248
  developerInstructions: null,
@@ -44308,14 +44254,34 @@ async function runCollectedCodexTurn(client, prompt, cwd, baseInstructions) {
44308
44254
  const mapper = new CodexBridgeEventMapper();
44309
44255
  mapper.reset();
44310
44256
  const chunks = [];
44257
+ let acceptedTurnId = null;
44258
+ let turnError = null;
44259
+ let resolveTurnEnded = null;
44260
+ const turnEnded = new Promise((resolve) => {
44261
+ resolveTurnEnded = resolve;
44262
+ });
44311
44263
  const unsub = client.onNotification((method, params) => {
44312
44264
  for (const ev of mapper.mapNotification({ method, params }, threadId)) {
44313
44265
  if (ev.type === "assistant") chunks.push(ev.content);
44266
+ else if (ev.type === "error") turnError = ev.content;
44267
+ }
44268
+ if (method === "turn/completed") {
44269
+ const p = params;
44270
+ const threadMatches = !p?.threadId || p.threadId === threadId;
44271
+ const turnMatches = !acceptedTurnId || !p?.turn?.id || p.turn.id === acceptedTurnId;
44272
+ if (threadMatches && turnMatches) resolveTurnEnded?.();
44314
44273
  }
44315
44274
  });
44316
44275
  try {
44317
44276
  await awaitTurnWithDeadline({
44318
- run: () => client.turn.start({ threadId, input: [{ type: "text", text: prompt }] }),
44277
+ run: async () => {
44278
+ const accepted = await client.turn.start({
44279
+ threadId,
44280
+ input: [{ type: "text", text: prompt }]
44281
+ });
44282
+ acceptedTurnId = accepted?.turn?.id ?? null;
44283
+ await turnEnded;
44284
+ },
44319
44285
  interrupt: () => client.turn.interrupt({ threadId }),
44320
44286
  timeoutMs: COLLECTED_TURN_TIMEOUT_MS,
44321
44287
  timeoutMessage: `Collected turn did not complete within ${COLLECTED_TURN_TIMEOUT_MS}ms.`
@@ -44324,6 +44290,7 @@ async function runCollectedCodexTurn(client, prompt, cwd, baseInstructions) {
44324
44290
  } finally {
44325
44291
  unsub();
44326
44292
  }
44293
+ if (turnError) throw new Error(`Collected turn failed: ${turnError}`);
44327
44294
  return chunks.join("");
44328
44295
  }
44329
44296
  function resolveDefaultBranch(checkoutPath) {
@@ -44362,7 +44329,7 @@ async function runOrphanWorktreeSweep(client, opts) {
44362
44329
  const cutoff = Date.now() - worktreeRetentionMs();
44363
44330
  const isReapable = (worktreePath) => {
44364
44331
  try {
44365
- return import_node_fs11.default.statSync(worktreePath).mtimeMs < cutoff;
44332
+ return import_node_fs10.default.statSync(worktreePath).mtimeMs < cutoff;
44366
44333
  } catch {
44367
44334
  return false;
44368
44335
  }
@@ -44399,8 +44366,8 @@ async function runOrphanWorktreeSweep(client, opts) {
44399
44366
  }
44400
44367
  }
44401
44368
  function readStateDir() {
44402
- if (process.env.CODEX_HOME) return import_node_path12.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge");
44403
- return import_node_path12.default.join(import_node_os8.default.homedir(), ".mrrlin", "director-bridge");
44369
+ if (process.env.CODEX_HOME) return import_node_path11.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge");
44370
+ return import_node_path11.default.join(import_node_os7.default.homedir(), ".mrrlin", "director-bridge");
44404
44371
  }
44405
44372
  function readAllowedOrigins() {
44406
44373
  const raw = (process.env.MRRLIN_DIRECTOR_BRIDGE_ALLOWED_ORIGINS ?? "").trim();
@@ -45264,13 +45231,14 @@ async function startDirectorBridge() {
45264
45231
  codexCwd: config2.codexCwd,
45265
45232
  codexExecutable: config2.codexExecutable,
45266
45233
  checkoutRegistry,
45267
- worktreeRoot: import_node_path12.default.join(config2.stateDir, "worktrees"),
45268
- locksDir: import_node_path12.default.join(config2.stateDir, "locks"),
45269
- // Wire the profile-enforced executor server for code-execution runs. Each run gets its own
45270
- // sandboxed Codex instance (scrubbed env, scratch CODEX_HOME, deny-read, egress allowlist).
45234
+ worktreeRoot: import_node_path11.default.join(config2.stateDir, "worktrees"),
45235
+ locksDir: import_node_path11.default.join(config2.stateDir, "locks"),
45236
+ // Wire the executor server for code-execution runs. Each run gets its own app-server
45237
+ // process at the run worktree, running UNRESTRICTED from the real ~/.codex (owner
45238
+ // no-sandbox decision — auth + mrrlin MCP come from the operator config).
45271
45239
  // The returned handle's dispose() shuts down the spawned app-server CHILD (manager.shutdown(),
45272
- // not the JSON-RPC-only client.close()) and removes the scratch dir releasing the executor
45273
- // therefore kills the process and cleans up. driveRun owns this dispose per-run (Fix A + G).
45240
+ // not the JSON-RPC-only client.close()) releasing the executor therefore kills the process.
45241
+ // driveRun owns this dispose per-run (Fix A + G).
45274
45242
  acquireExecutorCodex: async (worktreePath) => {
45275
45243
  const handle = await startExecutorServer({ worktree: worktreePath });
45276
45244
  return { client: handle.client, dispose: handle.dispose };
@@ -45281,15 +45249,15 @@ async function startDirectorBridge() {
45281
45249
  // fail. If the resolved branch does not exist on origin, createRunWorktree surfaces a
45282
45250
  // `default_branch_unresolved` error which the consumer routes to a clean operator handoff.
45283
45251
  getDefaultBranch: (projectSlug) => Promise.resolve(resolveDefaultBranch(checkoutRegistry.get(projectSlug)?.path ?? null)),
45284
- // ADR 0007 self-review: critique the committed diff on the executor's own sandbox before
45252
+ // ADR 0007 self-review: critique the committed diff on the executor's own client before
45285
45253
  // push/PR. Reuses the verification turn-runner (acquireThread + collect assistant text).
45286
45254
  selfReviewTurn: createSelfReviewTurn({
45287
45255
  runTurn: (codexClient, cwd, prompt) => runCollectedCodexTurn(codexClient, prompt, cwd, SELF_REVIEW_BASE_INSTRUCTIONS)
45288
45256
  }),
45289
45257
  // ADR 0015 deploy gate: after a pushed run reaches dev_deploy/prod_deploy, the backstop sweep
45290
- // dispatches the deploy + polls + runs a DEDICATED sandboxed verification turn against the
45291
- // deployed env URL. The verification sandbox is network-only (egress allowlisted to ONLY the
45292
- // env host) with a fresh empty scratch cwd no repo/worktree/secret access.
45258
+ // dispatches the deploy + polls + runs a verification turn against the deployed env URL in a
45259
+ // throwaway cwd. Per the owner no-sandbox decision the verification turn runs from the real
45260
+ // ~/.codex like every other executor turn; its observe-only base instructions bound behaviour.
45293
45261
  deployGate: {
45294
45262
  loadVerificationSection: async (projectSlug, task) => {
45295
45263
  if (!task.specWikiPageId) return null;
@@ -45297,14 +45265,16 @@ async function startDirectorBridge() {
45297
45265
  return page?.markdownBody ? extractVerificationSection(page.markdownBody) : null;
45298
45266
  },
45299
45267
  runVerificationTurn: createRunVerificationTurn({
45300
- startSandbox: async (egressDomains) => {
45301
- const cwd = import_node_fs11.default.mkdtempSync(import_node_path12.default.join(import_node_os8.default.tmpdir(), "mrrlin-verify-"));
45268
+ // egressDomains is retained in the seam signature (verification-turn.ts still computes the
45269
+ // env host) but no longer constrains anything — the executor is unrestricted.
45270
+ startSandbox: async (_egressDomains) => {
45271
+ const cwd = import_node_fs10.default.mkdtempSync(import_node_path11.default.join(import_node_os7.default.tmpdir(), "mrrlin-verify-"));
45302
45272
  let handle;
45303
45273
  try {
45304
- handle = await startExecutorServer({ worktree: cwd, egressDomains });
45274
+ handle = await startExecutorServer({ worktree: cwd });
45305
45275
  } catch (error51) {
45306
45276
  try {
45307
- import_node_fs11.default.rmSync(cwd, { recursive: true, force: true });
45277
+ import_node_fs10.default.rmSync(cwd, { recursive: true, force: true });
45308
45278
  } catch {
45309
45279
  }
45310
45280
  throw error51;
@@ -45315,7 +45285,7 @@ async function startDirectorBridge() {
45315
45285
  dispose: () => {
45316
45286
  handle.dispose();
45317
45287
  try {
45318
- import_node_fs11.default.rmSync(cwd, { recursive: true, force: true });
45288
+ import_node_fs10.default.rmSync(cwd, { recursive: true, force: true });
45319
45289
  } catch {
45320
45290
  }
45321
45291
  }
@@ -45455,7 +45425,7 @@ async function startDirectorBridge() {
45455
45425
  const bridgeLogger = createBridgeLogger(config2.stateDir, process.env);
45456
45426
  let bridgeBinShaShort = "unknown00000";
45457
45427
  try {
45458
- bridgeBinShaShort = import_node_crypto5.default.createHash("sha256").update(import_node_fs11.default.readFileSync(process.argv[1] ?? "")).digest("hex").slice(0, 12);
45428
+ bridgeBinShaShort = import_node_crypto5.default.createHash("sha256").update(import_node_fs10.default.readFileSync(process.argv[1] ?? "")).digest("hex").slice(0, 12);
45459
45429
  } catch {
45460
45430
  }
45461
45431
  attachBridgeWss(server, {
@@ -45564,8 +45534,8 @@ async function startDirectorBridge() {
45564
45534
  enqueueRun(
45565
45535
  () => runOrphanWorktreeSweep(client, {
45566
45536
  checkoutRegistry,
45567
- worktreeRoot: import_node_path12.default.join(config2.stateDir, "worktrees"),
45568
- locksDir: import_node_path12.default.join(config2.stateDir, "locks")
45537
+ worktreeRoot: import_node_path11.default.join(config2.stateDir, "worktrees"),
45538
+ locksDir: import_node_path11.default.join(config2.stateDir, "locks")
45569
45539
  })
45570
45540
  );
45571
45541
  enqueueRun(async () => {
@@ -45777,15 +45747,15 @@ function readDispatchBody(req) {
45777
45747
  function readOrCreateBridgeToken(stateDir) {
45778
45748
  const explicit = (process.env.MRRLIN_DIRECTOR_BRIDGE_TOKEN ?? "").trim();
45779
45749
  if (explicit) return explicit;
45780
- const tokenPath = import_node_path12.default.join(stateDir, "token.txt");
45750
+ const tokenPath = import_node_path11.default.join(stateDir, "token.txt");
45781
45751
  try {
45782
- const existing = import_node_fs11.default.readFileSync(tokenPath, "utf8").trim();
45752
+ const existing = import_node_fs10.default.readFileSync(tokenPath, "utf8").trim();
45783
45753
  if (existing) return existing;
45784
45754
  } catch {
45785
45755
  }
45786
- import_node_fs11.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
45756
+ import_node_fs10.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
45787
45757
  const token = import_node_crypto5.default.randomBytes(32).toString("base64url");
45788
- import_node_fs11.default.writeFileSync(tokenPath, `${token}
45758
+ import_node_fs10.default.writeFileSync(tokenPath, `${token}
45789
45759
  `, { mode: 384 });
45790
45760
  return token;
45791
45761
  }
@@ -49169,17 +49139,17 @@ var StdioServerTransport = class {
49169
49139
  // src/tools.ts
49170
49140
  var import_node_crypto8 = require("node:crypto");
49171
49141
  var import_promises3 = require("node:fs/promises");
49172
- var import_node_fs13 = require("node:fs");
49173
- var import_node_path15 = __toESM(require("node:path"), 1);
49174
- var import_node_os9 = __toESM(require("node:os"), 1);
49142
+ var import_node_fs12 = require("node:fs");
49143
+ var import_node_path14 = __toESM(require("node:path"), 1);
49144
+ var import_node_os8 = __toESM(require("node:os"), 1);
49175
49145
  var import_node_child_process7 = require("node:child_process");
49176
49146
 
49177
49147
  // ../../packages/wiki/dist/index.js
49178
49148
  var import_promises2 = __toESM(require("node:fs/promises"), 1);
49179
- var import_node_path13 = __toESM(require("node:path"), 1);
49149
+ var import_node_path12 = __toESM(require("node:path"), 1);
49180
49150
  var INCLUDED_DOC_FOLDERS = /* @__PURE__ */ new Set(["_meta", "explanation", "how-to", "reference", "sdd", "tutorials"]);
49181
49151
  function toPosixPath(input) {
49182
- return input.split(import_node_path13.default.sep).join("/");
49152
+ return input.split(import_node_path12.default.sep).join("/");
49183
49153
  }
49184
49154
  function slugifyWikiSegment(input) {
49185
49155
  return input.trim().toLowerCase().replace(/`/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -49193,12 +49163,12 @@ async function pathExists2(input) {
49193
49163
  }
49194
49164
  }
49195
49165
  async function findWorkspaceRoot(start = process.cwd()) {
49196
- let current = import_node_path13.default.resolve(start);
49166
+ let current = import_node_path12.default.resolve(start);
49197
49167
  while (true) {
49198
- if (await pathExists2(import_node_path13.default.join(current, "pnpm-workspace.yaml")) && await pathExists2(import_node_path13.default.join(current, "docs"))) {
49168
+ if (await pathExists2(import_node_path12.default.join(current, "pnpm-workspace.yaml")) && await pathExists2(import_node_path12.default.join(current, "docs"))) {
49199
49169
  return current;
49200
49170
  }
49201
- const parent = import_node_path13.default.dirname(current);
49171
+ const parent = import_node_path12.default.dirname(current);
49202
49172
  if (parent === current) {
49203
49173
  throw new Error(`Unable to find Mrrlin workspace root from ${start}`);
49204
49174
  }
@@ -49207,28 +49177,28 @@ async function findWorkspaceRoot(start = process.cwd()) {
49207
49177
  }
49208
49178
  async function resolveDocsRoot(input) {
49209
49179
  if (input) {
49210
- return import_node_path13.default.resolve(input);
49180
+ return import_node_path12.default.resolve(input);
49211
49181
  }
49212
49182
  if (process.env.MRRLIN_DOCS_ROOT) {
49213
- return import_node_path13.default.resolve(process.env.MRRLIN_DOCS_ROOT);
49183
+ return import_node_path12.default.resolve(process.env.MRRLIN_DOCS_ROOT);
49214
49184
  }
49215
- return import_node_path13.default.join(await findWorkspaceRoot(), "docs");
49185
+ return import_node_path12.default.join(await findWorkspaceRoot(), "docs");
49216
49186
  }
49217
49187
  async function resolveIndexPath(input) {
49218
49188
  if (input) {
49219
- return import_node_path13.default.resolve(input);
49189
+ return import_node_path12.default.resolve(input);
49220
49190
  }
49221
49191
  if (process.env.MRRLIN_WIKI_INDEX_PATH) {
49222
- return import_node_path13.default.resolve(process.env.MRRLIN_WIKI_INDEX_PATH);
49192
+ return import_node_path12.default.resolve(process.env.MRRLIN_WIKI_INDEX_PATH);
49223
49193
  }
49224
49194
  const workspaceRoot = await findWorkspaceRoot();
49225
- return import_node_path13.default.join(workspaceRoot, "apps", "web", "public", "wiki-index.json");
49195
+ return import_node_path12.default.join(workspaceRoot, "apps", "web", "public", "wiki-index.json");
49226
49196
  }
49227
49197
  async function walkMarkdownFiles(root, dir = root) {
49228
49198
  const entries = await import_promises2.default.readdir(dir, { withFileTypes: true });
49229
49199
  const files = [];
49230
49200
  for (const entry of entries) {
49231
- const absolute = import_node_path13.default.join(dir, entry.name);
49201
+ const absolute = import_node_path12.default.join(dir, entry.name);
49232
49202
  if (entry.isDirectory()) {
49233
49203
  files.push(...await walkMarkdownFiles(root, absolute));
49234
49204
  continue;
@@ -49247,7 +49217,7 @@ function parseTitle(markdown, sourcePath) {
49247
49217
  return title;
49248
49218
  }
49249
49219
  function documentFromFile(docsRoot, absolutePath, markdown) {
49250
- const relativePath = toPosixPath(import_node_path13.default.relative(docsRoot, absolutePath));
49220
+ const relativePath = toPosixPath(import_node_path12.default.relative(docsRoot, absolutePath));
49251
49221
  const [section, fileName] = relativePath.split("/");
49252
49222
  if (!section || !fileName || !INCLUDED_DOC_FOLDERS.has(section)) {
49253
49223
  return null;
@@ -49801,8 +49771,8 @@ function errMessage(err) {
49801
49771
  }
49802
49772
 
49803
49773
  // src/consensus/wiring.ts
49804
- var import_node_fs12 = __toESM(require("node:fs"), 1);
49805
- var import_node_path14 = __toESM(require("node:path"), 1);
49774
+ var import_node_fs11 = __toESM(require("node:fs"), 1);
49775
+ var import_node_path13 = __toESM(require("node:path"), 1);
49806
49776
  var import_node_url2 = require("node:url");
49807
49777
 
49808
49778
  // src/consensus/code-gate-git.ts
@@ -50037,15 +50007,15 @@ ${res.stdout}${res.stderr}`);
50037
50007
  var import_meta2 = {};
50038
50008
  function getPersonasDir() {
50039
50009
  if (typeof __dirname !== "undefined") {
50040
- return import_node_path14.default.join(__dirname, "consensus", "personas");
50010
+ return import_node_path13.default.join(__dirname, "consensus", "personas");
50041
50011
  }
50042
- return import_node_path14.default.join(import_node_path14.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url)), "personas");
50012
+ return import_node_path13.default.join(import_node_path13.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url)), "personas");
50043
50013
  }
50044
50014
  var personaCache = /* @__PURE__ */ new Map();
50045
50015
  function loadPersona(name) {
50046
50016
  const cached2 = personaCache.get(name);
50047
50017
  if (cached2 !== void 0) return cached2;
50048
- const content = import_node_fs12.default.readFileSync(import_node_path14.default.join(getPersonasDir(), `${name}.md`), "utf8");
50018
+ const content = import_node_fs11.default.readFileSync(import_node_path13.default.join(getPersonasDir(), `${name}.md`), "utf8");
50049
50019
  personaCache.set(name, content);
50050
50020
  return content;
50051
50021
  }
@@ -51905,7 +51875,7 @@ function createMrrlinTools(options) {
51905
51875
  "ARTIFACT_FILE_UPLOAD_FAILED",
51906
51876
  "Unable to upload artifact file.",
51907
51877
  async (c, { projectSlug, path: filePath, class: artifactClass, taskId, runId, description }) => {
51908
- const extension2 = import_node_path15.default.extname(filePath).toLowerCase();
51878
+ const extension2 = import_node_path14.default.extname(filePath).toLowerCase();
51909
51879
  const contentType = ARTIFACT_EXTENSION_TYPES[extension2];
51910
51880
  if (!contentType) {
51911
51881
  throw new McpToolCodedError(
@@ -51933,7 +51903,7 @@ function createMrrlinTools(options) {
51933
51903
  "Artifact exceeds 5MB; split or summarize instead."
51934
51904
  );
51935
51905
  }
51936
- const filename = import_node_path15.default.basename(filePath);
51906
+ const filename = import_node_path14.default.basename(filePath);
51937
51907
  const created = await c.createArtifactFile(projectSlug, {
51938
51908
  class: artifactClass,
51939
51909
  contentType,
@@ -52016,7 +51986,7 @@ function createMrrlinTools(options) {
52016
51986
  "Unable to register local checkout.",
52017
51987
  async (_c, { projectSlug, path: checkoutPath, repo }) => {
52018
51988
  const trimmedPath = checkoutPath.trim();
52019
- if (!import_node_path15.default.isAbsolute(trimmedPath)) {
51989
+ if (!import_node_path14.default.isAbsolute(trimmedPath)) {
52020
51990
  throw new McpToolCodedError(
52021
51991
  "LOCAL_CHECKOUT_PATH_NOT_ABSOLUTE",
52022
51992
  `Path must be absolute, got: ${trimmedPath}`
@@ -52024,7 +51994,7 @@ function createMrrlinTools(options) {
52024
51994
  }
52025
51995
  let resolvedPath;
52026
51996
  try {
52027
- resolvedPath = (0, import_node_fs13.realpathSync)(trimmedPath);
51997
+ resolvedPath = (0, import_node_fs12.realpathSync)(trimmedPath);
52028
51998
  } catch {
52029
51999
  throw new McpToolCodedError(
52030
52000
  "LOCAL_CHECKOUT_PATH_NOT_FOUND",
@@ -52051,7 +52021,7 @@ function createMrrlinTools(options) {
52051
52021
  `Origin remote ${remoteUrl} does not match repo ${repo}.`
52052
52022
  );
52053
52023
  }
52054
- const stateDir = process.env.CODEX_HOME ? import_node_path15.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge") : import_node_path15.default.join(import_node_os9.default.homedir(), ".mrrlin", "director-bridge");
52024
+ 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");
52055
52025
  const registry3 = new CheckoutRegistry(stateDir);
52056
52026
  const confirmedAt = (/* @__PURE__ */ new Date()).toISOString();
52057
52027
  registry3.confirm(projectSlug, resolvedPath, confirmedAt);
@@ -52258,13 +52228,13 @@ function runSetCredential(args) {
52258
52228
  }
52259
52229
 
52260
52230
  // src/install-service.ts
52261
- var import_node_os10 = __toESM(require("node:os"), 1);
52262
- var import_node_path17 = __toESM(require("node:path"), 1);
52231
+ var import_node_os9 = __toESM(require("node:os"), 1);
52232
+ var import_node_path16 = __toESM(require("node:path"), 1);
52263
52233
 
52264
52234
  // src/service-paths.ts
52265
- var import_node_path16 = __toESM(require("node:path"), 1);
52235
+ var import_node_path15 = __toESM(require("node:path"), 1);
52266
52236
  function isWorktreePath(p) {
52267
- const norm = import_node_path16.default.normalize(p).split(import_node_path16.default.sep).join("/");
52237
+ const norm = import_node_path15.default.normalize(p).split(import_node_path15.default.sep).join("/");
52268
52238
  return norm.includes("/.claude/worktrees/");
52269
52239
  }
52270
52240
  function resolveServiceCwd(opts) {
@@ -52318,7 +52288,7 @@ function installService(deps) {
52318
52288
  deps.log(resolved.reason);
52319
52289
  return { ok: false };
52320
52290
  }
52321
- const home = deps.env.HOME ?? import_node_os10.default.homedir();
52291
+ const home = deps.env.HOME ?? import_node_os9.default.homedir();
52322
52292
  const bins = ["node", "codex", "mrrlin-mcp"].map((b) => deps.which(b));
52323
52293
  if (bins.some((b) => !b)) {
52324
52294
  deps.log("Could not resolve absolute paths for node/codex/mrrlin-mcp on PATH. Install them or fix PATH.");
@@ -52329,7 +52299,7 @@ function installService(deps) {
52329
52299
  return { ok: false };
52330
52300
  }
52331
52301
  const resolvedBins = bins.filter((b) => b !== null);
52332
- const pathEnv = Array.from(new Set(resolvedBins.map((b) => import_node_path17.default.dirname(b)))).join(":");
52302
+ const pathEnv = Array.from(new Set(resolvedBins.map((b) => import_node_path16.default.dirname(b)))).join(":");
52333
52303
  const text = renderEcosystemConfig({
52334
52304
  cwd: resolved.cwd,
52335
52305
  home,
@@ -52338,7 +52308,7 @@ function installService(deps) {
52338
52308
  staging: deps.env.MRRLIN_STAGING === "1",
52339
52309
  apiBaseUrl: deps.env.MRRLIN_API_BASE_URL?.trim() || void 0
52340
52310
  });
52341
- const ecoPath = import_node_path17.default.join(home, ".mrrlin", "ecosystem.config.cjs");
52311
+ const ecoPath = import_node_path16.default.join(home, ".mrrlin", "ecosystem.config.cjs");
52342
52312
  deps.writeFile(ecoPath, text);
52343
52313
  const start = deps.runPm2(["startOrReload", ecoPath]);
52344
52314
  if (start.code !== 0) {
@@ -52369,13 +52339,13 @@ function uninstallService(deps) {
52369
52339
 
52370
52340
  // src/uninstall-codex.ts
52371
52341
  var import_promises4 = __toESM(require("node:fs/promises"), 1);
52372
- var import_node_os11 = __toESM(require("node:os"), 1);
52373
- var import_node_path18 = __toESM(require("node:path"), 1);
52342
+ var import_node_os10 = __toESM(require("node:os"), 1);
52343
+ var import_node_path17 = __toESM(require("node:path"), 1);
52374
52344
  var toml2 = __toESM(require_toml(), 1);
52375
52345
  function resolveCodexHome2(options) {
52376
52346
  if (options.codexHome) return options.codexHome;
52377
52347
  if (process.env.CODEX_HOME) return process.env.CODEX_HOME;
52378
- return import_node_path18.default.join(options.homeDir ?? import_node_os11.default.homedir(), ".codex");
52348
+ return import_node_path17.default.join(options.homeDir ?? import_node_os10.default.homedir(), ".codex");
52379
52349
  }
52380
52350
  async function pathExists3(target) {
52381
52351
  try {
@@ -52388,7 +52358,7 @@ async function pathExists3(target) {
52388
52358
  var MRRLIN_BLOCK_RE = /(^|\n)\[mcp_servers\.mrrlin(?:-browser)?(?:\]|\.[^\]\n]*\])[\s\S]*?(?=\n\[|$)/g;
52389
52359
  async function uninstallCodex(options = {}) {
52390
52360
  const codexHome = resolveCodexHome2(options);
52391
- const configPath = import_node_path18.default.join(codexHome, "config.toml");
52361
+ const configPath = import_node_path17.default.join(codexHome, "config.toml");
52392
52362
  const removePrompts = async () => uninstallPrompts(codexHome, options.promptNames ?? []);
52393
52363
  if (!await pathExists3(configPath)) {
52394
52364
  return { action: "missing", configPath, prompts: await removePrompts() };
@@ -52416,10 +52386,10 @@ async function uninstallCodex(options = {}) {
52416
52386
  }
52417
52387
  async function uninstallPrompts(codexHome, names) {
52418
52388
  if (names.length === 0) return [];
52419
- const promptsDir2 = import_node_path18.default.join(codexHome, "prompts");
52389
+ const promptsDir2 = import_node_path17.default.join(codexHome, "prompts");
52420
52390
  const out = [];
52421
52391
  for (const name of names) {
52422
- const promptPath = import_node_path18.default.join(promptsDir2, `${name}.md`);
52392
+ const promptPath = import_node_path17.default.join(promptsDir2, `${name}.md`);
52423
52393
  try {
52424
52394
  await import_promises4.default.unlink(promptPath);
52425
52395
  out.push({ name, action: "removed", promptPath });
@@ -52436,16 +52406,16 @@ async function uninstallPrompts(codexHome, names) {
52436
52406
  }
52437
52407
 
52438
52408
  // src/report-issue-prompt.ts
52439
- var import_node_fs14 = __toESM(require("node:fs"), 1);
52440
- var import_node_path19 = __toESM(require("node:path"), 1);
52409
+ var import_node_fs13 = __toESM(require("node:fs"), 1);
52410
+ var import_node_path18 = __toESM(require("node:path"), 1);
52441
52411
  var import_node_url3 = require("node:url");
52442
52412
  var import_meta3 = {};
52443
52413
  function promptsDir() {
52444
- if (typeof __dirname !== "undefined") return import_node_path19.default.join(__dirname, "prompts");
52445
- return import_node_path19.default.join(import_node_path19.default.dirname((0, import_node_url3.fileURLToPath)(import_meta3.url)), "prompts");
52414
+ if (typeof __dirname !== "undefined") return import_node_path18.default.join(__dirname, "prompts");
52415
+ return import_node_path18.default.join(import_node_path18.default.dirname((0, import_node_url3.fileURLToPath)(import_meta3.url)), "prompts");
52446
52416
  }
52447
52417
  function readReportIssuePrompt() {
52448
- return import_node_fs14.default.readFileSync(import_node_path19.default.join(promptsDir(), "report-issue.md"), "utf8");
52418
+ return import_node_fs13.default.readFileSync(import_node_path18.default.join(promptsDir(), "report-issue.md"), "utf8");
52449
52419
  }
52450
52420
 
52451
52421
  // src/bin.ts
@@ -52705,8 +52675,8 @@ async function main() {
52705
52675
  env: process.env,
52706
52676
  cwd: process.cwd(),
52707
52677
  writeFile: (p, c) => {
52708
- (0, import_node_fs15.mkdirSync)(import_node_path21.default.dirname(p), { recursive: true, mode: 448 });
52709
- (0, import_node_fs15.writeFileSync)(p, c, { mode: 384 });
52678
+ (0, import_node_fs14.mkdirSync)(import_node_path20.default.dirname(p), { recursive: true, mode: 448 });
52679
+ (0, import_node_fs14.writeFileSync)(p, c, { mode: 384 });
52710
52680
  },
52711
52681
  runPm2: pm2Runner,
52712
52682
  which: whichBin,
@@ -52723,35 +52693,35 @@ async function main() {
52723
52693
  runPm2: (a) => ({ code: pm2Runner(a).code }),
52724
52694
  removeFile: (p) => {
52725
52695
  try {
52726
- (0, import_node_fs15.rmSync)(p, { force: true });
52696
+ (0, import_node_fs14.rmSync)(p, { force: true });
52727
52697
  } catch {
52728
52698
  }
52729
52699
  },
52730
52700
  purgeSecret: purge,
52731
52701
  secretPath: agentCredentialPath(),
52732
52702
  tokenPath: operatorTokenPath(),
52733
- ecoPath: import_node_path21.default.join(process.env.HOME ?? (0, import_node_os12.homedir)(), ".mrrlin", "ecosystem.config.cjs"),
52703
+ ecoPath: import_node_path20.default.join(process.env.HOME ?? (0, import_node_os11.homedir)(), ".mrrlin", "ecosystem.config.cjs"),
52734
52704
  log: (m) => process.stderr.write(`[mrrlin-mcp uninstall-service] ${m}
52735
52705
  `)
52736
52706
  });
52737
52707
  return;
52738
52708
  }
52739
52709
  case "uninstall": {
52740
- const home = process.env.HOME ?? (0, import_node_os12.homedir)();
52710
+ const home = process.env.HOME ?? (0, import_node_os11.homedir)();
52741
52711
  const log = (m) => process.stderr.write(`[mrrlin-mcp uninstall] ${m}
52742
52712
  `);
52743
52713
  uninstallService({
52744
52714
  runPm2: (a) => ({ code: pm2Runner(a).code }),
52745
52715
  removeFile: (p) => {
52746
52716
  try {
52747
- (0, import_node_fs15.rmSync)(p, { force: true });
52717
+ (0, import_node_fs14.rmSync)(p, { force: true });
52748
52718
  } catch {
52749
52719
  }
52750
52720
  },
52751
52721
  purgeSecret: true,
52752
52722
  secretPath: agentCredentialPath(),
52753
52723
  tokenPath: operatorTokenPath(),
52754
- ecoPath: import_node_path21.default.join(home, ".mrrlin", "ecosystem.config.cjs"),
52724
+ ecoPath: import_node_path20.default.join(home, ".mrrlin", "ecosystem.config.cjs"),
52755
52725
  log
52756
52726
  });
52757
52727
  let codexOk = true;
@@ -52771,7 +52741,7 @@ async function main() {
52771
52741
  log(`codex config NOT modified: ${error51 instanceof Error ? error51.message : String(error51)}`);
52772
52742
  }
52773
52743
  try {
52774
- (0, import_node_fs15.rmSync)(import_node_path21.default.join(home, ".mrrlin"), { recursive: true, force: true });
52744
+ (0, import_node_fs14.rmSync)(import_node_path20.default.join(home, ".mrrlin"), { recursive: true, force: true });
52775
52745
  } catch {
52776
52746
  }
52777
52747
  log("removed ~/.mrrlin");
@@ -52812,7 +52782,7 @@ ${HELP_TEXT}`);
52812
52782
  }
52813
52783
  }
52814
52784
  function resolveSelfBinPath() {
52815
- return import_node_path20.default.resolve(process.argv[1] ?? process.execPath);
52785
+ return import_node_path19.default.resolve(process.argv[1] ?? process.execPath);
52816
52786
  }
52817
52787
  main().catch((error51) => {
52818
52788
  process.stderr.write(`mrrlin-mcp fatal error: ${error51 instanceof Error ? error51.message : String(error51)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrrlin-dev/mcp",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mrrlin-mcp": "dist/bin.cjs"