@mrrlin-dev/mcp 0.4.123 → 0.4.125

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 +184 -91
  2. package/package.json +1 -1
package/dist/bin.cjs CHANGED
@@ -39249,6 +39249,7 @@ var mrrlinTaskUpdateSchema = external_exports.object({
39249
39249
  directorThreadProvider: external_exports.enum(["codex", "claude", "agy"]).nullable().optional(),
39250
39250
  declaredOutcome: mrrlinDeclaredOutcomeSchema.nullable().optional(),
39251
39251
  dueDate: external_exports.string().date().nullable().optional(),
39252
+ expectedUpdatedAt: external_exports.string().datetime().optional(),
39252
39253
  impact: mrrlinTaskImpactSchema.optional(),
39253
39254
  notes: external_exports.string().nullable().optional(),
39254
39255
  planId: mrrlinPlanIdSchema.nullable().optional(),
@@ -40730,83 +40731,132 @@ function createMrrlinClient(config2) {
40730
40731
  const customFetch = config2.fetch ?? globalThis.fetch;
40731
40732
  const baseUrl = config2.baseUrl.replace(/\/$/, "");
40732
40733
  const token = config2.token;
40733
- async function request(path33, init) {
40734
- const method = init?.method ?? "GET";
40734
+ function parseRetryAfter(response) {
40735
+ const header = response.headers.get("retry-after");
40736
+ if (!header)
40737
+ return null;
40738
+ const seconds = Number(header);
40739
+ if (!isNaN(seconds) && seconds >= 0) {
40740
+ return seconds * 1e3;
40741
+ }
40742
+ const dateMs = Date.parse(header);
40743
+ if (!isNaN(dateMs)) {
40744
+ const diff = dateMs - Date.now();
40745
+ return diff > 0 ? diff : 0;
40746
+ }
40747
+ return null;
40748
+ }
40749
+ const RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
40750
+ async function abortableDelay(ms, signal) {
40751
+ if (signal?.aborted) {
40752
+ throw new DOMException("The user aborted a request.", "AbortError");
40753
+ }
40754
+ return new Promise((resolve2, reject) => {
40755
+ const timer = setTimeout(() => {
40756
+ if (signal)
40757
+ signal.removeEventListener("abort", onAbort);
40758
+ resolve2();
40759
+ }, ms);
40760
+ function onAbort() {
40761
+ clearTimeout(timer);
40762
+ signal?.removeEventListener("abort", onAbort);
40763
+ reject(new DOMException("The user aborted a request.", "AbortError"));
40764
+ }
40765
+ if (signal) {
40766
+ signal.addEventListener("abort", onAbort, { once: true });
40767
+ }
40768
+ });
40769
+ }
40770
+ async function executeFetch(path33, init) {
40771
+ const method = (init?.method ?? "GET").toUpperCase();
40735
40772
  const headers = new Headers(init?.headers ?? {});
40736
40773
  if (token && !headers.has("authorization")) {
40737
40774
  headers.set("authorization", `Bearer ${token}`);
40738
40775
  }
40739
- const response = await customFetch(`${baseUrl}${path33}`, { ...init, headers });
40740
- if (!response.ok) {
40741
- const body = await response.text();
40742
- let code = `HTTP_${response.status}`;
40743
- let violations = null;
40744
- let message = `Mrrlin API ${method} ${path33} failed: ${response.status}`;
40776
+ const url2 = `${baseUrl}${path33}`;
40777
+ const isIdempotent = method === "GET" || method === "HEAD";
40778
+ const maxAttempts = isIdempotent ? 3 : 1;
40779
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
40745
40780
  try {
40746
- const parsed = JSON.parse(body);
40747
- const violationsParsed = apiViolationsErrorSchema.safeParse(parsed);
40748
- if (violationsParsed.success) {
40749
- code = violationsParsed.data.code;
40750
- violations = violationsParsed.data.violations;
40751
- message = violationsParsed.data.message;
40752
- } else {
40753
- const errorParsed = apiErrorSchema.safeParse(parsed);
40754
- if (errorParsed.success) {
40755
- code = errorParsed.data.code;
40756
- message = errorParsed.data.message;
40781
+ if (init?.signal?.aborted) {
40782
+ throw new DOMException("The user aborted a request.", "AbortError");
40783
+ }
40784
+ const response = await customFetch(url2, { ...init, headers });
40785
+ if (response.ok) {
40786
+ return response;
40787
+ }
40788
+ if (isIdempotent && RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxAttempts - 1) {
40789
+ const retryAfterMs = parseRetryAfter(response);
40790
+ if (retryAfterMs !== null && retryAfterMs > 1e4) {
40791
+ return response;
40792
+ }
40793
+ try {
40794
+ await response.body?.cancel();
40795
+ } catch {
40757
40796
  }
40797
+ const backoffMs = retryAfterMs ?? Math.floor(Math.random() * Math.min(2e3, 250 * Math.pow(2, attempt)));
40798
+ await abortableDelay(backoffMs, init?.signal);
40799
+ continue;
40758
40800
  }
40759
- } catch {
40801
+ return response;
40802
+ } catch (err) {
40803
+ if (init?.signal?.aborted || err?.name === "AbortError") {
40804
+ throw err;
40805
+ }
40806
+ if (isIdempotent && attempt < maxAttempts - 1) {
40807
+ const backoffMs = Math.floor(Math.random() * Math.min(2e3, 250 * Math.pow(2, attempt)));
40808
+ await abortableDelay(backoffMs, init?.signal);
40809
+ continue;
40810
+ }
40811
+ throw err;
40760
40812
  }
40761
- throw new MrrlinApiError({
40762
- code,
40763
- method,
40764
- path: path33,
40765
- responseBody: body,
40766
- status: response.status,
40767
- violations,
40768
- message
40769
- });
40813
+ }
40814
+ throw new Error(`Failed to execute ${method} ${path33}`);
40815
+ }
40816
+ async function handleResponseError(response, method, path33) {
40817
+ const body = await response.text();
40818
+ let code = `HTTP_${response.status}`;
40819
+ let violations = null;
40820
+ let message = `Mrrlin API ${method} ${path33} failed: ${response.status}`;
40821
+ try {
40822
+ const parsed = JSON.parse(body);
40823
+ const violationsParsed = apiViolationsErrorSchema.safeParse(parsed);
40824
+ if (violationsParsed.success) {
40825
+ code = violationsParsed.data.code;
40826
+ violations = violationsParsed.data.violations;
40827
+ message = violationsParsed.data.message;
40828
+ } else {
40829
+ const errorParsed = apiErrorSchema.safeParse(parsed);
40830
+ if (errorParsed.success) {
40831
+ code = errorParsed.data.code;
40832
+ message = errorParsed.data.message;
40833
+ }
40834
+ }
40835
+ } catch {
40836
+ }
40837
+ throw new MrrlinApiError({
40838
+ code,
40839
+ method,
40840
+ path: path33,
40841
+ responseBody: body,
40842
+ status: response.status,
40843
+ violations,
40844
+ message
40845
+ });
40846
+ }
40847
+ async function request(path33, init) {
40848
+ const method = init?.method ?? "GET";
40849
+ const response = await executeFetch(path33, init);
40850
+ if (!response.ok) {
40851
+ await handleResponseError(response, method, path33);
40770
40852
  }
40771
40853
  return response.json();
40772
40854
  }
40773
40855
  async function requestText(path33, init) {
40774
40856
  const method = init?.method ?? "GET";
40775
- const headers = new Headers(init?.headers ?? {});
40776
- if (token && !headers.has("authorization")) {
40777
- headers.set("authorization", `Bearer ${token}`);
40778
- }
40779
- const response = await customFetch(`${baseUrl}${path33}`, { ...init, headers });
40857
+ const response = await executeFetch(path33, init);
40780
40858
  if (!response.ok) {
40781
- const body = await response.text();
40782
- let code = `HTTP_${response.status}`;
40783
- let violations = null;
40784
- let message = `Mrrlin API ${method} ${path33} failed: ${response.status}`;
40785
- try {
40786
- const parsed = JSON.parse(body);
40787
- const violationsParsed = apiViolationsErrorSchema.safeParse(parsed);
40788
- if (violationsParsed.success) {
40789
- code = violationsParsed.data.code;
40790
- violations = violationsParsed.data.violations;
40791
- message = violationsParsed.data.message;
40792
- } else {
40793
- const errorParsed = apiErrorSchema.safeParse(parsed);
40794
- if (errorParsed.success) {
40795
- code = errorParsed.data.code;
40796
- message = errorParsed.data.message;
40797
- }
40798
- }
40799
- } catch {
40800
- }
40801
- throw new MrrlinApiError({
40802
- code,
40803
- method,
40804
- path: path33,
40805
- responseBody: body,
40806
- status: response.status,
40807
- violations,
40808
- message
40809
- });
40859
+ await handleResponseError(response, method, path33);
40810
40860
  }
40811
40861
  return response.text();
40812
40862
  }
@@ -41968,6 +42018,48 @@ function createMrrlinClient(config2) {
41968
42018
  var import_promises3 = __toESM(require("node:fs/promises"), 1);
41969
42019
  var import_node_path5 = __toESM(require("node:path"), 1);
41970
42020
  var INCLUDED_DOC_FOLDERS = /* @__PURE__ */ new Set(["_meta", "explanation", "how-to", "reference", "sdd", "tutorials"]);
42021
+ var cachedPreparedIndex = null;
42022
+ var preparedIndexMap = /* @__PURE__ */ new WeakMap();
42023
+ function prepareWikiChunks(chunks) {
42024
+ const documents = chunks.map((chunk) => tokenize(`${chunk.heading_path.join(" ")} ${chunk.chunk_text}`));
42025
+ const averageLength = documents.reduce((sum, tokens2) => sum + tokens2.length, 0) / Math.max(documents.length, 1);
42026
+ const documentFrequency = /* @__PURE__ */ new Map();
42027
+ const termFrequencies = documents.map((tokens2) => {
42028
+ const freqs = /* @__PURE__ */ new Map();
42029
+ for (const token of tokens2) {
42030
+ freqs.set(token, (freqs.get(token) ?? 0) + 1);
42031
+ }
42032
+ for (const token of freqs.keys()) {
42033
+ documentFrequency.set(token, (documentFrequency.get(token) ?? 0) + 1);
42034
+ }
42035
+ return freqs;
42036
+ });
42037
+ return {
42038
+ chunks,
42039
+ documents,
42040
+ termFrequencies,
42041
+ documentFrequency,
42042
+ averageLength
42043
+ };
42044
+ }
42045
+ async function getOrPrepareWikiIndex(indexPath) {
42046
+ const resolvedIndexPath = await resolveIndexPath(indexPath);
42047
+ const stat3 = await import_promises3.default.stat(resolvedIndexPath);
42048
+ if (cachedPreparedIndex && cachedPreparedIndex.filePath === resolvedIndexPath && cachedPreparedIndex.mtimeMs === stat3.mtimeMs && cachedPreparedIndex.size === stat3.size) {
42049
+ return cachedPreparedIndex;
42050
+ }
42051
+ const raw = await import_promises3.default.readFile(resolvedIndexPath, "utf8");
42052
+ const chunks = wikiKnowledgeIndexSchema.parse(JSON.parse(raw));
42053
+ const prepared = {
42054
+ ...prepareWikiChunks(chunks),
42055
+ filePath: resolvedIndexPath,
42056
+ mtimeMs: stat3.mtimeMs,
42057
+ size: stat3.size
42058
+ };
42059
+ cachedPreparedIndex = prepared;
42060
+ preparedIndexMap.set(chunks, prepared);
42061
+ return prepared;
42062
+ }
41971
42063
  function toPosixPath(input) {
41972
42064
  return input.split(import_node_path5.default.sep).join("/");
41973
42065
  }
@@ -42115,50 +42207,51 @@ async function buildWikiKnowledgeIndex(options = {}) {
42115
42207
  const documents = await listWikiDocuments(options);
42116
42208
  return wikiKnowledgeIndexSchema.parse(documents.flatMap(buildChunksForDocument));
42117
42209
  }
42118
- async function loadBuiltWikiKnowledgeIndex(indexPath) {
42119
- const resolvedIndexPath = await resolveIndexPath(indexPath);
42120
- const raw = await import_promises3.default.readFile(resolvedIndexPath, "utf8");
42121
- return wikiKnowledgeIndexSchema.parse(JSON.parse(raw));
42122
- }
42123
42210
  function tokenize(input) {
42124
42211
  return input.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 2);
42125
42212
  }
42126
- function searchKnowledgeIndex(index, query, limit = 5) {
42127
- const queryTokens = tokenize(query);
42128
- if (queryTokens.length === 0)
42129
- return [];
42130
- const documents = index.map((chunk) => tokenize(`${chunk.heading_path.join(" ")} ${chunk.chunk_text}`));
42131
- const averageLength = documents.reduce((sum, tokens2) => sum + tokens2.length, 0) / Math.max(documents.length, 1);
42132
- const documentFrequency = /* @__PURE__ */ new Map();
42133
- for (const tokens2 of documents) {
42134
- for (const token of new Set(tokens2)) {
42135
- documentFrequency.set(token, (documentFrequency.get(token) ?? 0) + 1);
42136
- }
42137
- }
42213
+ function scorePreparedIndex(prepared, queryTokens, limit = 5) {
42138
42214
  const k1 = 1.2;
42139
42215
  const b = 0.75;
42140
- const scored = index.map((chunk, chunkIndex) => {
42141
- const tokens2 = documents[chunkIndex] ?? [];
42142
- const frequencies = /* @__PURE__ */ new Map();
42143
- for (const token of tokens2) {
42144
- frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
42145
- }
42216
+ const { chunks, documents, termFrequencies, documentFrequency, averageLength } = prepared;
42217
+ const totalDocs = documents.length;
42218
+ const scored = chunks.map((chunk, chunkIndex) => {
42219
+ const frequencies = termFrequencies[chunkIndex] ?? /* @__PURE__ */ new Map();
42220
+ const docLen = documents[chunkIndex]?.length ?? 0;
42146
42221
  const score = queryTokens.reduce((sum, token) => {
42147
42222
  const tf = frequencies.get(token) ?? 0;
42148
42223
  if (tf === 0)
42149
42224
  return sum;
42150
42225
  const df = documentFrequency.get(token) ?? 0;
42151
- const idf = Math.log(1 + (documents.length - df + 0.5) / (df + 0.5));
42152
- const denominator = tf + k1 * (1 - b + b * (tokens2.length / Math.max(averageLength, 1)));
42226
+ const idf = Math.log(1 + (totalDocs - df + 0.5) / (df + 0.5));
42227
+ const denominator = tf + k1 * (1 - b + b * (docLen / Math.max(averageLength, 1)));
42153
42228
  return sum + idf * (tf * (k1 + 1) / denominator);
42154
42229
  }, 0);
42155
42230
  return { ...chunk, score };
42156
42231
  });
42157
42232
  return scored.filter((result) => result.score > 0).sort((a, b2) => b2.score - a.score || a.wiki_url.localeCompare(b2.wiki_url)).slice(0, limit).map((result) => wikiKnowledgeSearchResultSchema.parse(result));
42158
42233
  }
42234
+ function searchKnowledgeIndex(index, query, limit = 5) {
42235
+ const queryTokens = tokenize(query);
42236
+ if (queryTokens.length === 0)
42237
+ return [];
42238
+ let prepared = preparedIndexMap.get(index);
42239
+ if (!prepared) {
42240
+ prepared = prepareWikiChunks(index);
42241
+ preparedIndexMap.set(index, prepared);
42242
+ }
42243
+ return scorePreparedIndex(prepared, queryTokens, limit);
42244
+ }
42159
42245
  async function searchKnowledge(query, options = {}) {
42160
- const index = options.docsRoot ? await buildWikiKnowledgeIndex({ docsRoot: options.docsRoot }) : await loadBuiltWikiKnowledgeIndex(options.indexPath);
42161
- return searchKnowledgeIndex(index, query, options.limit ?? 5);
42246
+ if (options.docsRoot) {
42247
+ const index = await buildWikiKnowledgeIndex({ docsRoot: options.docsRoot });
42248
+ return searchKnowledgeIndex(index, query, options.limit ?? 5);
42249
+ }
42250
+ const prepared = await getOrPrepareWikiIndex(options.indexPath);
42251
+ const queryTokens = tokenize(query);
42252
+ if (queryTokens.length === 0)
42253
+ return [];
42254
+ return scorePreparedIndex(prepared, queryTokens, options.limit ?? 5);
42162
42255
  }
42163
42256
 
42164
42257
  // src/consensus/verdict.ts
@@ -62120,7 +62213,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
62120
62213
  }
62121
62214
 
62122
62215
  // src/_generated/version.ts
62123
- var PKG_VERSION = "0.4.123";
62216
+ var PKG_VERSION = "0.4.125";
62124
62217
 
62125
62218
  // src/receipt-signing.ts
62126
62219
  var import_node_crypto13 = __toESM(require("node:crypto"), 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrrlin-dev/mcp",
3
- "version": "0.4.123",
3
+ "version": "0.4.125",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mrrlin-mcp": "dist/bin.cjs"