@hiai-gg/docsmint 0.8.8 → 0.9.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/README.md CHANGED
@@ -42,13 +42,11 @@ instructions, and verify the connection. Hosted MCP follows your plan and
42
42
  workspace permissions. Prefer workspace-bound or category-scoped credentials.
43
43
 
44
44
  The Cloud endpoint is `https://docsmint.com/mcp` (Streamable HTTP). OAuth-capable
45
- clients discover the hosted resource and authorization-server metadata; the current
46
- authorization metadata advertises client registration at
47
- `https://docsmint.com/oauth/register` (DCR). The hosted flow uses authorization-code
48
- PKCE S256; metadata advertises only the `authorization_code` grant and no token
49
- endpoint client authentication. CIMD is not part of the current contract. Browser consent binds the
50
- one-hour opaque access token to the selected workspace, optional category, and
51
- requested scopes. Refresh tokens are not issued. API-key clients can instead send
45
+ clients discover the hosted resource and authorization-server metadata; current
46
+ authorization and client-connection methods are described in the
47
+ [DocsMint Cloud setup guide](https://docsmint.com/mcp/connect?source=github_mcp).
48
+ Browser consent binds the one-hour opaque access token to the selected workspace,
49
+ optional category, and requested scopes. Refresh tokens are not issued. API-key clients can instead send
52
50
  `Authorization: Bearer <key>` with an MCP/API credential created in the authenticated
53
51
  browser UI. Credentials cannot create or elevate other credentials; lifecycle
54
52
  management belongs to the signed-in browser session.
@@ -58,6 +56,8 @@ key in its browser UI, and configure `HIAI_DOCS_URL` and `HIAI_DOCS_API_KEY` for
58
56
  `npx --yes --package @hiai-gg/docsmint docsmint-mcp`. The npm package is a stdio
59
57
  bridge to your own API, not a server installer or hosted OAuth server. See the
60
58
  [MCP guide](https://github.com/HiAi-gg/docsmint/blob/main/packages/mcp-server/README.md) for client configuration.
59
+ The open-source [Glama Server directory](https://glama.ai/mcp/servers) uses this
60
+ stdio bridge; the hosted DocsMint Cloud Connector is a separate listing.
61
61
 
62
62
  ## Why DocsMint?
63
63
 
@@ -74,19 +74,15 @@ bridge to your own API, not a server installer or hosted OAuth server. See the
74
74
  - **Choose how you run it.** Use [managed DocsMint](https://docsmint.com) or
75
75
  self-host the application, database, search, queues, and files.
76
76
 
77
- ## What's new in 0.8.8?
77
+ ## What's new in 0.9.2?
78
78
 
79
- - Clarify that hosted MCP authorization belongs to DocsMint Cloud and that the
80
- stdio bridge uses an API key to connect to your own DocsMint API.
81
- - Synchronize MCP Registry and LobeHub descriptions with the current Cloud
82
- authorization contract and the self-hosted boundary.
83
- - Keep the published catalog aligned with the implementation: 21 tools,
84
- 2 prompts, and 3 resources, with regression checks for route and metadata drift.
85
- - Update vulnerability reporting details and clarify that OAuth protocol flaws
86
- remain in scope for the product where they occur.
79
+ - Prepare the open-source MCP stdio bridge for a separate Glama Server listing
80
+ with a schema-valid organization maintainer claim.
81
+ - Clarify that Glama-hosted stdio needs a reachable self-hosted DocsMint API
82
+ and an API key; DocsMint Cloud remains a separate hosted Connector.
83
+ - Keep the existing 21 tools, 2 prompts, 3 resources, and API-key behavior.
87
84
 
88
- No MCP capabilities, existing integrations, or database schema change in this
89
- release. See the [release notes](https://github.com/HiAi-gg/docsmint/releases/tag/v0.8.8)
85
+ This release adds no database migration. See the [release notes](https://github.com/HiAi-gg/docsmint/releases/tag/v0.9.2)
90
86
  and [changelog](https://github.com/HiAi-gg/docsmint/blob/main/CHANGELOG.md).
91
87
 
92
88
  ## Install with an AI agent
@@ -139,7 +135,7 @@ docker pull vgalibov/docsmint:web-latest
139
135
  docker pull vgalibov/docsmint:caddy-latest
140
136
  ```
141
137
 
142
- Use versioned tags `api-v0.8.8`, `web-v0.8.8`, and `caddy-v0.8.8` for
138
+ Use versioned tags `api-v0.9.2`, `web-v0.9.2`, and `caddy-v0.9.2` for
143
139
  reproducible deploys. Caddy is the supporting reverse proxy with rate limiting;
144
140
  it is separate from the API and web application. The quickstart still builds the Compose stack from this repository so PostgreSQL,
145
141
  Redis, and SeaweedFS start together with the application.
@@ -224,7 +220,7 @@ command and configuration precedence.
224
220
  ## Connect an MCP client
225
221
 
226
222
  Give agents a secure path to search, read, and maintain your knowledge without
227
- database or filesystem access. DocsMint publishes 21 tools plus ready-made
223
+ database or filesystem access. DocsMint publishes MCP tools plus ready-made
228
224
  research prompts, scoped resources, and a document-manager skill.
229
225
 
230
226
  ### Hosted DocsMint
@@ -200640,6 +200640,112 @@ function buildApiAccessValues(input) {
200640
200640
  };
200641
200641
  }
200642
200642
 
200643
+ // ../../backend/src/lib/doc-cache.ts
200644
+ init_logger3();
200645
+ init_redis();
200646
+ var LIST_PREFIX = "hiai-docs:cache:docs:list:";
200647
+ var SINGLE_PREFIX = "hiai-docs:cache:docs:single:";
200648
+ function docListKey(userId, folderId, tag2, page = 1, limit = 20, workspaceId) {
200649
+ const parts = [`${LIST_PREFIX}${userId}`];
200650
+ if (workspaceId)
200651
+ parts.push(`w:${workspaceId}`);
200652
+ if (folderId)
200653
+ parts.push(`f:${folderId}`);
200654
+ if (tag2)
200655
+ parts.push(`t:${tag2}`);
200656
+ parts.push(`p:${page}`, `l:${limit}`);
200657
+ return parts.join(":");
200658
+ }
200659
+ function docSingleKey(docId, userId, workspaceId) {
200660
+ return `${SINGLE_PREFIX}${userId}${workspaceId ? `:w:${workspaceId}` : ""}:${docId}`;
200661
+ }
200662
+ async function cacheGetOrSet(key, ttl, compute, options = {}) {
200663
+ try {
200664
+ const cached2 = await redis.get(key);
200665
+ if (cached2)
200666
+ return JSON.parse(cached2);
200667
+ } catch (err) {
200668
+ logger3.warn({ err, key }, "Redis get failed, falling through to DB");
200669
+ }
200670
+ const value = await compute();
200671
+ if (options.shouldCache && !options.shouldCache(value))
200672
+ return value;
200673
+ try {
200674
+ await redis.set(key, JSON.stringify(value), "EX", ttl);
200675
+ } catch (err) {
200676
+ logger3.warn({ err, key }, "Redis set failed");
200677
+ }
200678
+ return value;
200679
+ }
200680
+ function isHttpCacheEnvelope(value) {
200681
+ return typeof value === "object" && value !== null && "version" in value && value.version === 1 && "kind" in value && value.kind === "http-response" && "status" in value && typeof value.status === "number" && Number.isInteger(value.status) && value.status >= 100 && value.status <= 599 && "body" in value;
200682
+ }
200683
+ async function cacheHttpResponse(key, ttl, compute, options = {}) {
200684
+ try {
200685
+ const cached2 = await redis.get(key);
200686
+ if (cached2) {
200687
+ const parsed = JSON.parse(cached2);
200688
+ if (isHttpCacheEnvelope(parsed)) {
200689
+ return {
200690
+ status: parsed.status,
200691
+ body: parsed.body,
200692
+ cacheHit: true
200693
+ };
200694
+ }
200695
+ }
200696
+ } catch (err) {
200697
+ logger3.warn({ err, key }, "Redis HTTP cache get failed, falling through to DB");
200698
+ }
200699
+ const value = await compute();
200700
+ if (options.shouldCache && !options.shouldCache(value)) {
200701
+ return { ...value, cacheHit: false };
200702
+ }
200703
+ const envelope = {
200704
+ version: 1,
200705
+ kind: "http-response",
200706
+ status: value.status,
200707
+ body: value.body
200708
+ };
200709
+ try {
200710
+ await redis.set(key, JSON.stringify(envelope), "EX", ttl);
200711
+ } catch (err) {
200712
+ logger3.warn({ err, key }, "Redis HTTP cache set failed");
200713
+ }
200714
+ return { ...value, cacheHit: false };
200715
+ }
200716
+ async function invalidateDocListCache(userId, workspaceId) {
200717
+ const patterns = new Set([`${LIST_PREFIX}${userId}:*`]);
200718
+ if (workspaceId)
200719
+ patterns.add(`${LIST_PREFIX}*:w:${workspaceId}:*`);
200720
+ try {
200721
+ for (const pattern of patterns) {
200722
+ let cursor = "0";
200723
+ do {
200724
+ const [newCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", 100);
200725
+ cursor = newCursor;
200726
+ if (keys.length > 0)
200727
+ await redis.del(...keys);
200728
+ } while (cursor !== "0");
200729
+ }
200730
+ } catch (err) {
200731
+ logger3.warn({ err, userId, workspaceId }, "Failed to invalidate doc list cache");
200732
+ }
200733
+ }
200734
+ async function invalidateDocCache(docId) {
200735
+ const pattern = `${SINGLE_PREFIX}*:${docId}:scope:*`;
200736
+ try {
200737
+ let cursor = "0";
200738
+ do {
200739
+ const [newCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", 100);
200740
+ cursor = newCursor;
200741
+ if (keys.length > 0)
200742
+ await redis.del(...keys);
200743
+ } while (cursor !== "0");
200744
+ } catch (err) {
200745
+ logger3.warn({ err, docId }, "Failed to invalidate doc cache");
200746
+ }
200747
+ }
200748
+
200643
200749
  // ../../backend/src/api/routes/categories.ts
200644
200750
  init_logger3();
200645
200751
  var createCategorySchema = exports_external2.object({
@@ -200861,6 +200967,7 @@ var categoryRoutes = new Elysia({ prefix: "/api" }).get("/categories", async ({
200861
200967
  set2.status = 404;
200862
200968
  return { error: "Category not found" };
200863
200969
  }
200970
+ await invalidateDocListCache(ctx.userId, ctx.workspaceId);
200864
200971
  if (parsed.data.name !== undefined) {
200865
200972
  dispatchMetadataReembedOutbox(updated.operationId, metadataReembedPageSize("category"));
200866
200973
  }
@@ -200908,6 +201015,7 @@ var categoryRoutes = new Elysia({ prefix: "/api" }).get("/categories", async ({
200908
201015
  set2.status = 404;
200909
201016
  return { error: "Category not found" };
200910
201017
  }
201018
+ await invalidateDocListCache(ctx.userId, ctx.workspaceId);
200911
201019
  dispatchMetadataReembedOutbox(deletion.operationId, metadataReembedPageSize("category"));
200912
201020
  return { success: true };
200913
201021
  } catch (err) {
@@ -228024,110 +228132,6 @@ function createYjsRoomKey(input) {
228024
228132
 
228025
228133
  // ../../backend/src/api/routes/collaboration.ts
228026
228134
  init_config();
228027
-
228028
- // ../../backend/src/lib/doc-cache.ts
228029
- init_logger3();
228030
- init_redis();
228031
- var LIST_PREFIX = "hiai-docs:cache:docs:list:";
228032
- var SINGLE_PREFIX = "hiai-docs:cache:docs:single:";
228033
- function docListKey(userId, folderId, tag2, page = 1, limit = 20, workspaceId) {
228034
- const parts = [`${LIST_PREFIX}${userId}`];
228035
- if (workspaceId)
228036
- parts.push(`w:${workspaceId}`);
228037
- if (folderId)
228038
- parts.push(`f:${folderId}`);
228039
- if (tag2)
228040
- parts.push(`t:${tag2}`);
228041
- parts.push(`p:${page}`, `l:${limit}`);
228042
- return parts.join(":");
228043
- }
228044
- function docSingleKey(docId, userId, workspaceId) {
228045
- return `${SINGLE_PREFIX}${userId}${workspaceId ? `:w:${workspaceId}` : ""}:${docId}`;
228046
- }
228047
- async function cacheGetOrSet(key, ttl, compute, options = {}) {
228048
- try {
228049
- const cached2 = await redis.get(key);
228050
- if (cached2)
228051
- return JSON.parse(cached2);
228052
- } catch (err) {
228053
- logger3.warn({ err, key }, "Redis get failed, falling through to DB");
228054
- }
228055
- const value = await compute();
228056
- if (options.shouldCache && !options.shouldCache(value))
228057
- return value;
228058
- try {
228059
- await redis.set(key, JSON.stringify(value), "EX", ttl);
228060
- } catch (err) {
228061
- logger3.warn({ err, key }, "Redis set failed");
228062
- }
228063
- return value;
228064
- }
228065
- function isHttpCacheEnvelope(value) {
228066
- return typeof value === "object" && value !== null && "version" in value && value.version === 1 && "kind" in value && value.kind === "http-response" && "status" in value && typeof value.status === "number" && Number.isInteger(value.status) && value.status >= 100 && value.status <= 599 && "body" in value;
228067
- }
228068
- async function cacheHttpResponse(key, ttl, compute, options = {}) {
228069
- try {
228070
- const cached2 = await redis.get(key);
228071
- if (cached2) {
228072
- const parsed = JSON.parse(cached2);
228073
- if (isHttpCacheEnvelope(parsed)) {
228074
- return {
228075
- status: parsed.status,
228076
- body: parsed.body,
228077
- cacheHit: true
228078
- };
228079
- }
228080
- }
228081
- } catch (err) {
228082
- logger3.warn({ err, key }, "Redis HTTP cache get failed, falling through to DB");
228083
- }
228084
- const value = await compute();
228085
- if (options.shouldCache && !options.shouldCache(value)) {
228086
- return { ...value, cacheHit: false };
228087
- }
228088
- const envelope = {
228089
- version: 1,
228090
- kind: "http-response",
228091
- status: value.status,
228092
- body: value.body
228093
- };
228094
- try {
228095
- await redis.set(key, JSON.stringify(envelope), "EX", ttl);
228096
- } catch (err) {
228097
- logger3.warn({ err, key }, "Redis HTTP cache set failed");
228098
- }
228099
- return { ...value, cacheHit: false };
228100
- }
228101
- async function invalidateDocListCache(userId) {
228102
- const pattern = `${LIST_PREFIX}${userId}:*`;
228103
- try {
228104
- let cursor = "0";
228105
- do {
228106
- const [newCursor, keys4] = await redis.scan(cursor, "MATCH", pattern, "COUNT", 100);
228107
- cursor = newCursor;
228108
- if (keys4.length > 0)
228109
- await redis.del(...keys4);
228110
- } while (cursor !== "0");
228111
- } catch (err) {
228112
- logger3.warn({ err, userId }, "Failed to invalidate doc list cache");
228113
- }
228114
- }
228115
- async function invalidateDocCache(docId) {
228116
- const pattern = `${SINGLE_PREFIX}*:${docId}:scope:*`;
228117
- try {
228118
- let cursor = "0";
228119
- do {
228120
- const [newCursor, keys4] = await redis.scan(cursor, "MATCH", pattern, "COUNT", 100);
228121
- cursor = newCursor;
228122
- if (keys4.length > 0)
228123
- await redis.del(...keys4);
228124
- } while (cursor !== "0");
228125
- } catch (err) {
228126
- logger3.warn({ err, docId }, "Failed to invalidate doc cache");
228127
- }
228128
- }
228129
-
228130
- // ../../backend/src/api/routes/collaboration.ts
228131
228135
  init_logger3();
228132
228136
 
228133
228137
  // ../../node_modules/.bun/y-protocols@1.0.7+48ebd0cf55a3d4ab/node_modules/y-protocols/sync.js
@@ -228514,7 +228518,7 @@ collaborationRoutes.ws("/api/ws/collab/:documentId", {
228514
228518
  });
228515
228519
  await Promise.all([
228516
228520
  invalidateDocCache(documentId),
228517
- invalidateDocListCache(access.userId)
228521
+ invalidateDocListCache(access.userId, access.ctx.workspaceId)
228518
228522
  ]);
228519
228523
  enqueueReembed([
228520
228524
  {
@@ -228982,6 +228986,27 @@ async function withTags(ctx, rows) {
228982
228986
  }
228983
228987
  return rows.map((r4) => ({ ...r4, tags: byDoc.get(r4.id) ?? [] }));
228984
228988
  }
228989
+ async function withDocumentLocationNames(ctx, rows) {
228990
+ if (rows.length === 0)
228991
+ return [];
228992
+ const folderIds = [
228993
+ ...new Set(rows.flatMap((row) => row.folderId ? [row.folderId] : []))
228994
+ ];
228995
+ const categoryIds = [
228996
+ ...new Set(rows.flatMap((row) => row.categoryId ? [row.categoryId] : []))
228997
+ ];
228998
+ const [folderRows, categoryRows] = await withTenant(ctx, async (tx) => Promise.all([
228999
+ folderIds.length > 0 ? tx.select({ id: folders.id, name: folders.name }).from(folders).where(inArray(folders.id, folderIds)) : Promise.resolve([]),
229000
+ categoryIds.length > 0 ? tx.select({ id: categories.id, name: categories.name }).from(categories).where(inArray(categories.id, categoryIds)) : Promise.resolve([])
229001
+ ]));
229002
+ const folderNames = new Map(folderRows.map((folder) => [folder.id, folder.name]));
229003
+ const categoryNames = new Map(categoryRows.map((category) => [category.id, category.name]));
229004
+ return rows.map((row) => ({
229005
+ ...row,
229006
+ folderName: row.folderId ? folderNames.get(row.folderId) ?? null : null,
229007
+ categoryName: row.categoryId ? categoryNames.get(row.categoryId) ?? null : null
229008
+ }));
229009
+ }
228985
229010
  var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ query, set: set2, request }) => {
228986
229011
  const ip = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? request.headers.get("x-real-ip") ?? "unknown";
228987
229012
  const rl = await documentRateLimiter(ip, request);
@@ -229009,7 +229034,7 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229009
229034
  }
229010
229035
  const { folderId, tag: tag2, page, limit } = parsed.data;
229011
229036
  const offset = (page - 1) * limit;
229012
- const cacheKey = `${docListKey(userId, folderId, tag2, page, limit, ctx.workspaceId)}:scope:${access.categoryId ?? "all"}`;
229037
+ const cacheKey = `${docListKey(userId, folderId, tag2, page, limit, ctx.workspaceId)}:scope:${access.categoryId ?? "all"}:response:v2`;
229013
229038
  try {
229014
229039
  return await cacheGetOrSet(cacheKey, 30, async () => {
229015
229040
  const conditions2 = [
@@ -229038,7 +229063,7 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229038
229063
  ]);
229039
229064
  });
229040
229065
  return {
229041
- items: await withTags(ctx, rows2),
229066
+ items: await withDocumentLocationNames(ctx, await withTags(ctx, rows2)),
229042
229067
  total: countResult2[0]?.total ?? 0,
229043
229068
  page,
229044
229069
  limit
@@ -229060,7 +229085,7 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229060
229085
  ]);
229061
229086
  });
229062
229087
  return {
229063
- items: await withTags(ctx, rows),
229088
+ items: await withDocumentLocationNames(ctx, await withTags(ctx, rows)),
229064
229089
  total: countResult[0]?.total ?? 0,
229065
229090
  page,
229066
229091
  limit
@@ -229231,9 +229256,9 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229231
229256
  await acquireTenantTopologyLock(tx, ctx);
229232
229257
  }
229233
229258
  let categoryId = requestedCategoryId;
229234
- if (folderId && !categoryId) {
229235
- const resolvedCategoryId = await resolveFolderEffectiveCategory(tx, ctx, folderId);
229236
- if (resolvedCategoryId === undefined) {
229259
+ if (folderId) {
229260
+ const folderCategoryId = await resolveFolderEffectiveCategory(tx, ctx, folderId);
229261
+ if (folderCategoryId === undefined) {
229237
229262
  return {
229238
229263
  row: null,
229239
229264
  replayed: false,
@@ -229241,7 +229266,16 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229241
229266
  forbidden: true
229242
229267
  };
229243
229268
  }
229244
- categoryId = resolvedCategoryId;
229269
+ if (access.restricted && !isAuthorizedCategory(access, folderCategoryId)) {
229270
+ return {
229271
+ row: null,
229272
+ replayed: false,
229273
+ conflict: false,
229274
+ forbidden: true
229275
+ };
229276
+ }
229277
+ if (!categoryId)
229278
+ categoryId = folderCategoryId;
229245
229279
  }
229246
229280
  if (!isAuthorizedCategory(access, categoryId)) {
229247
229281
  return {
@@ -229325,7 +229359,7 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229325
229359
  revision: contentHash(created.title, created.content ?? ""),
229326
229360
  source: "interactive"
229327
229361
  }).catch((err) => logger3.warn({ err, documentId: created.id }, "Pipeline enqueue failed"));
229328
- invalidateDocListCache(userId);
229362
+ invalidateDocListCache(userId, ctx.workspaceId);
229329
229363
  set2.status = 201;
229330
229364
  const ipAddress = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? request.headers.get("x-real-ip") ?? "";
229331
229365
  const userAgent = request.headers.get("user-agent") ?? "";
@@ -230051,7 +230085,7 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
230051
230085
  }
230052
230086
  await Promise.all([
230053
230087
  invalidateDocCache(params2.id),
230054
- invalidateDocListCache(userId)
230088
+ invalidateDocListCache(userId, ctx.workspaceId)
230055
230089
  ]);
230056
230090
  const ipAddress = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? request.headers.get("x-real-ip") ?? "";
230057
230091
  const userAgent = request.headers.get("user-agent") ?? "";
@@ -230316,7 +230350,7 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
230316
230350
  revision: contentHash(copy3.title, copy3.content ?? ""),
230317
230351
  source: "interactive"
230318
230352
  }).catch((err) => logger3.warn({ err, documentId: copy3.id }, "Pipeline enqueue failed"));
230319
- invalidateDocListCache(userId);
230353
+ invalidateDocListCache(userId, ctx.workspaceId);
230320
230354
  set2.status = 201;
230321
230355
  return copy3;
230322
230356
  } catch (err) {
@@ -230386,7 +230420,7 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
230386
230420
  return { error: "Document not found" };
230387
230421
  }
230388
230422
  invalidateDocCache(params2.id);
230389
- invalidateDocListCache(ctx.userId);
230423
+ invalidateDocListCache(ctx.userId, ctx.workspaceId);
230390
230424
  return { success: true };
230391
230425
  }).delete("/trash/documents/:id", async ({ params: params2, request, set: set2 }) => {
230392
230426
  const access = await resolveContentAccess(request);
@@ -230496,7 +230530,7 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
230496
230530
  });
230497
230531
  }
230498
230532
  invalidateDocCache(params2.id);
230499
- invalidateDocListCache(ctx.userId);
230533
+ invalidateDocListCache(ctx.userId, ctx.workspaceId);
230500
230534
  return { success: true };
230501
230535
  } catch (error53) {
230502
230536
  if (error53 instanceof DocumentPurgeNotFoundError) {
@@ -230552,7 +230586,7 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
230552
230586
  return { error: "Document not found" };
230553
230587
  }
230554
230588
  invalidateDocCache(params2.id);
230555
- invalidateDocListCache(userId);
230589
+ invalidateDocListCache(userId, ctx.workspaceId);
230556
230590
  const ipAddress = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? request.headers.get("x-real-ip") ?? "";
230557
230591
  const userAgent = request.headers.get("user-agent") ?? "";
230558
230592
  recordAuditEvent({
@@ -231397,7 +231431,7 @@ var folderRoutes = new Elysia({ prefix: "/api/folders" }).get("/:id", async ({ p
231397
231431
  }
231398
231432
  if (parsed.data.name !== undefined || parsed.data.parentId !== undefined || parsed.data.categoryId !== undefined) {
231399
231433
  dispatchMetadataReembedOutbox(result.operationId, metadataReembedPageSize("folder"));
231400
- invalidateDocListCache(userId);
231434
+ await invalidateDocListCache(userId, ctx.workspaceId);
231401
231435
  }
231402
231436
  return result.updated;
231403
231437
  } catch (err) {
@@ -231450,6 +231484,7 @@ var folderRoutes = new Elysia({ prefix: "/api/folders" }).get("/:id", async ({ p
231450
231484
  set2.status = 404;
231451
231485
  return { error: "Folder not found" };
231452
231486
  }
231487
+ await invalidateDocListCache(ctx.userId, ctx.workspaceId);
231453
231488
  dispatchMetadataReembedOutbox(deletion.operationId, metadataReembedPageSize("folder"));
231454
231489
  return { success: true };
231455
231490
  } catch (err) {
@@ -238425,7 +238460,7 @@ var swaggerConfig = {
238425
238460
  },
238426
238461
  info: {
238427
238462
  title: "DocsMint API",
238428
- version: "0.8.8",
238463
+ version: "0.9.2",
238429
238464
  description: "Self-hosted AI-native knowledge workspace and installable PWA with hybrid search, GraphRAG, REST, SDK, CLI, and MCP access for people and AI agents.",
238430
238465
  contact: { name: "HiAi-gg", url: "https://github.com/HiAi-gg/docsmint" },
238431
238466
  license: {
package/dist/client.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * failures (502 / 503 / 504 / timeout / network reset) are retried
8
8
  * with exponential backoff up to `config.retries` attempts.
9
9
  */
10
- import type { DocsApiKeyCreated, DocsApiKeyListResponse, DocsAttachment, DocsAttachmentConfirmInput, DocsAttachmentListResponse, DocsAttachmentPresignInput, DocsAttachmentPresignResponse, DocsCategory, DocsCategoryInput, DocsCategoryUpdate, DocsDocument, DocsDocumentCreateInput, DocsDocumentCursorPage, DocsDocumentIndexRefresh, DocsDocumentIndexStatus, DocsDocumentKnowledgeSummary, DocsDocumentListResponse, DocsDocumentPipeline, DocsPipelineWarningRetry, DocsDocumentUpdateInput, DocsFolder, DocsFolderCreateInput, DocsFolderUpdateInput, DocsGraphEntitiesResponse, DocsGraphRelatedResponse, DocsGraphSearchResponse, DocsHealthResponse, DocsRequestContext, DocsSearchOptions, DocsSearchResponse, DocsSearchSuggestItem, DocsSharedContent, DocsShareLink, DocsShareListResponse, DocsShareRole, DocsTag, DocsVersion, DocsVersionDiff } from "./types.js";
10
+ import type { DocsApiKeyCreated, DocsApiKeyListResponse, DocsAttachment, DocsAttachmentConfirmInput, DocsAttachmentListResponse, DocsAttachmentPresignInput, DocsAttachmentPresignResponse, DocsCategory, DocsCategoryListItem, DocsCategoryInput, DocsCategoryUpdate, DocsDocument, DocsDocumentCreateInput, DocsDocumentCursorPage, DocsDocumentIndexRefresh, DocsDocumentIndexStatus, DocsDocumentKnowledgeSummary, DocsDocumentListResponse, DocsDocumentPipeline, DocsPipelineWarningRetry, DocsDocumentUpdateInput, DocsFolder, DocsFolderCreateInput, DocsFolderUpdateInput, DocsGraphEntitiesResponse, DocsGraphRelatedResponse, DocsGraphSearchResponse, DocsHealthResponse, DocsRequestContext, DocsSearchOptions, DocsSearchResponse, DocsSearchSuggestItem, DocsSharedContent, DocsShareLink, DocsShareListResponse, DocsShareRole, DocsTag, DocsVersion, DocsVersionDiff } from "./types.js";
11
11
  export interface DocsClientConfig {
12
12
  /** Base URL of the DocsMint API, e.g. `http://localhost:50700`. */
13
13
  baseUrl: string;
@@ -126,7 +126,7 @@ export declare class DocsClient {
126
126
  deleteTag(id: string, context?: DocsRequestContext): Promise<void>;
127
127
  addTagToDoc(documentId: string, tagId: string, context?: DocsRequestContext): Promise<void>;
128
128
  removeTagFromDoc(documentId: string, tagId: string, context?: DocsRequestContext): Promise<void>;
129
- listCategories(context?: DocsRequestContext): Promise<DocsCategory[]>;
129
+ listCategories(context?: DocsRequestContext): Promise<DocsCategoryListItem[]>;
130
130
  createCategory(input: DocsCategoryInput, context?: DocsRequestContext): Promise<DocsCategory>;
131
131
  updateCategory(id: string, updates: DocsCategoryUpdate, context?: DocsRequestContext): Promise<DocsCategory>;
132
132
  deleteCategory(id: string, context?: DocsRequestContext): Promise<void>;