ai-advisory-board 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # CHANGELOG — AI Advisory Board CLI
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - cc1521a: Knowledge & discussion UI improvements:
8
+
9
+ - **Multi-source ingest** — the web UI Ingest panel now accepts multiple URLs (one per line) plus native file and folder pickers alongside pasted text. Picked files upload their content to the local server (browsers never expose absolute paths), are filtered to ingestible text/doc types with a 20 MB cap, and are ingested sequentially with per-item progress. New `ingestFileBuffer()` core function and a `file: { name, contentBase64 }` variant on `POST /api/knowledge/ingest`.
10
+ - **Markdown rendering in board-member answers** — member responses now render markdown (bold, headings, lists, inline code, code fences, blockquotes, and `[[wikilinks]]`) instead of showing raw markers.
11
+ - **Add action steps to the Action Board from a discussion** — each suggested action step now has a "+ Add" button that creates a linked action item (attributed to the advising member) without leaving the discussion view.
12
+
3
13
  ## 0.1.1
4
14
 
5
15
  ### Patch Changes
package/dist/bin/aab.js CHANGED
@@ -5807,10 +5807,26 @@ async function ingestFile(opts) {
5807
5807
  throw new UserError(`ingest: not a file: ${opts.path}`);
5808
5808
  }
5809
5809
  const buf = readFileSync13(opts.path);
5810
+ return ingestFileBuffer({
5811
+ buffer: buf,
5812
+ originalName: basename3(opts.path),
5813
+ workspace: opts.workspace,
5814
+ settings: opts.settings,
5815
+ force: opts.force,
5816
+ hintType: opts.hintType,
5817
+ modelOverride: opts.modelOverride
5818
+ });
5819
+ }
5820
+ async function ingestFileBuffer(opts) {
5821
+ const buf = opts.buffer;
5822
+ if (!buf || buf.length === 0) {
5823
+ throw new UserError("ingest: file is empty.");
5824
+ }
5825
+ const leaf = basename3(opts.originalName.replace(/\\/g, "/")) || "upload";
5810
5826
  const hash = sha256Hex(buf);
5811
5827
  const h6 = hash.slice(0, 6);
5812
- const ext = extname(opts.path).toLowerCase() || ".md";
5813
- const sanitizedName = sanitizeFilename(basename3(opts.path, extname(opts.path)));
5828
+ const ext = extname(leaf).toLowerCase() || ".md";
5829
+ const sanitizedName = sanitizeFilename(basename3(leaf, extname(leaf)));
5814
5830
  const rawFilename = `${h6}-${sanitizedName}${ext}`;
5815
5831
  const p = paths(opts.workspace.root);
5816
5832
  ensureWikiDirs(opts.workspace.root);
@@ -5821,7 +5837,7 @@ async function ingestFile(opts) {
5821
5837
  }
5822
5838
  const rawRelPath = toPosix(relative2(opts.workspace.root, rawPath));
5823
5839
  let inlineBody;
5824
- if ([".md", ".txt", ".json", ".csv", ".yaml", ".yml"].includes(ext)) {
5840
+ if ([".md", ".markdown", ".txt", ".json", ".csv", ".tsv", ".yaml", ".yml"].includes(ext)) {
5825
5841
  try {
5826
5842
  inlineBody = buf.toString("utf8");
5827
5843
  } catch {
@@ -5835,7 +5851,7 @@ async function ingestFile(opts) {
5835
5851
  rawRelPath,
5836
5852
  sourceType: "file",
5837
5853
  hash,
5838
- originalName: basename3(opts.path),
5854
+ originalName: opts.originalName,
5839
5855
  hintType: opts.hintType,
5840
5856
  inlineBody,
5841
5857
  force: opts.force,
@@ -11973,7 +11989,12 @@ async function startUiServer(opts) {
11973
11989
  const projectRoot = opts.projectRoot ?? process.cwd();
11974
11990
  const guiDir = resolveGuiDir();
11975
11991
  const app = express();
11976
- app.use(express.json({ limit: "256kb" }));
11992
+ const jsonStd = express.json({ limit: "256kb" });
11993
+ const jsonLarge = express.json({ limit: "64mb" });
11994
+ app.use((req, res, next) => {
11995
+ if (req.method === "POST" && req.path === "/api/knowledge/ingest") return jsonLarge(req, res, next);
11996
+ return jsonStd(req, res, next);
11997
+ });
11977
11998
  const sockets = /* @__PURE__ */ new Set();
11978
11999
  const broadcast = (evt) => {
11979
12000
  const data = JSON.stringify(evt);
@@ -13214,16 +13235,27 @@ async function startUiServer(opts) {
13214
13235
  const body = req.body ?? {};
13215
13236
  const workspace = resolveWorkspaceForWiki();
13216
13237
  const settings = await opts.storage.loadSettings();
13217
- broadcast({ type: "wiki_ingest_started", sourceType: body.url ? "url" : body.path ? "file" : "pasted" });
13238
+ const sourceType = body.url ? "url" : body.path || body.file ? "file" : "pasted";
13239
+ broadcast({ type: "wiki_ingest_started", sourceType });
13218
13240
  let result;
13219
13241
  if (body.paste) {
13220
13242
  result = await ingestPaste({ text: body.paste, workspace, settings, force: body.force, hintType: body.type });
13221
13243
  } else if (body.url) {
13222
13244
  result = await ingestUrl({ url: body.url, workspace, settings, force: body.force, hintType: body.type });
13245
+ } else if (body.file?.contentBase64) {
13246
+ const buffer = Buffer.from(body.file.contentBase64, "base64");
13247
+ result = await ingestFileBuffer({
13248
+ buffer,
13249
+ originalName: body.file.name?.trim() || "upload",
13250
+ workspace,
13251
+ settings,
13252
+ force: body.force,
13253
+ hintType: body.type
13254
+ });
13223
13255
  } else if (body.path) {
13224
13256
  result = await ingestFile({ path: body.path, workspace, settings, force: body.force, hintType: body.type });
13225
13257
  } else {
13226
- res.status(400).json({ error: "Provide paste, url, or path." });
13258
+ res.status(400).json({ error: "Provide paste, url, file, or path." });
13227
13259
  return;
13228
13260
  }
13229
13261
  for (const page of result.producedPages) broadcast({ type: "wiki_ingest_page_written", path: page, action: "created" });