@absolutejs/absolute 0.19.0-beta.497 → 0.19.0-beta.499

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/ai/index.js CHANGED
@@ -4846,6 +4846,12 @@ var normalizeStringArray2 = (value) => {
4846
4846
  };
4847
4847
  var normalizeChunkingOptions = (value) => isMetadataMap(value) ? value : undefined;
4848
4848
  var getNumericStatus = (status) => typeof status === "number" ? status : HTTP_STATUS_OK;
4849
+ var getBooleanProperty = (value, key) => {
4850
+ if (!isObjectRecord(value)) {
4851
+ return;
4852
+ }
4853
+ return typeof value[key] === "boolean" ? value[key] : undefined;
4854
+ };
4849
4855
  var isRAGDocumentChunk = (value) => isObjectRecord(value) && typeof value.chunkId === "string" && typeof value.text === "string";
4850
4856
  var isRAGDocument = (value) => isObjectRecord(value) && typeof value.text === "string";
4851
4857
  var isRAGDocumentUrl = (value) => isObjectRecord(value) && typeof value.url === "string" && value.url.trim().length > 0;
@@ -4979,6 +4985,7 @@ var ragChat = (config) => {
4979
4985
  const ingestJobs = [];
4980
4986
  const adminActions = [];
4981
4987
  const adminJobs = [];
4988
+ const syncJobs = [];
4982
4989
  const workflowRenderConfig = typeof config.htmx === "object" ? config.htmx.workflowRender ?? config.htmx.workflow?.render : undefined;
4983
4990
  const workflowRenderers = resolveRAGWorkflowRenderers(workflowRenderConfig);
4984
4991
  const createIngestJob = (inputKind, requestedCount) => {
@@ -5012,13 +5019,14 @@ var ragChat = (config) => {
5012
5019
  job.error = error;
5013
5020
  job.extractorNames = extractorNames;
5014
5021
  };
5015
- const createAdminAction = (action, documentId) => {
5022
+ const createAdminAction = (action, documentId, target) => {
5016
5023
  const record = {
5017
5024
  action,
5018
5025
  documentId,
5019
5026
  id: generateId(),
5020
5027
  startedAt: Date.now(),
5021
- status: "completed"
5028
+ status: "completed",
5029
+ target
5022
5030
  };
5023
5031
  adminActions.unshift(record);
5024
5032
  if (adminActions.length > MAX_ADMIN_ACTIONS) {
@@ -5026,7 +5034,7 @@ var ragChat = (config) => {
5026
5034
  }
5027
5035
  return record;
5028
5036
  };
5029
- const createAdminJob = (action, target) => {
5037
+ const createAdminJob = (action, target, bucket = adminJobs) => {
5030
5038
  const job = {
5031
5039
  action,
5032
5040
  id: generateId(),
@@ -5034,9 +5042,9 @@ var ragChat = (config) => {
5034
5042
  status: "running",
5035
5043
  target
5036
5044
  };
5037
- adminJobs.unshift(job);
5038
- if (adminJobs.length > MAX_ADMIN_JOBS) {
5039
- adminJobs.length = MAX_ADMIN_JOBS;
5045
+ bucket.unshift(job);
5046
+ if (bucket.length > MAX_ADMIN_JOBS) {
5047
+ bucket.length = MAX_ADMIN_JOBS;
5040
5048
  }
5041
5049
  return job;
5042
5050
  };
@@ -5066,6 +5074,12 @@ var ragChat = (config) => {
5066
5074
  job.elapsedMs = finishedAt - job.startedAt;
5067
5075
  job.error = error;
5068
5076
  };
5077
+ const buildSyncSources = async () => {
5078
+ if (!indexManager?.listSyncSources) {
5079
+ return [];
5080
+ }
5081
+ return await indexManager.listSyncSources();
5082
+ };
5069
5083
  const toHTMXResponse = (html, status, extraHeaders) => new Response(html, {
5070
5084
  headers: {
5071
5085
  ...HTML_HEADERS,
@@ -5501,6 +5515,12 @@ var ragChat = (config) => {
5501
5515
  }
5502
5516
  failuresByAdminAction.set(job.action, (failuresByAdminAction.get(job.action) ?? 0) + 1);
5503
5517
  }
5518
+ for (const job of syncJobs) {
5519
+ if (job.status !== "failed") {
5520
+ continue;
5521
+ }
5522
+ failuresByAdminAction.set(job.action, (failuresByAdminAction.get(job.action) ?? 0) + 1);
5523
+ }
5504
5524
  return {
5505
5525
  averageChunksPerDocument: documents.length > 0 ? Number((documents.reduce((sum, document) => sum + (document.chunkCount ?? 0), 0) / documents.length).toFixed(2)) : 0,
5506
5526
  duplicateDocumentIds: [...documentIdCounts.entries()].filter(([, count]) => count > 1).map(([id]) => id),
@@ -5546,10 +5566,13 @@ var ragChat = (config) => {
5546
5566
  canClearIndex: Boolean(ragStore?.clear),
5547
5567
  canCreateDocument: Boolean(indexManager?.createDocument),
5548
5568
  canDeleteDocument: Boolean(indexManager?.deleteDocument),
5569
+ canListSyncSources: Boolean(indexManager?.listSyncSources),
5549
5570
  canReindexDocument: Boolean(indexManager?.reindexDocument),
5550
5571
  canReindexSource: Boolean(indexManager?.reindexSource),
5551
5572
  canReseed: Boolean(indexManager?.reseed),
5552
- canReset: Boolean(indexManager?.reset)
5573
+ canReset: Boolean(indexManager?.reset),
5574
+ canSyncAllSources: Boolean(indexManager?.syncAllSources),
5575
+ canSyncSource: Boolean(indexManager?.syncSource)
5553
5576
  });
5554
5577
  const buildOperationsPayload = async () => {
5555
5578
  const collection = config.collection ?? (ragStore ? createRAGCollection({
@@ -5562,14 +5585,15 @@ var ragChat = (config) => {
5562
5585
  return {
5563
5586
  admin: buildAdminCapabilities(),
5564
5587
  adminActions: [...adminActions],
5565
- adminJobs: [...adminJobs],
5588
+ adminJobs: [...adminJobs, ...syncJobs].sort((left, right) => right.startedAt - left.startedAt),
5566
5589
  capabilities: collection?.getCapabilities?.(),
5567
5590
  documents: indexManager ? summarizeDocuments(indexedDocuments) : undefined,
5568
5591
  health: await summarizeHealth(indexedDocuments),
5569
5592
  ingestJobs: [...ingestJobs],
5570
5593
  ok: true,
5571
5594
  readiness: buildReadiness(),
5572
- status: collection?.getStatus?.()
5595
+ status: collection?.getStatus?.(),
5596
+ syncSources: await buildSyncSources()
5573
5597
  };
5574
5598
  };
5575
5599
  const handleStatus = async () => buildOperationsPayload();
@@ -5806,6 +5830,100 @@ var ragChat = (config) => {
5806
5830
  ...normalized
5807
5831
  };
5808
5832
  };
5833
+ const handleSyncSources = async () => {
5834
+ if (!indexManager?.listSyncSources) {
5835
+ return {
5836
+ error: "RAG source sync is not configured",
5837
+ ok: false
5838
+ };
5839
+ }
5840
+ return {
5841
+ ok: true,
5842
+ sources: await indexManager.listSyncSources()
5843
+ };
5844
+ };
5845
+ const handleSyncAllSources = async (options) => {
5846
+ if (!indexManager?.syncAllSources) {
5847
+ return {
5848
+ error: "RAG source sync is not configured",
5849
+ ok: false
5850
+ };
5851
+ }
5852
+ const job = createAdminJob("sync_all_sources", undefined, syncJobs);
5853
+ const action = createAdminAction("sync_all_sources");
5854
+ try {
5855
+ const result = await indexManager.syncAllSources(options);
5856
+ if (result && "ok" in result) {
5857
+ if (!result.ok) {
5858
+ failAdminJob(job, result.error);
5859
+ failAdminAction(action, result.error);
5860
+ return result;
5861
+ }
5862
+ if (result.partial) {
5863
+ const failedSourceIds = "sources" in result ? result.failedSourceIds : undefined;
5864
+ const message = failedSourceIds?.length ? `Partial source sync failure: ${failedSourceIds.join(", ")}` : "Partial source sync failure";
5865
+ failAdminJob(job, message);
5866
+ failAdminAction(action, message);
5867
+ return result;
5868
+ }
5869
+ completeAdminJob(job);
5870
+ completeAdminAction(action);
5871
+ return result;
5872
+ }
5873
+ completeAdminJob(job);
5874
+ completeAdminAction(action);
5875
+ return {
5876
+ ok: true,
5877
+ sources: await buildSyncSources()
5878
+ };
5879
+ } catch (caught) {
5880
+ const message = caught instanceof Error ? caught.message : String(caught);
5881
+ failAdminJob(job, message);
5882
+ failAdminAction(action, message);
5883
+ throw caught;
5884
+ }
5885
+ };
5886
+ const handleSyncSource = async (id, options) => {
5887
+ if (!indexManager?.syncSource) {
5888
+ return {
5889
+ error: "RAG source sync is not configured",
5890
+ ok: false
5891
+ };
5892
+ }
5893
+ if (!id) {
5894
+ return {
5895
+ error: "sync source id is required",
5896
+ ok: false
5897
+ };
5898
+ }
5899
+ const job = createAdminJob("sync_source", id, syncJobs);
5900
+ const action = createAdminAction("sync_source", undefined, id);
5901
+ try {
5902
+ const result = await indexManager.syncSource(id, options);
5903
+ if (result && "ok" in result) {
5904
+ if (!result.ok) {
5905
+ failAdminJob(job, result.error);
5906
+ failAdminAction(action, result.error);
5907
+ return result;
5908
+ }
5909
+ completeAdminJob(job);
5910
+ completeAdminAction(action);
5911
+ return result;
5912
+ }
5913
+ completeAdminJob(job);
5914
+ completeAdminAction(action);
5915
+ const source = (await buildSyncSources()).find((record) => record.id === id);
5916
+ return source ? { ok: true, source } : {
5917
+ error: "sync source not found",
5918
+ ok: false
5919
+ };
5920
+ } catch (caught) {
5921
+ const message = caught instanceof Error ? caught.message : String(caught);
5922
+ failAdminJob(job, message);
5923
+ failAdminAction(action, message);
5924
+ throw caught;
5925
+ }
5926
+ };
5809
5927
  const htmxRoutes = () => {
5810
5928
  if (!config.htmx) {
5811
5929
  return new Elysia2;
@@ -6042,6 +6160,53 @@ var ragChat = (config) => {
6042
6160
  set.status = HTTP_STATUS_NOT_FOUND;
6043
6161
  }
6044
6162
  return result;
6163
+ }).get(`${path}/sync`, async ({ request, set }) => {
6164
+ const result = await handleSyncSources();
6165
+ if (!result.ok) {
6166
+ set.status = HTTP_STATUS_NOT_FOUND;
6167
+ }
6168
+ if (config.htmx && isHTMXRequest(request)) {
6169
+ if (!result.ok) {
6170
+ return toHTMXResponse(workflowRenderers.error(result.error), getNumericStatus(set.status));
6171
+ }
6172
+ return toHTMXResponse(workflowRenderers.mutationResult({
6173
+ ok: true,
6174
+ status: `loaded ${"sources" in result ? result.sources.length : 1} sync sources`
6175
+ }));
6176
+ }
6177
+ return result;
6178
+ }).post(`${path}/sync`, async ({ body, request, set }) => {
6179
+ const background = getBooleanProperty(body, "background");
6180
+ const result = await handleSyncAllSources({ background });
6181
+ if (!result.ok) {
6182
+ set.status = HTTP_STATUS_NOT_FOUND;
6183
+ }
6184
+ if (config.htmx && isHTMXRequest(request)) {
6185
+ const html = result.ok ? workflowRenderers.mutationResult({
6186
+ ok: true,
6187
+ status: background === true ? "source sync queued in the background" : "source sync started and completed successfully"
6188
+ }) : workflowRenderers.error(result.error ?? "Failed to sync sources");
6189
+ return toHTMXResponse(html, getNumericStatus(set.status), {
6190
+ "HX-Trigger": "rag:mutated"
6191
+ });
6192
+ }
6193
+ return result;
6194
+ }).post(`${path}/sync/:id`, async ({ body, params, request, set }) => {
6195
+ const background = getBooleanProperty(body, "background");
6196
+ const result = await handleSyncSource(typeof params.id === "string" ? params.id.trim() : "", { background });
6197
+ if (!result.ok) {
6198
+ set.status = result.error === "sync source id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
6199
+ }
6200
+ if (config.htmx && isHTMXRequest(request)) {
6201
+ const html = result.ok ? workflowRenderers.mutationResult({
6202
+ ok: true,
6203
+ status: background === true ? "source sync queued in the background" : "source sync started and completed successfully"
6204
+ }) : workflowRenderers.error(result.error ?? "Failed to sync source");
6205
+ return toHTMXResponse(html, getNumericStatus(set.status), {
6206
+ "HX-Trigger": "rag:mutated"
6207
+ });
6208
+ }
6209
+ return result;
6045
6210
  }).post(`${path}/ingest`, async ({ body, request, set }) => {
6046
6211
  const result = await handleIngest(body);
6047
6212
  if (!result.ok) {
@@ -7020,6 +7185,436 @@ var resolveRAGStreamStage = ({
7020
7185
  }
7021
7186
  return "streaming";
7022
7187
  };
7188
+ // src/ai/rag/sync.ts
7189
+ import { createHash } from "crypto";
7190
+ import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
7191
+ import { dirname, resolve as resolve2 } from "path";
7192
+ var toSyncError = (caught) => caught instanceof Error ? caught.message : String(caught);
7193
+ var wait = async (delayMs) => {
7194
+ if (!(delayMs > 0)) {
7195
+ return;
7196
+ }
7197
+ await new Promise((resolve3) => setTimeout(resolve3, delayMs));
7198
+ };
7199
+ var parseSyncState = (content) => {
7200
+ try {
7201
+ const parsed = JSON.parse(content);
7202
+ return Array.isArray(parsed) ? parsed : [];
7203
+ } catch {
7204
+ return [];
7205
+ }
7206
+ };
7207
+ var createSyncFingerprint = (document) => createHash("sha1").update(document.source ?? "").update(`
7208
+ `).update(document.title ?? "").update(`
7209
+ `).update(document.text).digest("hex");
7210
+ var toManagedSyncDocument = (sourceId, document, syncKey) => ({
7211
+ ...document,
7212
+ metadata: {
7213
+ ...document.metadata ?? {},
7214
+ syncFingerprint: createSyncFingerprint(document),
7215
+ syncKey,
7216
+ syncSourceId: sourceId
7217
+ }
7218
+ });
7219
+ var isManagedBySyncSource = (document, sourceId) => document.metadata?.syncSourceId === sourceId;
7220
+ var getDocumentSyncFingerprint = (document) => typeof document.metadata?.syncFingerprint === "string" ? document.metadata.syncFingerprint : undefined;
7221
+ var reconcileManagedDocuments = async (input) => {
7222
+ const prepared = prepareRAGDocuments({
7223
+ documents: input.documents
7224
+ });
7225
+ const nextDocumentIds = new Set(prepared.map((document) => document.documentId));
7226
+ const nextFingerprintById = new Map(prepared.map((document, index) => [
7227
+ document.documentId,
7228
+ createSyncFingerprint(input.documents[index])
7229
+ ]));
7230
+ const existingDocuments = input.listDocuments ? await input.listDocuments() : [];
7231
+ const managedDocuments = existingDocuments.filter((document) => isManagedBySyncSource(document, input.sourceId));
7232
+ const staleDocuments = managedDocuments.filter((document) => !nextDocumentIds.has(document.id));
7233
+ const changedPrepared = prepared.filter((document) => {
7234
+ const existing = managedDocuments.find((entry) => entry.id === document.documentId);
7235
+ if (!existing) {
7236
+ return true;
7237
+ }
7238
+ return getDocumentSyncFingerprint(existing) !== nextFingerprintById.get(document.documentId);
7239
+ });
7240
+ if (input.deleteDocument) {
7241
+ await Promise.all(staleDocuments.map(async (document) => {
7242
+ await input.deleteDocument?.(document.id);
7243
+ }));
7244
+ }
7245
+ if (changedPrepared.length > 0) {
7246
+ await input.collection.ingest({
7247
+ chunks: changedPrepared.flatMap((document) => document.chunks)
7248
+ });
7249
+ }
7250
+ return {
7251
+ chunkCount: prepared.reduce((sum, document) => sum + document.chunks.length, 0),
7252
+ deletedCount: staleDocuments.length,
7253
+ documentCount: prepared.length,
7254
+ updatedCount: changedPrepared.length
7255
+ };
7256
+ };
7257
+ var toSourceRecord = (source, overrides) => ({
7258
+ description: source.description,
7259
+ id: source.id,
7260
+ kind: source.kind,
7261
+ label: source.label,
7262
+ metadata: source.metadata,
7263
+ status: "idle",
7264
+ target: source.target,
7265
+ ...overrides
7266
+ });
7267
+ var createRAGDirectorySyncSource = (options) => ({
7268
+ description: options.description,
7269
+ id: options.id,
7270
+ kind: "directory",
7271
+ label: options.label,
7272
+ metadata: options.metadata,
7273
+ retryAttempts: options.retryAttempts,
7274
+ retryDelayMs: options.retryDelayMs,
7275
+ target: options.directory,
7276
+ sync: async ({ collection, deleteDocument, listDocuments }) => {
7277
+ const loaded = await loadRAGDocumentsFromDirectory({
7278
+ baseMetadata: options.baseMetadata,
7279
+ defaultChunking: options.defaultChunking,
7280
+ directory: options.directory,
7281
+ extractors: options.extractors,
7282
+ includeExtensions: options.includeExtensions,
7283
+ recursive: options.recursive
7284
+ });
7285
+ const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.relativePath === "string" ? document.metadata.relativePath : document.source ?? document.title ?? ""));
7286
+ const reconciled = await reconcileManagedDocuments({
7287
+ collection,
7288
+ deleteDocument,
7289
+ documents: managedDocuments,
7290
+ listDocuments,
7291
+ sourceId: options.id
7292
+ });
7293
+ return {
7294
+ chunkCount: reconciled.chunkCount,
7295
+ documentCount: reconciled.documentCount,
7296
+ metadata: {
7297
+ deletedCount: reconciled.deletedCount,
7298
+ directory: options.directory,
7299
+ recursive: options.recursive !== false,
7300
+ updatedCount: reconciled.updatedCount
7301
+ }
7302
+ };
7303
+ }
7304
+ });
7305
+ var createRAGUrlSyncSource = (options) => ({
7306
+ description: options.description,
7307
+ id: options.id,
7308
+ kind: "url",
7309
+ label: options.label,
7310
+ metadata: options.metadata,
7311
+ retryAttempts: options.retryAttempts,
7312
+ retryDelayMs: options.retryDelayMs,
7313
+ target: options.urls.length === 1 ? options.urls[0]?.url : `${options.urls.length} urls`,
7314
+ sync: async ({ collection, deleteDocument, listDocuments }) => {
7315
+ const loaded = await loadRAGDocumentsFromURLs({
7316
+ baseMetadata: options.baseMetadata,
7317
+ defaultChunking: options.defaultChunking,
7318
+ extractors: options.extractors,
7319
+ urls: options.urls
7320
+ });
7321
+ const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.sourceUrl === "string" ? document.metadata.sourceUrl : document.source ?? document.title ?? ""));
7322
+ const reconciled = await reconcileManagedDocuments({
7323
+ collection,
7324
+ deleteDocument,
7325
+ documents: managedDocuments,
7326
+ listDocuments,
7327
+ sourceId: options.id
7328
+ });
7329
+ return {
7330
+ chunkCount: reconciled.chunkCount,
7331
+ documentCount: reconciled.documentCount,
7332
+ metadata: {
7333
+ deletedCount: reconciled.deletedCount,
7334
+ updatedCount: reconciled.updatedCount,
7335
+ urlCount: options.urls.length
7336
+ }
7337
+ };
7338
+ }
7339
+ });
7340
+ var createRAGSyncManager = (options) => {
7341
+ const sourceMap = new Map(options.sources.map((source) => [source.id, source]));
7342
+ const state = new Map(options.sources.map((source) => [source.id, toSourceRecord(source)]));
7343
+ const activeRuns = new Map;
7344
+ let hydrationPromise = null;
7345
+ const persistState = async () => {
7346
+ if (!options.saveState) {
7347
+ return;
7348
+ }
7349
+ await options.saveState([...state.values()]);
7350
+ };
7351
+ const ensureHydrated = async () => {
7352
+ if (!options.loadState) {
7353
+ return;
7354
+ }
7355
+ if (!hydrationPromise) {
7356
+ hydrationPromise = Promise.resolve(options.loadState()).then((records) => {
7357
+ for (const record of records ?? []) {
7358
+ const source = sourceMap.get(record.id);
7359
+ if (!source) {
7360
+ continue;
7361
+ }
7362
+ state.set(record.id, toSourceRecord(source, {
7363
+ ...record,
7364
+ metadata: {
7365
+ ...source.metadata ?? {},
7366
+ ...record.metadata ?? {}
7367
+ }
7368
+ }));
7369
+ }
7370
+ });
7371
+ }
7372
+ await hydrationPromise;
7373
+ };
7374
+ const resolveRetryAttempts = (source) => Math.max(0, source.retryAttempts ?? options.retryAttempts ?? 0);
7375
+ const resolveRetryDelayMs = (source) => Math.max(0, source.retryDelayMs ?? options.retryDelayMs ?? 0);
7376
+ const setSourceState = async (record) => {
7377
+ state.set(record.id, record);
7378
+ await persistState();
7379
+ };
7380
+ const runSource = async (source) => {
7381
+ await ensureHydrated();
7382
+ const existingRun = activeRuns.get(source.id);
7383
+ if (existingRun) {
7384
+ return existingRun;
7385
+ }
7386
+ const previous = state.get(source.id);
7387
+ const retryAttempts = resolveRetryAttempts(source);
7388
+ const retryDelayMs = resolveRetryDelayMs(source);
7389
+ const startedAt = Date.now();
7390
+ const running = toSourceRecord(source, {
7391
+ chunkCount: previous?.chunkCount,
7392
+ consecutiveFailures: previous?.consecutiveFailures ?? 0,
7393
+ documentCount: previous?.documentCount,
7394
+ lastError: undefined,
7395
+ lastStartedAt: startedAt,
7396
+ lastSuccessfulSyncAt: previous?.lastSuccessfulSyncAt,
7397
+ lastSyncedAt: previous?.lastSyncedAt,
7398
+ lastSyncDurationMs: previous?.lastSyncDurationMs,
7399
+ nextRetryAt: undefined,
7400
+ retryAttempts,
7401
+ status: "running"
7402
+ });
7403
+ const runPromise = (async () => {
7404
+ await setSourceState(running);
7405
+ for (let attempt = 0;attempt <= retryAttempts; attempt++) {
7406
+ try {
7407
+ const result = await source.sync({
7408
+ collection: options.collection,
7409
+ deleteDocument: options.deleteDocument,
7410
+ listDocuments: options.listDocuments
7411
+ });
7412
+ const finishedAt = Date.now();
7413
+ const completed = toSourceRecord(source, {
7414
+ chunkCount: result.chunkCount,
7415
+ consecutiveFailures: 0,
7416
+ documentCount: result.documentCount,
7417
+ lastError: undefined,
7418
+ lastStartedAt: startedAt,
7419
+ lastSuccessfulSyncAt: finishedAt,
7420
+ lastSyncedAt: finishedAt,
7421
+ lastSyncDurationMs: finishedAt - startedAt,
7422
+ metadata: result.metadata === undefined ? source.metadata : {
7423
+ ...source.metadata ?? {},
7424
+ ...result.metadata
7425
+ },
7426
+ nextRetryAt: undefined,
7427
+ retryAttempts,
7428
+ status: "completed"
7429
+ });
7430
+ await setSourceState(completed);
7431
+ return completed;
7432
+ } catch (caught) {
7433
+ const message = toSyncError(caught);
7434
+ const finishedAt = Date.now();
7435
+ const hasRetriesRemaining = attempt < retryAttempts;
7436
+ const consecutiveFailures = (previous?.consecutiveFailures ?? 0) + attempt + 1;
7437
+ const failed = toSourceRecord(source, {
7438
+ chunkCount: previous?.chunkCount,
7439
+ consecutiveFailures,
7440
+ documentCount: previous?.documentCount,
7441
+ lastError: message,
7442
+ lastStartedAt: startedAt,
7443
+ lastSuccessfulSyncAt: previous?.lastSuccessfulSyncAt,
7444
+ lastSyncedAt: finishedAt,
7445
+ lastSyncDurationMs: finishedAt - startedAt,
7446
+ nextRetryAt: hasRetriesRemaining ? finishedAt + retryDelayMs : undefined,
7447
+ retryAttempts,
7448
+ status: "failed"
7449
+ });
7450
+ await setSourceState(failed);
7451
+ if (!hasRetriesRemaining) {
7452
+ return failed;
7453
+ }
7454
+ await wait(retryDelayMs);
7455
+ }
7456
+ }
7457
+ return state.get(source.id) ?? toSourceRecord(source, { status: "failed" });
7458
+ })().finally(() => {
7459
+ activeRuns.delete(source.id);
7460
+ });
7461
+ activeRuns.set(source.id, runPromise);
7462
+ return runPromise;
7463
+ };
7464
+ const resolveBackground = (runOptions) => runOptions?.background ?? options.backgroundByDefault ?? false;
7465
+ return {
7466
+ listSyncSources: async () => {
7467
+ await ensureHydrated();
7468
+ return [...state.values()];
7469
+ },
7470
+ syncAllSources: async (runOptions) => {
7471
+ await ensureHydrated();
7472
+ if (resolveBackground(runOptions)) {
7473
+ for (const source of options.sources) {
7474
+ runSource(source);
7475
+ }
7476
+ return {
7477
+ ok: true,
7478
+ sources: [...state.values()]
7479
+ };
7480
+ }
7481
+ const sources = [];
7482
+ const failedSourceIds = [];
7483
+ const errorsBySource = {};
7484
+ for (const source of options.sources) {
7485
+ const record = await runSource(source);
7486
+ sources.push(record);
7487
+ if (record.status === "failed") {
7488
+ failedSourceIds.push(record.id);
7489
+ if (record.lastError) {
7490
+ errorsBySource[record.id] = record.lastError;
7491
+ }
7492
+ if (options.continueOnError === false) {
7493
+ return {
7494
+ errorsBySource,
7495
+ failedSourceIds,
7496
+ ok: true,
7497
+ partial: true,
7498
+ sources
7499
+ };
7500
+ }
7501
+ }
7502
+ }
7503
+ return {
7504
+ errorsBySource: failedSourceIds.length > 0 ? errorsBySource : undefined,
7505
+ failedSourceIds: failedSourceIds.length > 0 ? failedSourceIds : undefined,
7506
+ ok: true,
7507
+ partial: failedSourceIds.length > 0,
7508
+ sources
7509
+ };
7510
+ },
7511
+ syncSource: async (id, runOptions) => {
7512
+ await ensureHydrated();
7513
+ const source = sourceMap.get(id);
7514
+ if (!source) {
7515
+ return {
7516
+ error: `RAG sync source ${id} is not configured`,
7517
+ ok: false
7518
+ };
7519
+ }
7520
+ if (resolveBackground(runOptions)) {
7521
+ const existingRecord = state.get(id);
7522
+ if (existingRecord?.status !== "running") {
7523
+ const running = toSourceRecord(source, {
7524
+ chunkCount: existingRecord?.chunkCount,
7525
+ consecutiveFailures: existingRecord?.consecutiveFailures ?? 0,
7526
+ documentCount: existingRecord?.documentCount,
7527
+ lastError: undefined,
7528
+ lastStartedAt: Date.now(),
7529
+ lastSuccessfulSyncAt: existingRecord?.lastSuccessfulSyncAt,
7530
+ lastSyncedAt: existingRecord?.lastSyncedAt,
7531
+ lastSyncDurationMs: existingRecord?.lastSyncDurationMs,
7532
+ nextRetryAt: undefined,
7533
+ retryAttempts: resolveRetryAttempts(source),
7534
+ status: "running"
7535
+ });
7536
+ await setSourceState(running);
7537
+ runSource(source);
7538
+ }
7539
+ return {
7540
+ ok: true,
7541
+ source: state.get(id) ?? toSourceRecord(source, {
7542
+ status: "running"
7543
+ })
7544
+ };
7545
+ }
7546
+ const record = await runSource(source);
7547
+ if (record.status === "failed") {
7548
+ return {
7549
+ error: record.lastError ?? `RAG sync source ${id} failed`,
7550
+ ok: false
7551
+ };
7552
+ }
7553
+ return {
7554
+ ok: true,
7555
+ source: record
7556
+ };
7557
+ }
7558
+ };
7559
+ };
7560
+ var createRAGFileSyncStateStore = (path) => {
7561
+ const resolvedPath = resolve2(path);
7562
+ return {
7563
+ load: async () => {
7564
+ try {
7565
+ return parseSyncState(await readFile2(resolvedPath, "utf8"));
7566
+ } catch {
7567
+ return [];
7568
+ }
7569
+ },
7570
+ save: async (records) => {
7571
+ await mkdir(dirname(resolvedPath), { recursive: true });
7572
+ await writeFile(resolvedPath, JSON.stringify(records, null, 2), "utf8");
7573
+ }
7574
+ };
7575
+ };
7576
+ var createRAGSyncScheduler = (input) => {
7577
+ const timers = new Map;
7578
+ let running = false;
7579
+ const runSchedule = async (schedule) => {
7580
+ if (schedule.sourceIds?.length) {
7581
+ for (const sourceId of schedule.sourceIds) {
7582
+ await input.manager.syncSource?.(sourceId, {
7583
+ background: schedule.background
7584
+ });
7585
+ }
7586
+ return;
7587
+ }
7588
+ await input.manager.syncAllSources?.({
7589
+ background: schedule.background
7590
+ });
7591
+ };
7592
+ return {
7593
+ start: async () => {
7594
+ if (running) {
7595
+ return;
7596
+ }
7597
+ running = true;
7598
+ for (const schedule of input.schedules) {
7599
+ if (schedule.runImmediately) {
7600
+ runSchedule(schedule);
7601
+ }
7602
+ timers.set(schedule.id, setInterval(() => {
7603
+ runSchedule(schedule);
7604
+ }, schedule.intervalMs));
7605
+ }
7606
+ },
7607
+ stop: () => {
7608
+ for (const timer of timers.values()) {
7609
+ clearInterval(timer);
7610
+ }
7611
+ timers.clear();
7612
+ running = false;
7613
+ },
7614
+ isRunning: () => running,
7615
+ listSchedules: () => [...input.schedules]
7616
+ };
7617
+ };
7023
7618
  // src/ai/rag/adapters/utils.ts
7024
7619
  var vectorDimensionDefault = 24;
7025
7620
  var createRAGVector = (text, dimensions = vectorDimensionDefault) => {
@@ -7178,7 +7773,7 @@ import { existsSync as existsSync2 } from "fs";
7178
7773
  import { existsSync, readFileSync } from "fs";
7179
7774
  import { createRequire } from "module";
7180
7775
  import { arch, platform } from "os";
7181
- import { dirname, join as join2 } from "path";
7776
+ import { dirname as dirname2, join as join2 } from "path";
7182
7777
  var require2 = createRequire(import.meta.url);
7183
7778
  var PLATFORM_PACKAGE_MAP = {
7184
7779
  "darwin-arm64": {
@@ -7229,7 +7824,7 @@ var resolveAbsoluteSQLiteVec = () => {
7229
7824
  }
7230
7825
  try {
7231
7826
  const packageJsonPath = require2.resolve(`${packageInfo.packageName}/package.json`);
7232
- const packageRoot = dirname(packageJsonPath);
7827
+ const packageRoot = dirname2(packageJsonPath);
7233
7828
  const libraryPath = join2(packageRoot, packageInfo.libraryFile);
7234
7829
  const packageVersion = readPackageVersion(packageJsonPath);
7235
7830
  if (!existsSync(libraryPath)) {
@@ -8760,6 +9355,41 @@ var createRAGClient = (options) => {
8760
9355
  }
8761
9356
  return parseJson(response);
8762
9357
  },
9358
+ async syncSources() {
9359
+ const response = await fetchImpl(`${basePath}/sync`);
9360
+ if (!response.ok) {
9361
+ throw new Error(await toErrorMessage3(response));
9362
+ }
9363
+ return parseJson(response);
9364
+ },
9365
+ async syncAllSources(options2) {
9366
+ const response = await fetchImpl(`${basePath}/sync`, {
9367
+ body: options2?.background === true ? JSON.stringify({ background: true }) : undefined,
9368
+ headers: options2?.background === true ? jsonHeaders : undefined,
9369
+ method: "POST"
9370
+ });
9371
+ if (!response.ok) {
9372
+ return {
9373
+ error: await toErrorMessage3(response),
9374
+ ok: false
9375
+ };
9376
+ }
9377
+ return parseJson(response);
9378
+ },
9379
+ async syncSource(id, options2) {
9380
+ const response = await fetchImpl(`${basePath}/sync/${encodeURIComponent(id)}`, {
9381
+ body: options2?.background === true ? JSON.stringify({ background: true }) : undefined,
9382
+ headers: options2?.background === true ? jsonHeaders : undefined,
9383
+ method: "POST"
9384
+ });
9385
+ if (!response.ok) {
9386
+ return {
9387
+ error: await toErrorMessage3(response),
9388
+ ok: false
9389
+ };
9390
+ }
9391
+ return parseJson(response);
9392
+ },
8763
9393
  async reindexDocument(id) {
8764
9394
  const response = await fetchImpl(`${basePath}/reindex/documents/${encodeURIComponent(id)}`, {
8765
9395
  method: "POST"
@@ -8902,7 +9532,10 @@ export {
8902
9532
  createTextFileExtractor,
8903
9533
  createSQLiteRAGStore,
8904
9534
  createRAGVector,
9535
+ createRAGUrlSyncSource,
8905
9536
  createAIStream as createRAGTransport,
9537
+ createRAGSyncScheduler,
9538
+ createRAGSyncManager,
8906
9539
  createRAGReranker,
8907
9540
  createRAGQueryTransform,
8908
9541
  createRAGPDFOCRExtractor,
@@ -8912,9 +9545,11 @@ export {
8912
9545
  createRAGImageOCRExtractor,
8913
9546
  createRAGHTMXWorkflowRenderConfig,
8914
9547
  createRAGHTMXConfig,
9548
+ createRAGFileSyncStateStore,
8915
9549
  createRAGFileExtractor,
8916
9550
  createRAGEvaluationSuite,
8917
9551
  createRAGEmbeddingProvider,
9552
+ createRAGDirectorySyncSource,
8918
9553
  createRAGCollection,
8919
9554
  createRAGClient,
8920
9555
  createRAGArchiveFileExtractor,
@@ -8955,5 +9590,5 @@ export {
8955
9590
  aiChat
8956
9591
  };
8957
9592
 
8958
- //# debugId=55FD05298CEAFBDB64756E2164756E21
9593
+ //# debugId=61173B84573291CC64756E2164756E21
8959
9594
  //# sourceMappingURL=index.js.map