@stll/folio-cli 0.5.0 → 0.5.2

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.js CHANGED
@@ -1,17 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import { runFolioCli } from "./cli.js";
3
+ import { panic } from "better-result";
4
+ import { createTextReader, encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
3
5
  //#region src/bin.ts
4
6
  const readStdin = async () => {
7
+ const reader = createTextReader({ ignoreBOM: true });
5
8
  const chunks = [];
6
- for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
7
- return Buffer.concat(chunks).toString("utf8");
9
+ for await (const chunk of process.stdin) {
10
+ if (!(chunk instanceof Uint8Array)) panic("Standard input must provide byte chunks");
11
+ chunks.push(reader.read(chunk));
12
+ }
13
+ return chunks.join("") + reader.finish();
8
14
  };
9
15
  process.exitCode = await runFolioCli(process.argv.slice(2), {
10
16
  stdout: (text) => {
11
- process.stdout.write(text);
17
+ process.stdout.write(encodeUtf8(text));
12
18
  },
13
19
  stderr: (text) => {
14
- process.stderr.write(text);
20
+ process.stderr.write(encodeUtf8(text));
15
21
  },
16
22
  readStdin,
17
23
  isTTY: process.stdout.isTTY,
@@ -3,6 +3,7 @@ import { fileVersionOf } from "./file-system.js";
3
3
  import { DEFAULT_LEASE_OWNER, acquireLease, publicLockHolder, readLease } from "./lock.js";
4
4
  import { readSidecarFile, writeNewSidecarFile } from "./sidecar.js";
5
5
  import { Result } from "better-result";
6
+ import { decodeText, encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
6
7
  import { watch } from "node:fs";
7
8
  import { readFile, readdir, rename, rm } from "node:fs/promises";
8
9
  import path from "node:path";
@@ -103,7 +104,7 @@ const pendingFlushRequests = async (documentPath, now = /* @__PURE__ */ new Date
103
104
  const requestPath = path.join(directory, name);
104
105
  const bytes = await readSidecarFile(requestPath);
105
106
  if (bytes.isErr()) continue;
106
- const request = parseRequest(new TextDecoder().decode(bytes.value));
107
+ const request = parseRequest(decodeText(bytes.value));
107
108
  if (request === null) continue;
108
109
  if (Date.parse(request.deadline) > now.getTime()) pending.push(request);
109
110
  else await rm(requestPath, { force: true });
@@ -113,7 +114,7 @@ const pendingFlushRequests = async (documentPath, now = /* @__PURE__ */ new Date
113
114
  const placeRequest = async (documentPath, request) => {
114
115
  const target = flushRequestPathFor(documentPath, request.id);
115
116
  const temporary = path.join(path.dirname(documentPath), `.${path.basename(documentPath)}.folio-flush.${request.id}.tmp`);
116
- const written = await writeNewSidecarFile(temporary, new TextEncoder().encode(`${JSON.stringify(request)}\n`));
117
+ const written = await writeNewSidecarFile(temporary, encodeUtf8(`${JSON.stringify(request)}\n`));
117
118
  if (written.isErr()) return Result.err(written.error);
118
119
  if ((await Result.tryPromise(() => rename(temporary, target))).isOk()) return Result.ok();
119
120
  await rm(temporary, { force: true });
@@ -4,6 +4,7 @@ import { MAX_READ_BLOCKS } from "./registry.js";
4
4
  import { Result, panic } from "better-result";
5
5
  import { executeFolioToolCallUntyped } from "@stll/folio-agents/execute";
6
6
  import { FOLIO_AGENT_TOOL_NAMES } from "@stll/folio-agents/types";
7
+ import { base64ToBytes, bytesToBase64Url, decodeText, encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
7
8
  import { createReviewerBridge } from "@stll/folio-agents/bridges/reviewer";
8
9
  import { compareDocxVersions, formatVersionDiffForLLM } from "@stll/folio-agents/compare";
9
10
  //#region src/execute-read.ts
@@ -36,12 +37,12 @@ const labelBlocks = (blocks, sources, cells = /* @__PURE__ */ new Map()) => bloc
36
37
  ...cell !== void 0 && { tableCell: cell }
37
38
  };
38
39
  });
39
- const encodeReadCursor = ({ fileVersion, afterBlockId }) => Buffer.from(JSON.stringify({
40
+ const encodeReadCursor = ({ fileVersion, afterBlockId }) => bytesToBase64Url(encodeUtf8(JSON.stringify({
40
41
  v: fileVersion,
41
42
  a: afterBlockId
42
- }), "utf8").toString("base64url");
43
+ })));
43
44
  const decodeReadCursor = (cursor) => {
44
- const parsed = Result.try(() => JSON.parse(Buffer.from(cursor, "base64url").toString("utf8")));
45
+ const parsed = Result.try(() => JSON.parse(decodeText(base64ToBytes(cursor), { ignoreBOM: true })));
45
46
  if (parsed.isErr() || !isRecord(parsed.value)) return null;
46
47
  const { v, a } = parsed.value;
47
48
  return typeof v === "string" && typeof a === "string" ? {
@@ -54,7 +55,7 @@ const invalidInput = (message, hint) => cliError({
54
55
  message,
55
56
  hint
56
57
  });
57
- const byteLength = (value) => Buffer.byteLength(JSON.stringify(value) ?? "", "utf8");
58
+ const byteLength = (value) => encodeUtf8(JSON.stringify(value) ?? "").byteLength;
58
59
  /** Room kept in a paged response for its envelope fields and `nextCursor`. */
59
60
  const PAGE_ENVELOPE_RESERVE_BYTES = 512;
60
61
  /**
package/dist/flags.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { FOLIO_CLI_ERROR_CODES, cliError } from "./errors.js";
2
2
  import { Result, panic } from "better-result";
3
+ import { decodeText, encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
3
4
  import { readFile } from "node:fs/promises";
4
5
  //#region src/flags.ts
5
6
  /**
@@ -82,13 +83,13 @@ const readInputSource = async ({ source, readStdin }) => {
82
83
  else if (source.startsWith("@")) {
83
84
  const inputPath = source.slice(1);
84
85
  const read = await Result.tryPromise({
85
- try: () => readFile(inputPath, "utf8"),
86
+ try: () => readFile(inputPath).then((bytes) => decodeText(bytes, { ignoreBOM: true })),
86
87
  catch: () => usage(`Cannot read --input file ${inputPath}.`)
87
88
  });
88
89
  if (read.isErr()) return Result.err(read.error);
89
90
  text = read.value;
90
91
  }
91
- if (Buffer.byteLength(text, "utf8") > 16777216) return Result.err(usage(`--input is over the ${MAX_INPUT_BYTES}-byte limit.`));
92
+ if (encodeUtf8(text).byteLength > 16777216) return Result.err(usage(`--input is over the ${MAX_INPUT_BYTES}-byte limit.`));
92
93
  const parsed = Result.try(() => JSON.parse(text));
93
94
  if (parsed.isErr()) return Result.err(usage("--input is not valid JSON.", "Pass inline JSON, @file.json, or - for stdin."));
94
95
  const value = parsed.value;
package/dist/journal.js CHANGED
@@ -3,6 +3,7 @@ import { errnoCode, fileVersionOf, sameFile } from "./file-system.js";
3
3
  import { checkPackageIntegrity } from "./package-parts.js";
4
4
  import { SIDECAR_DIRECTORY, ensureSidecarParent, inspectPath, openSidecarFile, readSidecarFile, syncDirectory } from "./sidecar.js";
5
5
  import { Result } from "better-result";
6
+ import { decodeText, encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
6
7
  import { constants } from "node:fs";
7
8
  import { readdir, rename, rm } from "node:fs/promises";
8
9
  import path from "node:path";
@@ -42,7 +43,7 @@ const appendJournal = async (journalPath, entry) => {
42
43
  const { handle } = opened.value;
43
44
  const written = await Result.tryPromise(async () => {
44
45
  try {
45
- await handle.writeFile(`${JSON.stringify(entry)}\n`);
46
+ await handle.writeFile(encodeUtf8(`${JSON.stringify(entry)}\n`));
46
47
  await handle.sync();
47
48
  } finally {
48
49
  await handle.close();
@@ -90,7 +91,7 @@ const findCommit = async (journalPath, txId) => {
90
91
  const bytes = await readSidecarFile(journalPath);
91
92
  if (bytes.isErr()) return Result.err(bytes.error);
92
93
  let found;
93
- for (const line of new TextDecoder().decode(bytes.value).split("\n")) {
94
+ for (const line of decodeText(bytes.value).split("\n")) {
94
95
  if (!line.includes(txId)) continue;
95
96
  if (isDiscardRecord(line, txId)) return Result.ok(void 0);
96
97
  const commit = parseCommit(line);
@@ -121,7 +122,7 @@ const latestCommitFor = async (documentPath, journalPath) => {
121
122
  if (parent.isErr() || parent.value.type !== "directory") return null;
122
123
  const bytes = await readSidecarFile(journal);
123
124
  if (bytes.isErr()) return null;
124
- const lines = new TextDecoder().decode(bytes.value).split("\n");
125
+ const lines = decodeText(bytes.value).split("\n");
125
126
  const discarded = /* @__PURE__ */ new Set();
126
127
  for (let index = lines.length - 1; index >= 0; index--) {
127
128
  const parsed = Result.try(() => JSON.parse(lines[index] ?? ""));
package/dist/lock.js CHANGED
@@ -2,6 +2,7 @@ import { FOLIO_CLI_ERROR_CODES, cliError } from "./errors.js";
2
2
  import { errnoCode } from "./file-system.js";
3
3
  import { inspectPath, readSidecarFile, writeNewSidecarFile } from "./sidecar.js";
4
4
  import { Result } from "better-result";
5
+ import { decodeText, encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
5
6
  import { link, rename, rm } from "node:fs/promises";
6
7
  import path from "node:path";
7
8
  import { randomUUID } from "node:crypto";
@@ -90,7 +91,7 @@ const readLease = async (documentPath) => {
90
91
  if (entry.value.type === "missing") return { type: "free" };
91
92
  const modifiedMs = entry.value.type === "file" ? entry.value.modifiedMs : Date.now();
92
93
  const bytes = await readSidecarFile(lockPath);
93
- const holder = bytes.isOk() ? parseHolder(new TextDecoder().decode(bytes.value)) : null;
94
+ const holder = bytes.isOk() ? parseHolder(decodeText(bytes.value)) : null;
94
95
  return holder === null ? {
95
96
  type: "unreadable",
96
97
  modifiedMs
@@ -132,7 +133,7 @@ const placeLock = async (lockPath, contents, mode) => {
132
133
  await rm(temporary, { force: true });
133
134
  }
134
135
  };
135
- const encodeHolder = (holder) => new TextEncoder().encode(`${JSON.stringify(holder)}\n`);
136
+ const encodeHolder = (holder) => encodeUtf8(`${JSON.stringify(holder)}\n`);
136
137
  /** How long a swap lock may stand before another process breaks it (its holder crashed). */
137
138
  const SWAP_STALE_MS = 10 * 1e3;
138
139
  /** How long to wait for another process's swap before refusing. */
@@ -152,7 +153,7 @@ const lockSwapPathFor = (documentPath) => `${lockPathFor(documentPath)}.swap`;
152
153
  */
153
154
  const underSwapLock = async (documentPath, change) => {
154
155
  const swapPath = lockSwapPathFor(documentPath);
155
- const marker = new TextEncoder().encode(`${process.pid}\n`);
156
+ const marker = encodeUtf8(`${process.pid}\n`);
156
157
  const deadline = Date.now() + SWAP_WAIT_MS;
157
158
  for (;;) {
158
159
  const taken = await placeLock(swapPath, marker, "create");
@@ -1,5 +1,6 @@
1
1
  import { FOLIO_CLI_ERROR_CODES, cliError } from "./errors.js";
2
2
  import { Result } from "better-result";
3
+ import { createTextReader } from "@stll/folio-core/utils/textEncoding";
3
4
  import path from "node:path";
4
5
  import { XMLParser, XMLValidator } from "fast-xml-parser";
5
6
  import JSZip from "jszip";
@@ -121,23 +122,23 @@ const childrenNamed = (element, namespace, localName) => element.children.filter
121
122
  const partKey = (name) => name.toLowerCase();
122
123
  /** Inflate at most `limit` bytes of an entry; `truncated` when it holds more. */
123
124
  const inflatePrefix = (entry, limit) => new Promise((resolve, reject) => {
124
- const decoder = new TextDecoder();
125
+ const decoder = createTextReader();
125
126
  let text = "";
126
127
  let size = 0;
127
128
  const stream = entry.internalStream("uint8array");
128
129
  stream.on("data", (chunk) => {
129
130
  const room = limit - size;
130
131
  size += chunk.length;
131
- text += decoder.decode(chunk.subarray(0, Math.max(0, room)), { stream: true });
132
+ text += decoder.read(chunk.subarray(0, Math.max(0, room)));
132
133
  if (size > limit) {
133
134
  stream.pause();
134
135
  resolve({
135
- text: text + decoder.decode(),
136
+ text: text + decoder.finishPrefix(),
136
137
  truncated: true
137
138
  });
138
139
  }
139
140
  }).on("error", reject).on("end", () => resolve({
140
- text: text + decoder.decode(),
141
+ text: text + decoder.finish(),
141
142
  truncated: false
142
143
  })).resume();
143
144
  });
@@ -1,3 +1,4 @@
1
+ import { encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
1
2
  //#region src/mcp-kit/schema.ts
2
3
  const ANNOTATION_KEYWORDS = /* @__PURE__ */ new Set([
3
4
  "$comment",
@@ -92,6 +93,6 @@ const hoistRepeatedSchemas = (schema, { minBytes = 200 } = {}) => {
92
93
  } : schema;
93
94
  };
94
95
  /** The UTF-8 size of what `tools/list` sends for these tools. */
95
- const advertisedBytes = (tools) => new TextEncoder().encode(JSON.stringify(tools)).length;
96
+ const advertisedBytes = (tools) => encodeUtf8(JSON.stringify(tools)).length;
96
97
  //#endregion
97
98
  export { advertisedBytes, compactSchema, hoistRepeatedSchemas };
@@ -1,6 +1,8 @@
1
1
  import { KIT_ERROR_CODES, closestNames, didYouMean, failure, success, toCallResult, validationError } from "./envelope.js";
2
2
  import { readToolInput } from "./input.js";
3
3
  import { compactSchema } from "./schema.js";
4
+ import { Result } from "better-result";
5
+ import { base64ToBytes, bytesToBase64Url, decodeText, encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
4
6
  //#region src/mcp-kit/surface.ts
5
7
  /**
6
8
  * A tool surface with lazy discovery. The frequent tools are listed directly
@@ -102,9 +104,11 @@ const compareIds = (a, b) => {
102
104
  if (a === b) return 0;
103
105
  return a < b ? -1 : 1;
104
106
  };
105
- const encodeCursor = (id) => Buffer.from(id, "utf8").toString("base64url");
107
+ const encodeCursor = (id) => bytesToBase64Url(encodeUtf8(id));
106
108
  const decodeCursor = (cursor) => {
107
- const decoded = Buffer.from(cursor, "base64url").toString("utf8");
109
+ const parsed = Result.try(() => decodeText(base64ToBytes(cursor), { ignoreBOM: true }));
110
+ if (parsed.isErr()) return null;
111
+ const decoded = parsed.value;
108
112
  return decoded.length > 0 && encodeCursor(decoded) === cursor ? decoded : null;
109
113
  };
110
114
  const hasJsonType = (value, type) => {
package/dist/output.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { sliceText } from "@stll/folio-core/utils/sliceText";
1
2
  //#region src/output.ts
2
3
  const OUTPUT_FORMATS = ["json", "text"];
3
4
  const isOutputFormat = (value) => OUTPUT_FORMATS.some((format) => format === value);
@@ -43,7 +44,11 @@ const renderResult = (tool, result) => {
43
44
  if (tool === "compare_documents" && isRecord(result) && typeof result["text"] === "string") return [result["text"]];
44
45
  return null;
45
46
  };
46
- const shortVersion = (value) => text(value).slice(0, 12);
47
+ const shortVersion = (value) => sliceText({
48
+ text: text(value),
49
+ units: "codeUnits",
50
+ end: 12
51
+ });
47
52
  /** One line per committed or replayed transaction. */
48
53
  const renderReceipt = (receipt) => {
49
54
  const parts = Array.isArray(receipt["changedParts"]) ? receipt["changedParts"].length : 0;
@@ -1,6 +1,7 @@
1
1
  import { FOLIO_CLI_ERROR_CODES, cliError } from "./errors.js";
2
2
  import { checkWordprocessingPackage } from "./main-document-part.js";
3
3
  import { Result } from "better-result";
4
+ import { decodeText } from "@stll/folio-core/utils/textEncoding";
4
5
  import path from "node:path";
5
6
  import { FolioDocxReviewer } from "@stll/folio-core/server";
6
7
  import { XMLParser, XMLValidator } from "fast-xml-parser";
@@ -73,7 +74,7 @@ const nextRevisionIdSeed = async (bytes) => {
73
74
  let highest = 0;
74
75
  for (const [part, entry] of partEntries(zip.value)) {
75
76
  if (!part.endsWith(".xml")) continue;
76
- const xml = await entry.async("string");
77
+ const xml = await entry.async("uint8array").then((partBytes) => decodeText(partBytes, { ignoreBOM: true }));
77
78
  for (const prefix of prefixesBoundTo(xml, WORDPROCESSINGML_NAMESPACES)) {
78
79
  const pattern = new RegExp(`\\s${escapeRegExp(prefix)}:id\\s*=\\s*"(\\d+)"`, "gu");
79
80
  for (const [, id] of xml.matchAll(pattern)) highest = Math.max(highest, Number(id));
@@ -123,7 +124,7 @@ const integrityError = (message) => cliError({
123
124
  const checkChangedPart = async ({ zip, parts, part }) => {
124
125
  const entry = parts.get(part);
125
126
  if (entry === void 0 || !(part.endsWith(".xml") || part.endsWith(".rels"))) return null;
126
- const xml = await entry.async("string");
127
+ const xml = await entry.async("uint8array").then((partBytes) => decodeText(partBytes, { ignoreBOM: true }));
127
128
  if (XMLValidator.validate(xml) !== true) return `${part} is not well-formed XML.`;
128
129
  if (part.endsWith(".rels")) {
129
130
  const lowerCaseParts = new Set([...parts.keys()].map((name) => name.toLowerCase()));
@@ -136,7 +137,7 @@ const checkChangedPart = async ({ zip, parts, part }) => {
136
137
  }
137
138
  const prefixes = prefixesBoundTo(xml, RELATIONSHIP_NAMESPACES);
138
139
  if (prefixes.size === 0) return null;
139
- const relsXml = await zip.file(relationshipsPartFor(part))?.async("string");
140
+ const relsXml = await zip.file(relationshipsPartFor(part))?.async("uint8array").then((partBytes) => decodeText(partBytes, { ignoreBOM: true }));
140
141
  const ids = new Set(parseRelationships(relsXml ?? "").map(({ id }) => id));
141
142
  for (const prefix of prefixes) {
142
143
  const pattern = new RegExp(`\\s${escapeRegExp(prefix)}:[A-Za-z]+\\s*=\\s*"([^"]*)"`, "gu");
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.5.0";
2
+ var version = "0.5.2";
3
3
  //#endregion
4
4
  export { version };
@@ -6,6 +6,7 @@ import { resolveTransactionDate } from "./provenance.js";
6
6
  import { buildDisplayList, checkPages, displayListHtml, renderPdf, renderPng } from "./render.js";
7
7
  import { startPreviewServer } from "./serve.js";
8
8
  import { Result } from "better-result";
9
+ import { encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
9
10
  import { parseArgs } from "node:util";
10
11
  import path from "node:path";
11
12
  //#region src/preview-commands.ts
@@ -140,7 +141,7 @@ const render = async ({ values, positionals }) => {
140
141
  const list = await buildDisplayList(file.value);
141
142
  const selected = list.isOk() ? checkPages(list.value, pages) : list;
142
143
  output = selected.isOk() ? Result.ok({
143
- bytes: new TextEncoder().encode(displayListHtml(selected.value, path.basename(file.value.path))),
144
+ bytes: encodeUtf8(displayListHtml(selected.value, path.basename(file.value.path))),
144
145
  pageCount: selected.value.pages.length
145
146
  }) : Result.err(selected.error);
146
147
  break;
package/dist/serve.js CHANGED
@@ -4,6 +4,7 @@ import { latestCommitFor } from "./journal.js";
4
4
  import { buildDisplayList, displayListHtml } from "./render.js";
5
5
  import { SIDECAR_DIRECTORY } from "./sidecar.js";
6
6
  import { Result } from "better-result";
7
+ import { bytesToBase64, bytesToBase64Url, encodeUtf8 } from "@stll/folio-core/utils/textEncoding";
7
8
  import { watch } from "node:fs";
8
9
  import { stat } from "node:fs/promises";
9
10
  import path from "node:path";
@@ -85,7 +86,7 @@ const startPreviewServer = async ({ documentPath, port }) => {
85
86
  if (initial.isErr()) return Result.err(initial.error);
86
87
  const realPath = initial.value.path;
87
88
  const title = path.basename(realPath);
88
- const prefix = `/${randomBytes(24).toString("base64url")}/`;
89
+ const prefix = `/${bytesToBase64Url(randomBytes(24))}/`;
89
90
  let stamp = null;
90
91
  let rendered = null;
91
92
  let rendering = null;
@@ -129,7 +130,7 @@ const startPreviewServer = async ({ documentPath, port }) => {
129
130
  rendered = result;
130
131
  stamp = next;
131
132
  if (changed) {
132
- const message = `data: ${JSON.stringify({ fileVersion: result.fileVersion })}\n\n`;
133
+ const message = encodeUtf8(`data: ${JSON.stringify({ fileVersion: result.fileVersion })}\n\n`);
133
134
  for (const listener of listeners) listener.write(message);
134
135
  }
135
136
  return result;
@@ -144,7 +145,7 @@ const startPreviewServer = async ({ documentPath, port }) => {
144
145
  ...SECURITY_HEADERS,
145
146
  ...headers
146
147
  });
147
- response.end(body);
148
+ response.end(encodeUtf8(body));
148
149
  };
149
150
  const handle = async (request, response) => {
150
151
  if (!allowedHosts().has(request.headers.host ?? "")) {
@@ -165,7 +166,7 @@ const startPreviewServer = async ({ documentPath, port }) => {
165
166
  }
166
167
  const route = url.pathname.slice(prefix.length);
167
168
  if (route === "") {
168
- const nonce = randomBytes(16).toString("base64");
169
+ const nonce = bytesToBase64(randomBytes(16));
169
170
  const page = await current();
170
171
  respond(response, 200, shellHtml(title, nonce, page.type === "page" ? page.pageWidthPx : null), {
171
172
  "Content-Type": "text/html; charset=utf-8",
@@ -203,7 +204,7 @@ const startPreviewServer = async ({ documentPath, port }) => {
203
204
  listeners.add(response);
204
205
  request.on("close", () => listeners.delete(response));
205
206
  const page = await current();
206
- response.write(`data: ${JSON.stringify({ fileVersion: page.fileVersion })}\n\n`);
207
+ response.write(encodeUtf8(`data: ${JSON.stringify({ fileVersion: page.fileVersion })}\n\n`));
207
208
  return;
208
209
  }
209
210
  respond(response, 404, "not found", { "Content-Type": "text/plain" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Command line and stdio MCP server for reading, reviewing, and redlining .docx files with folio: tracked-change edits, comments, and comparisons written atomically with version preconditions.",
5
5
  "keywords": [
6
6
  "cli",
@@ -39,15 +39,18 @@
39
39
  "exports": {
40
40
  ".": {
41
41
  "types": "./dist/cli.d.ts",
42
- "import": "./dist/cli.js"
42
+ "import": "./dist/cli.js",
43
+ "default": "./dist/cli.js"
43
44
  },
44
45
  "./editor-lease": {
45
46
  "types": "./dist/editor-lease.d.ts",
46
- "import": "./dist/editor-lease.js"
47
+ "import": "./dist/editor-lease.js",
48
+ "default": "./dist/editor-lease.js"
47
49
  },
48
50
  "./save": {
49
51
  "types": "./dist/save.d.ts",
50
- "import": "./dist/save.js"
52
+ "import": "./dist/save.js",
53
+ "default": "./dist/save.js"
51
54
  }
52
55
  },
53
56
  "publishConfig": {
@@ -71,8 +74,9 @@
71
74
  "@fontsource/tinos": "^5.2.7",
72
75
  "@modelcontextprotocol/server": "2.0.0",
73
76
  "@stll/agent-input": "0.1.0",
74
- "@stll/folio-agents": "^0.16.0",
75
- "@stll/folio-core": "^0.59.0",
77
+ "@stll/folio-agents": "^0.16.2",
78
+ "@stll/folio-core": "^0.61.0",
79
+ "@stll/utf8-boundaries": "0.1.0",
76
80
  "better-result": "3.0.1",
77
81
  "fast-xml-parser": "^5.10.1",
78
82
  "jszip": "3.10.2"
@@ -82,7 +86,21 @@
82
86
  "playwright-core": "1.61.1"
83
87
  },
84
88
  "peerDependencies": {
85
- "playwright-core": ">=1.50.0"
89
+ "playwright-core": ">=1.50.0",
90
+ "prosemirror-commands": "^1.7.1",
91
+ "prosemirror-dropcursor": "^1.8.2",
92
+ "prosemirror-gapcursor": "^1.4.1",
93
+ "prosemirror-history": "^1.5.0",
94
+ "prosemirror-inputrules": "^1.5.0",
95
+ "prosemirror-keymap": "^1.2.3",
96
+ "prosemirror-model": "^1.25.9",
97
+ "prosemirror-state": "^1.4.4",
98
+ "prosemirror-tables": "^1.8.5",
99
+ "prosemirror-transform": "^1.12.0",
100
+ "prosemirror-view": "^1.42.6",
101
+ "y-prosemirror": "^1.3.7",
102
+ "y-protocols": "^1.0.6",
103
+ "yjs": "^13.6.31"
86
104
  },
87
105
  "peerDependenciesMeta": {
88
106
  "playwright-core": {