zam-core 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8352,7 +8352,7 @@ async function prepareRecallChain(db, opts) {
8352
8352
  } else {
8353
8353
  spawnLocalRunner(endpoint.url, endpoint.model, endpoint.runner);
8354
8354
  while (Date.now() < deadline) {
8355
- await new Promise((resolve7) => setTimeout(resolve7, 1e3));
8355
+ await new Promise((resolve8) => setTimeout(resolve8, 1e3));
8356
8356
  if (await isLlmOnline(endpoint.url)) {
8357
8357
  online = true;
8358
8358
  break;
@@ -8645,7 +8645,7 @@ async function startLocalRunner(url, model, locale, hint) {
8645
8645
  let attempts = 0;
8646
8646
  const dotsPerLine = 30;
8647
8647
  while (true) {
8648
- await new Promise((resolve7) => setTimeout(resolve7, 500));
8648
+ await new Promise((resolve8) => setTimeout(resolve8, 500));
8649
8649
  if (await isLlmOnline(url)) {
8650
8650
  if (attempts > 0) process.stdout.write("\n");
8651
8651
  return true;
@@ -8734,8 +8734,8 @@ async function fetchWithInteractiveTimeout(url, options = {}) {
8734
8734
  const dotsPerLine = 30;
8735
8735
  while (true) {
8736
8736
  let timeoutId;
8737
- const timeoutPromise = new Promise((resolve7) => {
8738
- timeoutId = setTimeout(() => resolve7("timeout"), timeoutMs);
8737
+ const timeoutPromise = new Promise((resolve8) => {
8738
+ timeoutId = setTimeout(() => resolve8("timeout"), timeoutMs);
8739
8739
  });
8740
8740
  const dotsInterval = setInterval(() => {
8741
8741
  process.stdout.write(".");
@@ -8989,10 +8989,272 @@ var init_client = __esm({
8989
8989
  }
8990
8990
  });
8991
8991
 
8992
+ // src/cli/okf/bundle.ts
8993
+ var bundle_exports = {};
8994
+ __export(bundle_exports, {
8995
+ OKF_VERSION: () => OKF_VERSION,
8996
+ RESERVED_FILES: () => RESERVED_FILES,
8997
+ appendLog: () => appendLog,
8998
+ buildCatalog: () => buildCatalog,
8999
+ isReservedFile: () => isReservedFile,
9000
+ parseFrontmatter: () => parseFrontmatter,
9001
+ renderIndex: () => renderIndex,
9002
+ toCatalogEntry: () => toCatalogEntry,
9003
+ validateArticle: () => validateArticle
9004
+ });
9005
+ function isReservedFile(file) {
9006
+ return RESERVED_FILES.includes(file);
9007
+ }
9008
+ function unquote(raw) {
9009
+ const v = raw.trim();
9010
+ if (v.length >= 2 && (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'"))) {
9011
+ return v.slice(1, -1);
9012
+ }
9013
+ return v;
9014
+ }
9015
+ function parseFrontmatter(markdown) {
9016
+ const lines = markdown.split("\n");
9017
+ if (lines[0]?.trim() !== "---") {
9018
+ throw new Error("frontmatter: file must start with a --- fence");
9019
+ }
9020
+ const fields = {};
9021
+ let listKey = null;
9022
+ let i = 1;
9023
+ for (; i < lines.length; i++) {
9024
+ const line = lines[i];
9025
+ if (line.trim() === "---") break;
9026
+ if (line.trim() === "") continue;
9027
+ const listItem = /^\s+-\s+(.+)$/.exec(line);
9028
+ if (listItem) {
9029
+ if (!listKey) {
9030
+ throw new Error(`frontmatter line ${i + 1}: list item without a key`);
9031
+ }
9032
+ fields[listKey].push(unquote(listItem[1]));
9033
+ continue;
9034
+ }
9035
+ const pair = /^([A-Za-z0-9_-]+):(.*)$/.exec(line);
9036
+ if (!pair) {
9037
+ throw new Error(
9038
+ `frontmatter line ${i + 1}: expected "key: value" or "- item"`
9039
+ );
9040
+ }
9041
+ const key = pair[1];
9042
+ const rest = pair[2].trim();
9043
+ if (rest === "") {
9044
+ fields[key] = [];
9045
+ listKey = key;
9046
+ } else {
9047
+ fields[key] = unquote(rest);
9048
+ listKey = null;
9049
+ }
9050
+ }
9051
+ if (i >= lines.length) {
9052
+ throw new Error("frontmatter: missing closing --- fence");
9053
+ }
9054
+ return { fields, body: lines.slice(i + 1).join("\n") };
9055
+ }
9056
+ function scalar(fields, key) {
9057
+ const v = fields[key];
9058
+ return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
9059
+ }
9060
+ function validateArticle(file, markdown) {
9061
+ const problems = [];
9062
+ if (isReservedFile(file)) {
9063
+ return { ok: false, problems: [`${file}: reserved OKF file name`] };
9064
+ }
9065
+ if (!FILE_NAME_RE.test(file)) {
9066
+ problems.push(`${file}: file name must be kebab-case and end in .md`);
9067
+ }
9068
+ let parsed = null;
9069
+ try {
9070
+ parsed = parseFrontmatter(markdown);
9071
+ } catch (err) {
9072
+ problems.push(
9073
+ `${file}: ${err instanceof Error ? err.message : String(err)}`
9074
+ );
9075
+ }
9076
+ if (parsed) {
9077
+ if (!scalar(parsed.fields, "type")) {
9078
+ problems.push(`${file}: frontmatter field "type" is required`);
9079
+ }
9080
+ if (!scalar(parsed.fields, "description")) {
9081
+ problems.push(`${file}: frontmatter field "description" is required`);
9082
+ }
9083
+ if (parsed.body.trim() === "") {
9084
+ problems.push(`${file}: article body is empty`);
9085
+ }
9086
+ }
9087
+ return { ok: problems.length === 0, problems };
9088
+ }
9089
+ function toCatalogEntry(file, markdown) {
9090
+ const { fields } = parseFrontmatter(markdown);
9091
+ const tags = Array.isArray(fields.tags) ? fields.tags : [];
9092
+ return {
9093
+ file,
9094
+ type: scalar(fields, "type") ?? "",
9095
+ title: scalar(fields, "title") ?? file.replace(/\.md$/, ""),
9096
+ description: scalar(fields, "description") ?? "",
9097
+ tags,
9098
+ resource: scalar(fields, "resource"),
9099
+ timestamp: scalar(fields, "timestamp")
9100
+ };
9101
+ }
9102
+ function buildCatalog(articles) {
9103
+ return articles.map(({ file, markdown }) => toCatalogEntry(file, markdown)).sort((a, b) => a.file.localeCompare(b.file));
9104
+ }
9105
+ function renderIndex(catalog, okfVersion = OKF_VERSION) {
9106
+ const types = [...new Set(catalog.map((e) => e.type))].sort();
9107
+ const sections = types.map((type) => {
9108
+ const rows = catalog.filter((e) => e.type === type).map((e) => `- [${e.title}](${e.file}) \u2014 ${e.description}`).join("\n");
9109
+ return `## ${type}
9110
+
9111
+ ${rows}`;
9112
+ });
9113
+ return [
9114
+ "---",
9115
+ `okf_version: "${okfVersion}"`,
9116
+ "---",
9117
+ "",
9118
+ "# ZAM Knowledge Base",
9119
+ "",
9120
+ "Living reference knowledge for this repository in",
9121
+ "[Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog).",
9122
+ "Current truth only \u2014 the *why* behind it lives in [../adr/](../adr/)",
9123
+ "(ADR 2026-07-17). Do not edit by hand: write through the",
9124
+ "`zam_okf_upsert` MCP tool.",
9125
+ "",
9126
+ sections.join("\n\n"),
9127
+ ""
9128
+ ].join("\n");
9129
+ }
9130
+ function appendLog(existing, date, line) {
9131
+ const header = `## ${date}`;
9132
+ const entry = `- ${line}`;
9133
+ const trimmed = existing.trim();
9134
+ if (trimmed === "") {
9135
+ return `# Log
9136
+
9137
+ ${header}
9138
+
9139
+ ${entry}
9140
+ `;
9141
+ }
9142
+ const lines = trimmed.split("\n");
9143
+ const firstHeaderIdx = lines.findIndex((l) => l.startsWith("## "));
9144
+ if (firstHeaderIdx !== -1 && lines[firstHeaderIdx].trim() === header) {
9145
+ lines.splice(firstHeaderIdx + 2, 0, entry);
9146
+ return `${lines.join("\n")}
9147
+ `;
9148
+ }
9149
+ const insertAt = firstHeaderIdx === -1 ? lines.length : firstHeaderIdx;
9150
+ lines.splice(insertAt, 0, header, "", entry, "");
9151
+ return `${lines.join("\n")}
9152
+ `;
9153
+ }
9154
+ var OKF_VERSION, RESERVED_FILES, FILE_NAME_RE;
9155
+ var init_bundle = __esm({
9156
+ "src/cli/okf/bundle.ts"() {
9157
+ "use strict";
9158
+ OKF_VERSION = "0.1";
9159
+ RESERVED_FILES = ["index.md", "log.md"];
9160
+ FILE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*\.md$/;
9161
+ }
9162
+ });
9163
+
9164
+ // src/cli/okf/io.ts
9165
+ var io_exports = {};
9166
+ __export(io_exports, {
9167
+ DEFAULT_BUNDLE_DIR: () => DEFAULT_BUNDLE_DIR,
9168
+ loadBundle: () => loadBundle,
9169
+ resolveArticlePath: () => resolveArticlePath,
9170
+ upsertArticle: () => upsertArticle
9171
+ });
9172
+ import { mkdirSync as mkdirSync16, readdirSync as readdirSync3, readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "fs";
9173
+ import { join as join24, resolve as resolve7 } from "path";
9174
+ function resolveArticlePath(dir, file) {
9175
+ if (file.includes("/") || file.includes("\\") || file.includes("..")) {
9176
+ throw new Error(`invalid article file name: ${file}`);
9177
+ }
9178
+ if (isReservedFile(file)) {
9179
+ throw new Error(`refusing to address reserved file: ${file}`);
9180
+ }
9181
+ return join24(resolve7(dir), file);
9182
+ }
9183
+ function loadBundle(dir) {
9184
+ const root = resolve7(dir);
9185
+ let entries;
9186
+ try {
9187
+ entries = readdirSync3(root).filter(
9188
+ (name) => name.endsWith(".md") && !isReservedFile(name)
9189
+ );
9190
+ } catch {
9191
+ throw new Error(`OKF bundle directory not found: ${root}`);
9192
+ }
9193
+ const articles = entries.sort().map((file) => ({
9194
+ file,
9195
+ markdown: readFileSync18(join24(root, file), "utf8")
9196
+ }));
9197
+ const problems = articles.flatMap(
9198
+ ({ file, markdown }) => validateArticle(file, markdown).problems
9199
+ );
9200
+ const catalog = problems.length === 0 ? buildCatalog(articles) : safeCatalog(articles);
9201
+ return { dir: root, articles, catalog, problems };
9202
+ }
9203
+ function safeCatalog(articles) {
9204
+ const entries = [];
9205
+ for (const { file, markdown } of articles) {
9206
+ try {
9207
+ entries.push(toCatalogEntry(file, markdown));
9208
+ } catch {
9209
+ }
9210
+ }
9211
+ return entries.sort((a, b) => a.file.localeCompare(b.file));
9212
+ }
9213
+ function upsertArticle(dir, file, markdown, today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
9214
+ const target = resolveArticlePath(dir, file);
9215
+ const validation = validateArticle(file, markdown);
9216
+ if (!validation.ok) return { validation };
9217
+ const root = resolve7(dir);
9218
+ mkdirSync16(root, { recursive: true });
9219
+ let created = true;
9220
+ try {
9221
+ readFileSync18(target, "utf8");
9222
+ created = false;
9223
+ } catch {
9224
+ }
9225
+ writeFileSync13(target, markdown, "utf8");
9226
+ const bundle = loadBundle(root);
9227
+ writeFileSync13(join24(root, "index.md"), renderIndex(bundle.catalog), "utf8");
9228
+ let log = "";
9229
+ try {
9230
+ log = readFileSync18(join24(root, "log.md"), "utf8");
9231
+ } catch {
9232
+ }
9233
+ const entry = bundle.catalog.find((e) => e.file === file);
9234
+ writeFileSync13(
9235
+ join24(root, "log.md"),
9236
+ appendLog(
9237
+ log,
9238
+ today,
9239
+ `**${created ? "Creation" : "Update"}** \u2014 [${entry?.title ?? file}](${file})`
9240
+ ),
9241
+ "utf8"
9242
+ );
9243
+ return { validation, entry, created };
9244
+ }
9245
+ var DEFAULT_BUNDLE_DIR;
9246
+ var init_io = __esm({
9247
+ "src/cli/okf/io.ts"() {
9248
+ "use strict";
9249
+ init_bundle();
9250
+ DEFAULT_BUNDLE_DIR = "docs/okf";
9251
+ }
9252
+ });
9253
+
8992
9254
  // src/cli/commands/mcp.ts
8993
9255
  init_kernel();
8994
- import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
8995
- import { dirname as dirname11, join as join24 } from "path";
9256
+ import { existsSync as existsSync21, readFileSync as readFileSync19 } from "fs";
9257
+ import { dirname as dirname11, join as join25 } from "path";
8996
9258
  import { fileURLToPath as fileURLToPath6 } from "url";
8997
9259
  import {
8998
9260
  RESOURCE_MIME_TYPE,
@@ -11636,7 +11898,7 @@ async function readWebLink(url) {
11636
11898
  redirect: "manual",
11637
11899
  signal: controller.signal,
11638
11900
  headers: {
11639
- "User-Agent": "ZAM-Content-Studio/0.11.1"
11901
+ "User-Agent": "ZAM-Content-Studio/0.12.0"
11640
11902
  }
11641
11903
  });
11642
11904
  if (res.status >= 300 && res.status < 400) {
@@ -28964,25 +29226,25 @@ async function observeUiSnapshotViaLLM(db, input) {
28964
29226
  const imageUrls = [];
28965
29227
  const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input.imagePath);
28966
29228
  if (isVideo) {
28967
- const { mkdirSync: mkdirSync16, readdirSync: readdirSync3, rmSync: rmSync4 } = await import("fs");
29229
+ const { mkdirSync: mkdirSync17, readdirSync: readdirSync4, rmSync: rmSync4 } = await import("fs");
28968
29230
  const { execSync: execSync5 } = await import("child_process");
28969
29231
  const tempDir = join20(
28970
29232
  tmpdir2(),
28971
29233
  `zam-frames-${randomBytes(4).toString("hex")}`
28972
29234
  );
28973
- mkdirSync16(tempDir, { recursive: true });
29235
+ mkdirSync17(tempDir, { recursive: true });
28974
29236
  try {
28975
29237
  execSync5(
28976
29238
  `ffmpeg -i "${input.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
28977
29239
  { stdio: "ignore" }
28978
29240
  );
28979
- let files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
29241
+ let files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
28980
29242
  if (files.length === 0) {
28981
29243
  execSync5(
28982
29244
  `ffmpeg -i "${input.imagePath}" -vframes 1 "${tempDir}/frame_001.png"`,
28983
29245
  { stdio: "ignore" }
28984
29246
  );
28985
- files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
29247
+ files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
28986
29248
  }
28987
29249
  const maxFrames = cfg.maxFrames ?? 100;
28988
29250
  let sampledFiles = files;
@@ -30801,7 +31063,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
30801
31063
  const statePath = join22(tmpdir3(), `zam-recording-${sessionId}.json`);
30802
31064
  const defaultExt = platform === "win32" ? ".mkv" : ".mov";
30803
31065
  const outputPath = opts.output ?? join22(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
30804
- const { existsSync: existsSync22, writeFileSync: writeFileSync13, openSync, closeSync } = await import("fs");
31066
+ const { existsSync: existsSync22, writeFileSync: writeFileSync14, openSync, closeSync } = await import("fs");
30805
31067
  if (existsSync22(statePath)) {
30806
31068
  jsonOut({
30807
31069
  sessionId,
@@ -30856,7 +31118,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
30856
31118
  }
30857
31119
  child.unref();
30858
31120
  if (child.pid) {
30859
- writeFileSync13(
31121
+ writeFileSync14(
30860
31122
  statePath,
30861
31123
  JSON.stringify({
30862
31124
  pid: child.pid,
@@ -30893,7 +31155,7 @@ bridgeCommand.command("stop-recording").description(
30893
31155
  }
30894
31156
  const sessionId = opts.session;
30895
31157
  const statePath = join22(tmpdir3(), `zam-recording-${sessionId}.json`);
30896
- const { existsSync: existsSync22, readFileSync: readFileSync19, rmSync: rmSync4 } = await import("fs");
31158
+ const { existsSync: existsSync22, readFileSync: readFileSync20, rmSync: rmSync4 } = await import("fs");
30897
31159
  if (!existsSync22(statePath)) {
30898
31160
  jsonOut({
30899
31161
  sessionId,
@@ -30902,7 +31164,7 @@ bridgeCommand.command("stop-recording").description(
30902
31164
  });
30903
31165
  return;
30904
31166
  }
30905
- const state = JSON.parse(readFileSync19(statePath, "utf8"));
31167
+ const state = JSON.parse(readFileSync20(statePath, "utf8"));
30906
31168
  const { pid, outputPath } = state;
30907
31169
  try {
30908
31170
  process.kill(pid, "SIGINT");
@@ -30918,7 +31180,7 @@ bridgeCommand.command("stop-recording").description(
30918
31180
  };
30919
31181
  let attempts = 0;
30920
31182
  while (isProcessRunning(pid) && attempts < 20) {
30921
- await new Promise((resolve7) => setTimeout(resolve7, 250));
31183
+ await new Promise((resolve8) => setTimeout(resolve8, 250));
30922
31184
  attempts++;
30923
31185
  }
30924
31186
  if (isProcessRunning(pid)) {
@@ -33556,11 +33818,11 @@ async function publishUiIntent(app, input = {}, opts = {}) {
33556
33818
 
33557
33819
  // src/cli/commands/mcp.ts
33558
33820
  var __dirname = dirname11(fileURLToPath6(import.meta.url));
33559
- var pkgPath = join24(__dirname, "..", "..", "package.json");
33821
+ var pkgPath = join25(__dirname, "..", "..", "package.json");
33560
33822
  if (!existsSync21(pkgPath)) {
33561
- pkgPath = join24(__dirname, "..", "..", "..", "package.json");
33823
+ pkgPath = join25(__dirname, "..", "..", "..", "package.json");
33562
33824
  }
33563
- var pkg = JSON.parse(readFileSync18(pkgPath, "utf-8"));
33825
+ var pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
33564
33826
  var STUDIO_RESOURCE_URI = "ui://zam/studio";
33565
33827
  var RECALL_RESOURCE_URI = "ui://zam/recall";
33566
33828
  var GRAPH_RESOURCE_URI = "ui://zam/graph";
@@ -33587,13 +33849,13 @@ var STUDIO_BRIDGE_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
33587
33849
  function loadPanelHtml(fileName, placeholderTitle) {
33588
33850
  const candidates = [
33589
33851
  // dist/cli/commands/mcp.js → dist/ui/
33590
- join24(__dirname, "..", "..", "ui", fileName),
33852
+ join25(__dirname, "..", "..", "ui", fileName),
33591
33853
  // src/cli/commands/mcp.ts via tsx → <repo>/dist/ui/
33592
- join24(__dirname, "..", "..", "..", "dist", "ui", fileName)
33854
+ join25(__dirname, "..", "..", "..", "dist", "ui", fileName)
33593
33855
  ];
33594
33856
  for (const candidate of candidates) {
33595
33857
  if (existsSync21(candidate)) {
33596
- return readFileSync18(candidate, "utf-8");
33858
+ return readFileSync19(candidate, "utf-8");
33597
33859
  }
33598
33860
  }
33599
33861
  return `<!doctype html>
@@ -34358,6 +34620,87 @@ function createMcpServer(db) {
34358
34620
  }
34359
34621
  )
34360
34622
  );
34623
+ const okfBundleDirSchema = z.string().optional().describe("Bundle directory (default docs/okf under the server cwd)");
34624
+ server.registerTool(
34625
+ "zam_okf_catalog",
34626
+ {
34627
+ description: "List the OKF knowledge-base articles (type, title, description, tags, resource URL) plus conformance problems, if any",
34628
+ inputSchema: {
34629
+ bundle_dir: okfBundleDirSchema
34630
+ },
34631
+ annotations: {
34632
+ ...commonAnnotations,
34633
+ readOnlyHint: true
34634
+ }
34635
+ },
34636
+ wrapHandler(async (params) => {
34637
+ const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, loadBundle: loadBundle2 } = await Promise.resolve().then(() => (init_io(), io_exports));
34638
+ const bundle = loadBundle2(params.bundle_dir ?? DEFAULT_BUNDLE_DIR2);
34639
+ return {
34640
+ dir: bundle.dir,
34641
+ articles: bundle.catalog,
34642
+ problems: bundle.problems
34643
+ };
34644
+ })
34645
+ );
34646
+ server.registerTool(
34647
+ "zam_okf_read",
34648
+ {
34649
+ description: "Read one OKF knowledge-base article: raw markdown plus parsed frontmatter",
34650
+ inputSchema: {
34651
+ bundle_dir: okfBundleDirSchema,
34652
+ file: z.string().describe("Article file name, e.g. fsrs-scheduling.md")
34653
+ },
34654
+ annotations: {
34655
+ ...commonAnnotations,
34656
+ readOnlyHint: true
34657
+ }
34658
+ },
34659
+ wrapHandler(async (params) => {
34660
+ const { readFileSync: readFileSync20 } = await import("fs");
34661
+ const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, resolveArticlePath: resolveArticlePath2 } = await Promise.resolve().then(() => (init_io(), io_exports));
34662
+ const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
34663
+ const path = resolveArticlePath2(
34664
+ params.bundle_dir ?? DEFAULT_BUNDLE_DIR2,
34665
+ params.file
34666
+ );
34667
+ const markdown = readFileSync20(path, "utf8");
34668
+ const { fields } = parseFrontmatter2(markdown);
34669
+ return { file: params.file, frontmatter: fields, markdown };
34670
+ })
34671
+ );
34672
+ server.registerTool(
34673
+ "zam_okf_upsert",
34674
+ {
34675
+ description: "Create or update an OKF knowledge-base article through the validated write path: checks the frontmatter contract, regenerates index.md, and appends the log.md entry. Never edit bundle files directly.",
34676
+ inputSchema: {
34677
+ bundle_dir: okfBundleDirSchema,
34678
+ file: z.string().describe(
34679
+ "Kebab-case article file name ending in .md (permanent ID)"
34680
+ ),
34681
+ markdown: z.string().describe(
34682
+ "Full article: --- frontmatter (type, title, description, tags, resource, timestamp) then the body"
34683
+ )
34684
+ },
34685
+ annotations: {
34686
+ ...commonAnnotations
34687
+ }
34688
+ },
34689
+ wrapHandler(
34690
+ async (params) => {
34691
+ const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, upsertArticle: upsertArticle2 } = await Promise.resolve().then(() => (init_io(), io_exports));
34692
+ const result = upsertArticle2(
34693
+ params.bundle_dir ?? DEFAULT_BUNDLE_DIR2,
34694
+ params.file,
34695
+ params.markdown
34696
+ );
34697
+ if (!result.validation.ok) {
34698
+ return { ok: false, problems: result.validation.problems };
34699
+ }
34700
+ return { ok: true, created: result.created, entry: result.entry };
34701
+ }
34702
+ )
34703
+ );
34361
34704
  return server;
34362
34705
  }
34363
34706
  async function runMcpServer() {