@qualcomm-ui/mdx-vite 2.5.2 → 2.5.4

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/cli.js CHANGED
@@ -5168,138 +5168,359 @@ function addGeneratePageMapCommand() {
5168
5168
  import { mkdir, writeFile as writeFile2 } from "node:fs/promises";
5169
5169
  import { resolve as resolve2 } from "node:path";
5170
5170
 
5171
- // src/open-web-ui-knowledge/common.ts
5172
- import { config } from "dotenv";
5173
- function loadEnv() {
5174
- const options = program.optsWithGlobals();
5175
- console.debug(options);
5176
- if (options.env) {
5177
- config({ path: options.env });
5178
- } else {
5179
- config();
5180
- }
5181
- }
5182
- function getConfigFromEnv() {
5183
- const openWebUiUrl = process.env.WEB_UI_URL;
5184
- const openWebUiKey = process.env.WEB_UI_KEY;
5185
- const knowledgeId = process.env.KNOWLEDGE_ID;
5186
- if (!openWebUiUrl || !openWebUiKey || !knowledgeId) {
5187
- throw new Error("WEB_UI_URL, WEB_UI_KEY, and KNOWLEDGE_ID must be set");
5188
- }
5189
- return {
5190
- knowledgeId,
5191
- webUiKey: openWebUiKey,
5192
- webUiUrl: openWebUiUrl
5193
- };
5171
+ // src/open-web-ui-knowledge/api.ts
5172
+ function isErrorResponse(response) {
5173
+ return typeof response === "object" && response !== null && "detail" in response;
5194
5174
  }
5195
- var KnowledgeApi = class {
5175
+ var FilesApi = class {
5196
5176
  config;
5197
- knowledgeCache = null;
5198
5177
  constructor(config2) {
5199
5178
  this.config = config2;
5200
5179
  }
5201
5180
  get headers() {
5202
5181
  return {
5203
- Authorization: `Bearer ${this.config.webUiKey}`
5182
+ Authorization: `Bearer ${this.config.apiKey}`
5204
5183
  };
5205
5184
  }
5206
- async listKnowledgeFiles() {
5207
- if (this.knowledgeCache) {
5208
- return this.knowledgeCache;
5185
+ get jsonHeaders() {
5186
+ return {
5187
+ ...this.headers,
5188
+ "Content-Type": "application/json"
5189
+ };
5190
+ }
5191
+ async handleResponse(response) {
5192
+ const data = await response.json();
5193
+ if (isErrorResponse(data)) {
5194
+ throw new Error(data.detail);
5195
+ }
5196
+ return data;
5197
+ }
5198
+ async upload(file, filename, options) {
5199
+ const formData = new FormData();
5200
+ let blob;
5201
+ if (file instanceof Blob) {
5202
+ blob = file;
5203
+ } else if (file instanceof ArrayBuffer) {
5204
+ blob = new Blob([file]);
5205
+ } else {
5206
+ const copy = new Uint8Array(file).buffer;
5207
+ blob = new Blob([copy]);
5208
+ }
5209
+ formData.append("file", blob, filename);
5210
+ if (options?.metadata) {
5211
+ formData.append("metadata", JSON.stringify(options.metadata));
5212
+ }
5213
+ const params = new URLSearchParams();
5214
+ if (options?.process !== void 0) {
5215
+ params.set("process", String(options.process));
5216
+ }
5217
+ if (options?.processInBackground !== void 0) {
5218
+ params.set("process_in_background", String(options.processInBackground));
5209
5219
  }
5210
- const knowledge = await fetch(
5211
- `${this.config.webUiUrl}/api/v1/knowledge/${this.config.knowledgeId}`,
5220
+ const url = `${this.config.baseUrl}/api/v1/files/${params.toString() ? `?${params}` : ""}`;
5221
+ const response = await fetch(url, {
5222
+ body: formData,
5223
+ headers: this.headers,
5224
+ method: "POST"
5225
+ });
5226
+ return this.handleResponse(response);
5227
+ }
5228
+ async list(includeContent = true) {
5229
+ const params = new URLSearchParams({ content: String(includeContent) });
5230
+ const response = await fetch(
5231
+ `${this.config.baseUrl}/api/v1/files/?${params}`,
5212
5232
  {
5213
- headers: {
5214
- ...this.headers,
5215
- Accept: "application/json"
5216
- }
5233
+ headers: this.headers
5217
5234
  }
5218
- ).then((res) => res.json());
5219
- if ("detail" in knowledge) {
5220
- throw new Error(knowledge.detail);
5221
- } else {
5222
- this.knowledgeCache = knowledge;
5235
+ );
5236
+ return this.handleResponse(response);
5237
+ }
5238
+ async search(pattern, includeContent = true) {
5239
+ const params = new URLSearchParams({
5240
+ content: String(includeContent),
5241
+ filename: pattern
5242
+ });
5243
+ const response = await fetch(
5244
+ `${this.config.baseUrl}/api/v1/files/search?${params}`,
5245
+ { headers: this.headers }
5246
+ );
5247
+ if (response.status === 404) {
5248
+ return [];
5223
5249
  }
5224
- return this.knowledgeCache;
5250
+ return this.handleResponse(response);
5251
+ }
5252
+ async deleteAll() {
5253
+ const response = await fetch(`${this.config.baseUrl}/api/v1/files/all`, {
5254
+ headers: this.headers,
5255
+ method: "DELETE"
5256
+ });
5257
+ return this.handleResponse(response);
5258
+ }
5259
+ async getById(id) {
5260
+ const response = await fetch(`${this.config.baseUrl}/api/v1/files/${id}`, {
5261
+ headers: this.headers
5262
+ });
5263
+ return this.handleResponse(response);
5225
5264
  }
5226
- async downloadFile(fileId) {
5227
- const fileResponse = await fetch(
5228
- `${this.config.webUiUrl}/api/v1/files/${fileId}`,
5265
+ async getProcessStatus(id) {
5266
+ const response = await fetch(
5267
+ `${this.config.baseUrl}/api/v1/files/${id}/process/status`,
5268
+ { headers: this.headers }
5269
+ );
5270
+ return this.handleResponse(response);
5271
+ }
5272
+ async waitForProcessing(id, options) {
5273
+ const maxAttempts = options?.maxAttempts ?? 120;
5274
+ const intervalMs = options?.intervalMs ?? 1e3;
5275
+ for (let i = 0; i < maxAttempts; i++) {
5276
+ const status = await this.getProcessStatus(id);
5277
+ if (status.status === "completed" || status.status === "failed") {
5278
+ return status;
5279
+ }
5280
+ await new Promise((resolve9) => setTimeout(resolve9, intervalMs));
5281
+ }
5282
+ throw new Error(`File processing timed out after ${maxAttempts} attempts`);
5283
+ }
5284
+ async getDataContent(id) {
5285
+ const response = await fetch(
5286
+ `${this.config.baseUrl}/api/v1/files/${id}/data/content`,
5287
+ { headers: this.headers }
5288
+ );
5289
+ return this.handleResponse(response);
5290
+ }
5291
+ async updateDataContent(id, content) {
5292
+ const response = await fetch(
5293
+ `${this.config.baseUrl}/api/v1/files/${id}/data/content/update`,
5229
5294
  {
5230
- headers: {
5231
- ...this.headers,
5232
- Accept: "application/json"
5233
- }
5295
+ body: JSON.stringify({ content }),
5296
+ headers: this.jsonHeaders,
5297
+ method: "POST"
5234
5298
  }
5235
- ).then((res) => res.json());
5236
- return fileResponse?.data?.content ?? "";
5299
+ );
5300
+ return this.handleResponse(response);
5237
5301
  }
5238
- async removeKnowledgeFile(id) {
5302
+ async getContent(id, asAttachment = false) {
5303
+ const params = new URLSearchParams({ attachment: String(asAttachment) });
5239
5304
  return fetch(
5240
- `${this.config.webUiUrl}/api/v1/knowledge/${this.config.knowledgeId}/file/remove`,
5305
+ `${this.config.baseUrl}/api/v1/files/${id}/content?${params}`,
5241
5306
  {
5242
- body: JSON.stringify({ file_id: id }),
5243
- headers: {
5244
- ...this.headers,
5245
- "Content-Type": "application/json"
5246
- },
5247
- method: "POST"
5307
+ headers: this.headers
5248
5308
  }
5249
- ).then((res) => res.json());
5309
+ );
5250
5310
  }
5251
- async deleteFile(id) {
5252
- return fetch(`${this.config.webUiUrl}/api/v1/files/${id}`, {
5253
- headers: {
5254
- ...this.headers,
5255
- "Content-Type": "application/json"
5256
- },
5311
+ async getContentAsText(id) {
5312
+ const response = await this.getContent(id);
5313
+ if (!response.ok) {
5314
+ const error = await response.json();
5315
+ throw new Error(error.detail || "Failed to get file content");
5316
+ }
5317
+ return response.text();
5318
+ }
5319
+ async delete(id) {
5320
+ const response = await fetch(`${this.config.baseUrl}/api/v1/files/${id}`, {
5321
+ headers: this.headers,
5257
5322
  method: "DELETE"
5258
- }).then((res) => res.json());
5323
+ });
5324
+ return this.handleResponse(response);
5259
5325
  }
5260
- async uploadFile(fileBuffer, name) {
5261
- const formData = new FormData();
5262
- formData.append("file", new Blob([fileBuffer]), name);
5263
- formData.append("knowledge_id", this.config.knowledgeId);
5264
- return fetch(`${this.config.webUiUrl}/api/v1/files/`, {
5265
- body: formData,
5266
- headers: {
5267
- ...this.headers,
5268
- Accept: "application/json"
5269
- },
5270
- method: "POST"
5271
- }).then((res) => res.json());
5326
+ };
5327
+ var KnowledgeApi = class {
5328
+ config;
5329
+ constructor(config2) {
5330
+ this.config = config2;
5272
5331
  }
5273
- async associateFile(fileId) {
5274
- return fetch(
5275
- `${this.config.webUiUrl}/api/v1/knowledge/${this.config.knowledgeId}/file/add`,
5332
+ get headers() {
5333
+ return {
5334
+ Authorization: `Bearer ${this.config.apiKey}`
5335
+ };
5336
+ }
5337
+ get jsonHeaders() {
5338
+ return {
5339
+ ...this.headers,
5340
+ "Content-Type": "application/json"
5341
+ };
5342
+ }
5343
+ async handleResponse(response) {
5344
+ const data = await response.json();
5345
+ if (isErrorResponse(data)) {
5346
+ throw new Error(data.detail);
5347
+ }
5348
+ return data;
5349
+ }
5350
+ async list() {
5351
+ const response = await fetch(`${this.config.baseUrl}/api/v1/knowledge/`, {
5352
+ headers: this.headers
5353
+ });
5354
+ return this.handleResponse(response);
5355
+ }
5356
+ async listWritable() {
5357
+ const response = await fetch(
5358
+ `${this.config.baseUrl}/api/v1/knowledge/list`,
5359
+ {
5360
+ headers: this.headers
5361
+ }
5362
+ );
5363
+ return this.handleResponse(response);
5364
+ }
5365
+ async create(form) {
5366
+ const response = await fetch(
5367
+ `${this.config.baseUrl}/api/v1/knowledge/create`,
5368
+ {
5369
+ body: JSON.stringify(form),
5370
+ headers: this.jsonHeaders,
5371
+ method: "POST"
5372
+ }
5373
+ );
5374
+ return this.handleResponse(response);
5375
+ }
5376
+ async reindex() {
5377
+ const response = await fetch(
5378
+ `${this.config.baseUrl}/api/v1/knowledge/reindex`,
5379
+ {
5380
+ headers: this.jsonHeaders,
5381
+ method: "POST"
5382
+ }
5383
+ );
5384
+ return this.handleResponse(response);
5385
+ }
5386
+ async getById(id) {
5387
+ const response = await fetch(
5388
+ `${this.config.baseUrl}/api/v1/knowledge/${id}`,
5389
+ {
5390
+ headers: this.headers
5391
+ }
5392
+ );
5393
+ return this.handleResponse(response);
5394
+ }
5395
+ async update(id, form) {
5396
+ const response = await fetch(
5397
+ `${this.config.baseUrl}/api/v1/knowledge/${id}/update`,
5398
+ {
5399
+ body: JSON.stringify(form),
5400
+ headers: this.jsonHeaders,
5401
+ method: "POST"
5402
+ }
5403
+ );
5404
+ return this.handleResponse(response);
5405
+ }
5406
+ async addFile(knowledgeId, fileId) {
5407
+ const response = await fetch(
5408
+ `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/file/add`,
5409
+ {
5410
+ body: JSON.stringify({ file_id: fileId }),
5411
+ headers: this.jsonHeaders,
5412
+ method: "POST"
5413
+ }
5414
+ );
5415
+ return this.handleResponse(response);
5416
+ }
5417
+ async updateFile(knowledgeId, fileId) {
5418
+ const response = await fetch(
5419
+ `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/file/update`,
5276
5420
  {
5277
5421
  body: JSON.stringify({ file_id: fileId }),
5278
- headers: {
5279
- ...this.headers,
5280
- "Content-Type": "application/json"
5281
- },
5422
+ headers: this.jsonHeaders,
5423
+ method: "POST"
5424
+ }
5425
+ );
5426
+ return this.handleResponse(response);
5427
+ }
5428
+ async removeFile(knowledgeId, fileId, deleteFile = true) {
5429
+ const params = new URLSearchParams({ delete_file: String(deleteFile) });
5430
+ const response = await fetch(
5431
+ `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/file/remove?${params}`,
5432
+ {
5433
+ body: JSON.stringify({ file_id: fileId }),
5434
+ headers: this.jsonHeaders,
5435
+ method: "POST"
5436
+ }
5437
+ );
5438
+ return this.handleResponse(response);
5439
+ }
5440
+ async delete(id) {
5441
+ const response = await fetch(
5442
+ `${this.config.baseUrl}/api/v1/knowledge/${id}/delete`,
5443
+ {
5444
+ headers: this.headers,
5445
+ method: "DELETE"
5446
+ }
5447
+ );
5448
+ return this.handleResponse(response);
5449
+ }
5450
+ async reset(id) {
5451
+ const response = await fetch(
5452
+ `${this.config.baseUrl}/api/v1/knowledge/${id}/reset`,
5453
+ {
5454
+ headers: this.jsonHeaders,
5455
+ method: "POST"
5456
+ }
5457
+ );
5458
+ return this.handleResponse(response);
5459
+ }
5460
+ async addFilesBatch(knowledgeId, fileIds) {
5461
+ const response = await fetch(
5462
+ `${this.config.baseUrl}/api/v1/knowledge/${knowledgeId}/files/batch/add`,
5463
+ {
5464
+ body: JSON.stringify(fileIds.map((file_id) => ({ file_id }))),
5465
+ headers: this.jsonHeaders,
5282
5466
  method: "POST"
5283
5467
  }
5284
- ).then((res) => res.json());
5468
+ );
5469
+ return this.handleResponse(response);
5285
5470
  }
5286
5471
  };
5287
5472
 
5473
+ // src/open-web-ui-knowledge/common.ts
5474
+ import { config } from "dotenv";
5475
+ function loadEnv() {
5476
+ const options = program.optsWithGlobals();
5477
+ console.debug(options);
5478
+ if (options.env) {
5479
+ config({ path: options.env });
5480
+ } else {
5481
+ config();
5482
+ }
5483
+ }
5484
+ function getConfigFromEnv() {
5485
+ const openWebUiUrl = process.env.WEB_UI_URL;
5486
+ const openWebUiKey = process.env.WEB_UI_KEY;
5487
+ const knowledgeId = process.env.KNOWLEDGE_ID;
5488
+ if (!openWebUiUrl || !openWebUiKey || !knowledgeId) {
5489
+ throw new Error("WEB_UI_URL, WEB_UI_KEY, and KNOWLEDGE_ID must be set");
5490
+ }
5491
+ return {
5492
+ knowledgeId,
5493
+ webUiKey: openWebUiKey,
5494
+ webUiUrl: openWebUiUrl
5495
+ };
5496
+ }
5497
+
5288
5498
  // src/open-web-ui-knowledge/download-knowledge.ts
5289
5499
  function addDownloadKnowledgeCommand() {
5290
5500
  program.command("download-knowledge").description("Download files from an Open Web UI knowledge base").requiredOption("-o, --output-dir <outputDir>", "Folder path").action(async (opts) => {
5291
5501
  loadEnv();
5292
5502
  await mkdir(opts.outputDir, { recursive: true }).catch();
5293
- const api = new KnowledgeApi(getConfigFromEnv());
5294
- const knowledge = await api.listKnowledgeFiles();
5295
- for (const file of knowledge.files) {
5296
- const data = await api.downloadFile(file.id);
5297
- if (data) {
5298
- await writeFile2(
5299
- resolve2(opts.outputDir, file.meta.name),
5300
- data,
5301
- "utf-8"
5302
- );
5503
+ const config2 = getConfigFromEnv();
5504
+ const apiConfig = { apiKey: config2.webUiKey, baseUrl: config2.webUiUrl };
5505
+ const knowledgeApi = new KnowledgeApi(apiConfig);
5506
+ const filesApi = new FilesApi(apiConfig);
5507
+ const knowledge = await knowledgeApi.getById(config2.knowledgeId);
5508
+ for (const file of knowledge.files ?? []) {
5509
+ const fileName = file.meta?.name;
5510
+ if (!fileName) {
5511
+ continue;
5512
+ }
5513
+ try {
5514
+ const content = await filesApi.getDataContent(file.id);
5515
+ if (content?.content) {
5516
+ await writeFile2(
5517
+ resolve2(opts.outputDir, fileName),
5518
+ content.content,
5519
+ "utf-8"
5520
+ );
5521
+ }
5522
+ } catch {
5523
+ console.warn(`Failed to download ${fileName}`);
5303
5524
  }
5304
5525
  }
5305
5526
  });
@@ -5657,6 +5878,12 @@ var replaceNpmInstallTabs = () => {
5657
5878
  done();
5658
5879
  };
5659
5880
  };
5881
+ function getPath(obj, path) {
5882
+ return path.split(".").reduce(
5883
+ (acc, key) => acc && typeof acc === "object" ? acc[key] : void 0,
5884
+ obj
5885
+ );
5886
+ }
5660
5887
  var KnowledgeGenerator = class {
5661
5888
  config;
5662
5889
  docProps = null;
@@ -5919,6 +6146,69 @@ ${codeText}
5919
6146
  required: extractRequired(propInfo, isPartial) || void 0
5920
6147
  };
5921
6148
  }
6149
+ /**
6150
+ * Creates a remark plugin that replaces TypeDocProps JSX elements with JSON
6151
+ * code blocks containing component prop documentation.
6152
+ */
6153
+ async replaceThemeNodes() {
6154
+ let themes = null;
6155
+ try {
6156
+ themes = await import("@qualcomm-ui/tailwind-plugin/theme");
6157
+ } catch {
6158
+ return () => {
6159
+ };
6160
+ }
6161
+ const handlers = {
6162
+ ColorTable: (node) => {
6163
+ const path = this.getAttrExpression(node, "data");
6164
+ return path && getPath(themes, path);
6165
+ },
6166
+ FontTable: (node) => {
6167
+ const path = this.getAttrExpression(node, "data");
6168
+ return path && getPath(themes, path);
6169
+ },
6170
+ ThemePropertyTable: (node) => {
6171
+ const path = this.getAttrExpression(node, "data");
6172
+ const property = this.getAttrExpression(node, "cssProperty");
6173
+ const data = path && getPath(themes, path);
6174
+ return path && property ? { cssPropertyName: property, data } : void 0;
6175
+ }
6176
+ };
6177
+ return () => (tree, _file, done) => {
6178
+ visit7(tree, "mdxJsxFlowElement", (node) => {
6179
+ const handler = node.name && handlers[node.name];
6180
+ if (!handler) {
6181
+ return;
6182
+ }
6183
+ const data = handler(node);
6184
+ if (!data) {
6185
+ console.warn(`No theme data for ${node.name}`);
6186
+ return;
6187
+ }
6188
+ Object.assign(node, {
6189
+ lang: "json",
6190
+ meta: null,
6191
+ type: "code",
6192
+ value: JSON.stringify(data, null, 2)
6193
+ });
6194
+ });
6195
+ done();
6196
+ };
6197
+ }
6198
+ getAttrExpression(node, name) {
6199
+ const attr = node.attributes?.find(
6200
+ (a) => a.type === "mdxJsxAttribute" && a.name === name
6201
+ );
6202
+ if (!attr?.value) {
6203
+ return null;
6204
+ }
6205
+ if (typeof attr.value === "string") {
6206
+ return attr.value;
6207
+ } else if (typeof attr.value === "object" && "value" in attr.value) {
6208
+ return attr.value.value;
6209
+ }
6210
+ return null;
6211
+ }
5922
6212
  /**
5923
6213
  * Creates a remark plugin that replaces TypeDocProps JSX elements with JSON
5924
6214
  * code blocks containing component prop documentation.
@@ -6088,7 +6378,7 @@ ${codeText}
6088
6378
  /\[([^\]]+)\]\(\.\/#([^)]+)\)/g,
6089
6379
  (_, text, anchor) => pageUrl && this.config.outputMode === "per-page" ? `[${text}](${pageUrl}#${anchor})` : text
6090
6380
  );
6091
- const processor = unified4().use(remarkParse4).use(remarkMdx3).use(this.replaceTypeDocProps()).use(this.replaceDemos(demosFolder, demoFiles)).use(remarkStringify3);
6381
+ const processor = unified4().use(remarkParse4).use(remarkMdx3).use(this.replaceTypeDocProps()).use(await this.replaceThemeNodes()).use(this.replaceDemos(demosFolder, demoFiles)).use(remarkStringify3);
6092
6382
  const processed = await processor.process(processedContent);
6093
6383
  processedContent = String(processed);
6094
6384
  processedContent = processedContent.replace(/\n\s*\n\s*\n/g, "\n\n");
@@ -6278,29 +6568,76 @@ function addGenerateKnowledgeCommand() {
6278
6568
 
6279
6569
  // src/open-web-ui-knowledge/upload-knowledge.ts
6280
6570
  import { createHash as createHash2 } from "node:crypto";
6571
+ import { writeFileSync } from "node:fs";
6281
6572
  import { access as access2, readdir as readdir2, readFile as readFile2, stat as stat2 } from "node:fs/promises";
6282
6573
  import { resolve as resolve6 } from "node:path";
6283
6574
  import { setTimeout as setTimeout2 } from "node:timers/promises";
6284
6575
  import ora from "ora";
6576
+
6577
+ // src/open-web-ui-knowledge/knowledge-cleaner.ts
6578
+ var KnowledgeCleaner = class {
6579
+ filesApi;
6580
+ knowledgeApi;
6581
+ constructor(config2) {
6582
+ const apiConfig = {
6583
+ apiKey: config2.webUiKey,
6584
+ baseUrl: config2.webUiUrl
6585
+ };
6586
+ this.filesApi = new FilesApi(apiConfig);
6587
+ this.knowledgeApi = new KnowledgeApi(apiConfig);
6588
+ }
6589
+ async cleanUpOrphanedFiles() {
6590
+ const files = await this.filesApi.list();
6591
+ const knowledgeBases = await this.knowledgeApi.list();
6592
+ const knowledgeIds = knowledgeBases.map((k) => k.id);
6593
+ for (const file of files) {
6594
+ const collectionName = file.meta?.collection_name;
6595
+ if (collectionName && !knowledgeIds.includes(collectionName)) {
6596
+ await this.filesApi.delete(file.id);
6597
+ } else if (file.data?.status === "failed") {
6598
+ await this.filesApi.delete(file.id);
6599
+ }
6600
+ }
6601
+ }
6602
+ };
6603
+
6604
+ // src/open-web-ui-knowledge/upload-knowledge.ts
6605
+ function toKnowledgeFile(file) {
6606
+ return {
6607
+ id: file.id,
6608
+ meta: { name: file.meta?.name }
6609
+ };
6610
+ }
6285
6611
  function calculateFileHash(fileData) {
6286
6612
  const normalized = fileData.normalize("NFC").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n+$/, "");
6287
6613
  return createHash2("sha256").update(normalized).digest("hex");
6288
6614
  }
6289
6615
  var Uploader = class {
6290
6616
  config;
6291
- api;
6617
+ knowledgeApi;
6618
+ filesApi;
6292
6619
  fileHashCache = /* @__PURE__ */ new Map();
6293
6620
  knowledgeFilesCache = null;
6621
+ cleaner;
6294
6622
  constructor(config2) {
6295
6623
  this.config = config2;
6296
- this.api = new KnowledgeApi(config2);
6624
+ const apiConfig = {
6625
+ apiKey: config2.webUiKey,
6626
+ baseUrl: config2.webUiUrl
6627
+ };
6628
+ this.knowledgeApi = new KnowledgeApi(apiConfig);
6629
+ this.filesApi = new FilesApi(apiConfig);
6630
+ this.cleaner = new KnowledgeCleaner(config2);
6297
6631
  }
6298
6632
  async buildHashCache(files) {
6299
6633
  const results = await Promise.allSettled(
6300
6634
  files.map(async (f) => {
6301
- const data = await this.api.downloadFile(f.id);
6302
- if (data) {
6303
- this.fileHashCache.set(f.id, calculateFileHash(data));
6635
+ try {
6636
+ const content = await this.filesApi.getDataContent(f.id);
6637
+ if (content?.content) {
6638
+ this.fileHashCache.set(f.id, calculateFileHash(content.content));
6639
+ }
6640
+ } catch {
6304
6641
  }
6305
6642
  })
6306
6643
  );
@@ -6313,11 +6650,11 @@ var Uploader = class {
6313
6650
  const spinner = ora(`File changed, deleting ${fileName}`).start();
6314
6651
  for (let i = 0; i < maxAttempts; i++) {
6315
6652
  this.knowledgeFilesCache = null;
6316
- const knowledge = await this.api.listKnowledgeFiles();
6653
+ const knowledge = await this.knowledgeApi.getById(this.config.knowledgeId);
6317
6654
  const stillExists = (knowledge.files ?? []).some((f) => f.id === fileId);
6318
6655
  if (!stillExists) {
6319
6656
  this.fileHashCache.delete(fileId);
6320
- spinner.succeed(`File ${fileId} deleted`);
6657
+ spinner.succeed(`File deleted: ${fileId}`);
6321
6658
  return true;
6322
6659
  }
6323
6660
  await setTimeout2(100 * (i + 1));
@@ -6332,10 +6669,19 @@ var Uploader = class {
6332
6669
  if (result.success) {
6333
6670
  return result;
6334
6671
  }
6335
- if (result.response?.detail?.includes("Duplicate content detected")) {
6672
+ if (result.error?.detail?.includes("Duplicate content detected")) {
6336
6673
  console.warn(
6337
6674
  `Duplicate content: ${name} is already in knowledge base, skipping`
6338
6675
  );
6676
+ if (result.error.fileId) {
6677
+ try {
6678
+ console.debug(`Removing duplicate file: ${result.error.fileId}`);
6679
+ await this.filesApi.delete(result.error.fileId);
6680
+ await this.waitForFileDeletion(result.error.fileId, name);
6681
+ } catch (e) {
6682
+ console.debug("Failed to remove duplicate file", e);
6683
+ }
6684
+ }
6339
6685
  return { skipped: true, success: true };
6340
6686
  }
6341
6687
  if (attempt < maxRetries - 1) {
@@ -6349,11 +6695,6 @@ var Uploader = class {
6349
6695
  console.debug(`Failed to upload ${name}`);
6350
6696
  return { success: false };
6351
6697
  }
6352
- get headers() {
6353
- return {
6354
- Authorization: `Bearer ${this.config.webUiKey}`
6355
- };
6356
- }
6357
6698
  async uploadDirectory() {
6358
6699
  const fileNames = await readdir2(this.config.knowledgeFilePath);
6359
6700
  const files = await Promise.all(
@@ -6365,8 +6706,8 @@ var Uploader = class {
6365
6706
  name
6366
6707
  }))
6367
6708
  );
6368
- const knowledge = await this.api.listKnowledgeFiles();
6369
- this.knowledgeFilesCache = knowledge.files ?? [];
6709
+ const knowledge = await this.knowledgeApi.getById(this.config.knowledgeId);
6710
+ this.knowledgeFilesCache = (knowledge.files ?? []).map(toKnowledgeFile);
6370
6711
  await this.buildHashCache(this.knowledgeFilesCache);
6371
6712
  let skippedCount = 0;
6372
6713
  let successCount = 0;
@@ -6405,27 +6746,59 @@ var Uploader = class {
6405
6746
  }
6406
6747
  }
6407
6748
  if (knowledgeFile) {
6408
- await this.api.removeKnowledgeFile(knowledgeFile.id);
6409
- await this.waitForFileDeletion(knowledgeFile.id, name);
6749
+ try {
6750
+ const fileId = knowledgeFile.id;
6751
+ const fileString = await readFile2(
6752
+ resolve6(this.config.knowledgeFilePath, name),
6753
+ "utf-8"
6754
+ );
6755
+ const spinner2 = ora(`Updating ${name}`).start();
6756
+ await this.filesApi.updateDataContent(fileId, fileString);
6757
+ await this.knowledgeApi.updateFile(this.config.knowledgeId, fileId);
6758
+ spinner2.succeed(`Updated ${name}`);
6759
+ return { success: true };
6760
+ } catch (e) {
6761
+ console.warn(`Failed to update existing file ${name}:`, e);
6762
+ return { success: false };
6763
+ }
6410
6764
  }
6411
6765
  const spinner = ora(`Uploading ${name}`).start();
6412
6766
  const fileBuffer = await readFile2(
6413
6767
  resolve6(this.config.knowledgeFilePath, name)
6414
6768
  );
6415
- const uploadResponse = await this.api.uploadFile(fileBuffer, name);
6416
- if (!uploadResponse.id || !uploadResponse.filename) {
6769
+ let uploadedFileId = void 0;
6770
+ try {
6771
+ const uploadResponse = await this.filesApi.upload(fileBuffer, name, {
6772
+ processInBackground: false
6773
+ });
6774
+ uploadedFileId = uploadResponse.id;
6775
+ if (!uploadResponse.id || !uploadResponse.filename) {
6776
+ spinner.fail(`Error uploading ${name}`);
6777
+ return {
6778
+ error: { fileId: uploadResponse.id, uploadResponse },
6779
+ success: false
6780
+ };
6781
+ }
6782
+ spinner.text = `Associating ${name} with knowledge base`;
6783
+ const addResponse = await this.knowledgeApi.addFile(
6784
+ this.config.knowledgeId,
6785
+ uploadResponse.id
6786
+ );
6787
+ if (addResponse.name) {
6788
+ spinner.succeed(`${name} associated with knowledge base`);
6789
+ this.fileHashCache.set(uploadResponse.id, contentHash);
6790
+ return { success: true };
6791
+ } else {
6792
+ spinner.stop();
6793
+ return {
6794
+ error: { addResponse, fileId: uploadResponse.id },
6795
+ success: false
6796
+ };
6797
+ }
6798
+ } catch (e) {
6417
6799
  spinner.fail(`Error uploading ${name}`);
6418
- return { response: uploadResponse, success: false };
6419
- }
6420
- spinner.text = `Associating ${name} with knowledge base`;
6421
- const addResponse = await this.api.associateFile(uploadResponse.id);
6422
- if (addResponse.name) {
6423
- spinner.succeed(`${name} associated with knowledge base`);
6424
- this.fileHashCache.set(uploadResponse.id, contentHash);
6425
- return { success: true };
6426
- } else {
6427
- spinner.stop();
6428
- return { response: addResponse, success: false };
6800
+ const detail = e instanceof Error ? e.message : String(e);
6801
+ return { error: { detail, fileId: uploadedFileId }, success: false };
6429
6802
  }
6430
6803
  }
6431
6804
  async uploadKnowledge() {
@@ -6437,28 +6810,77 @@ var Uploader = class {
6437
6810
  if (stats.isDirectory()) {
6438
6811
  return this.uploadDirectory();
6439
6812
  } else {
6813
+ console.error("Not a directory, can't upload.");
6440
6814
  }
6441
6815
  }
6442
6816
  };
6443
6817
  function addUploadKnowledgeCommand() {
6444
- program.name("upload-knowledge").description("Upload files to OpenWebUI knowledge base").command("upload-knowledge").option("-p, --path <path>", "Path to file or folder relative to script").option(
6445
- "--force",
6446
- "force upload files, even if their contents have not changed"
6447
- ).action(async (options) => {
6448
- loadEnv();
6818
+ function getUploader(knowledgePath, forceUpload) {
6449
6819
  const sharedConfig = getConfigFromEnv();
6450
- const knowledgeFilePath = options.path || process.env.KNOWLEDGE_OUTPUT_PATH;
6820
+ const knowledgeFilePath = knowledgePath || process.env.KNOWLEDGE_OUTPUT_PATH;
6451
6821
  if (!knowledgeFilePath) {
6452
6822
  throw new Error(
6453
6823
  "KNOWLEDGE_FILE_PATH must be set or provided as the --path option"
6454
6824
  );
6455
6825
  }
6456
- const uploader = new Uploader({
6826
+ return new Uploader({
6457
6827
  ...sharedConfig,
6458
- force: options.force || false,
6828
+ force: forceUpload,
6459
6829
  knowledgeFilePath
6460
6830
  });
6461
- return uploader.uploadKnowledge();
6831
+ }
6832
+ program.name("upload-knowledge").description("Upload files to OpenWebUI knowledge base").command("upload-knowledge").option("-p, --path <path>", "Path to file or folder relative to script").option(
6833
+ "--force",
6834
+ "force upload files, even if their contents have not changed"
6835
+ ).action(async (options) => {
6836
+ loadEnv();
6837
+ return getUploader(options.path, options.force).uploadKnowledge();
6838
+ });
6839
+ program.command("get-knowledge-files").description("Get files from OpenWebUI knowledge base").option("-p, --path <path>", "Path to file or folder relative to script").action(async (options) => {
6840
+ loadEnv();
6841
+ const uploader = getUploader(options.path);
6842
+ const files = await uploader.filesApi.search("*");
6843
+ console.debug(`found ${files.length} files`);
6844
+ writeFileSync(
6845
+ resolve6(uploader.config.knowledgeFilePath, "files.json"),
6846
+ JSON.stringify(files, null, 2),
6847
+ "utf-8"
6848
+ );
6849
+ });
6850
+ program.command("clear-knowledge").description("Remove all files from the knowledge base collection").action(async () => {
6851
+ loadEnv();
6852
+ const sharedConfig = getConfigFromEnv();
6853
+ const apiConfig = {
6854
+ apiKey: sharedConfig.webUiKey,
6855
+ baseUrl: sharedConfig.webUiUrl
6856
+ };
6857
+ const filesApi = new FilesApi(apiConfig);
6858
+ const knowledgeApi = new KnowledgeApi(apiConfig);
6859
+ const cleaner = new KnowledgeCleaner(sharedConfig);
6860
+ await cleaner.cleanUpOrphanedFiles();
6861
+ const knowledge = await knowledgeApi.getById(sharedConfig.knowledgeId);
6862
+ const knowledgeFiles = knowledge.files ?? [];
6863
+ if (!knowledge) {
6864
+ console.log("Knowledge base not found");
6865
+ return;
6866
+ }
6867
+ const files = await filesApi.list(false).then(
6868
+ (files2) => files2.filter((file) => file.meta?.collection_name === knowledge.id)
6869
+ );
6870
+ if (files.length === 0) {
6871
+ console.log("No files in knowledge base");
6872
+ return;
6873
+ }
6874
+ console.log(`Removing ${files.length} files from knowledge base...`);
6875
+ for (const file of files) {
6876
+ if (knowledgeFiles.some((f) => f.id === file.id)) {
6877
+ await knowledgeApi.removeFile(knowledge.id, file.id, true);
6878
+ } else {
6879
+ await filesApi.delete(file.id);
6880
+ }
6881
+ console.log(`Removed ${file.id}`);
6882
+ }
6883
+ console.log(`Removed ${files.length} files`);
6462
6884
  });
6463
6885
  }
6464
6886