Skip to content

Commit ad8a7b9

Browse files
Merge origin/main into feat/connection-pooling
Resolve conflicts to keep both connection pooling (PR #75) and batch operations + memory management from main.
2 parents 8fea2c0 + e6484d1 commit ad8a7b9

File tree

12 files changed

+2576
-14
lines changed

12 files changed

+2576
-14
lines changed

apps/mcp-server/src/services/ILighthouseService.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
*/
44

55
import { UploadResult, DownloadResult, AccessCondition, Dataset } from "@lighthouse-tooling/types";
6-
import { EnhancedAccessCondition } from "@lighthouse-tooling/sdk-wrapper";
6+
import {
7+
EnhancedAccessCondition,
8+
BatchUploadOptions,
9+
BatchDownloadOptions,
10+
BatchOperationResult,
11+
BatchDownloadFileResult,
12+
FileInfo,
13+
} from "@lighthouse-tooling/sdk-wrapper";
714

815
export interface StoredFile {
916
cid: string;
@@ -150,4 +157,20 @@ export interface ILighthouseService {
150157
success: boolean;
151158
error?: string;
152159
}>;
160+
161+
/**
162+
* Batch upload multiple files with configurable concurrency
163+
*/
164+
batchUploadFiles(
165+
filePaths: string[],
166+
options?: BatchUploadOptions,
167+
): Promise<BatchOperationResult<FileInfo>>;
168+
169+
/**
170+
* Batch download multiple files by CID with configurable concurrency
171+
*/
172+
batchDownloadFiles(
173+
cids: string[],
174+
options?: BatchDownloadOptions,
175+
): Promise<BatchOperationResult<BatchDownloadFileResult>>;
153176
}

apps/mcp-server/src/services/LighthouseService.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ import {
66
LighthouseAISDK,
77
EnhancedAccessCondition,
88
ConnectionPoolConfig,
9+
BatchUploadOptions,
10+
BatchDownloadOptions,
11+
BatchOperationResult,
12+
BatchDownloadFileResult,
13+
BatchUploadInput,
14+
BatchDownloadInput,
15+
FileInfo,
916
} from "@lighthouse-tooling/sdk-wrapper";
1017
import { UploadResult, DownloadResult, AccessCondition, Dataset } from "@lighthouse-tooling/types";
1118
import { Logger } from "@lighthouse-tooling/shared";
@@ -885,6 +892,107 @@ export class LighthouseService implements ILighthouseService {
885892
}
886893
}
887894

895+
/**
896+
* Batch upload multiple files with configurable concurrency
897+
*/
898+
async batchUploadFiles(
899+
filePaths: string[],
900+
options?: BatchUploadOptions,
901+
): Promise<BatchOperationResult<FileInfo>> {
902+
const startTime = Date.now();
903+
904+
try {
905+
this.logger.info("Starting batch upload", {
906+
fileCount: filePaths.length,
907+
concurrency: options?.concurrency || 3,
908+
});
909+
910+
// Convert string paths to BatchUploadInput objects
911+
const inputs: BatchUploadInput[] = filePaths.map((filePath) => ({
912+
filePath,
913+
}));
914+
915+
const result = await this.sdk.batchUpload(inputs, options);
916+
917+
// Store successful uploads in cache and database
918+
for (const fileResult of result.results) {
919+
if (fileResult.success && fileResult.data) {
920+
const storedFile: StoredFile = {
921+
cid: fileResult.data.hash,
922+
filePath: fileResult.data.name,
923+
size: fileResult.data.size,
924+
encrypted: fileResult.data.encrypted,
925+
accessConditions: options?.accessConditions,
926+
tags: options?.tags,
927+
uploadedAt: fileResult.data.uploadedAt,
928+
pinned: true,
929+
hash: fileResult.data.hash,
930+
};
931+
932+
this.storage.saveFile(storedFile);
933+
this.fileCache.set(fileResult.data.hash, storedFile);
934+
}
935+
}
936+
937+
const executionTime = Date.now() - startTime;
938+
this.logger.info("Batch upload completed", {
939+
total: result.total,
940+
successful: result.successful,
941+
failed: result.failed,
942+
successRate: result.successRate,
943+
executionTime,
944+
});
945+
946+
return result;
947+
} catch (error) {
948+
this.logger.error("Batch upload failed", error as Error, {
949+
fileCount: filePaths.length,
950+
});
951+
throw error;
952+
}
953+
}
954+
955+
/**
956+
* Batch download multiple files by CID with configurable concurrency
957+
*/
958+
async batchDownloadFiles(
959+
cids: string[],
960+
options?: BatchDownloadOptions,
961+
): Promise<BatchOperationResult<BatchDownloadFileResult>> {
962+
const startTime = Date.now();
963+
964+
try {
965+
this.logger.info("Starting batch download", {
966+
cidCount: cids.length,
967+
concurrency: options?.concurrency || 3,
968+
outputDir: options?.outputDir,
969+
});
970+
971+
// Convert string CIDs to BatchDownloadInput objects
972+
const inputs: BatchDownloadInput[] = cids.map((cid) => ({
973+
cid,
974+
}));
975+
976+
const result = await this.sdk.batchDownload(inputs, options);
977+
978+
const executionTime = Date.now() - startTime;
979+
this.logger.info("Batch download completed", {
980+
total: result.total,
981+
successful: result.successful,
982+
failed: result.failed,
983+
successRate: result.successRate,
984+
executionTime,
985+
});
986+
987+
return result;
988+
} catch (error) {
989+
this.logger.error("Batch download failed", error as Error, {
990+
cidCount: cids.length,
991+
});
992+
throw error;
993+
}
994+
}
995+
888996
/**
889997
* Cleanup resources
890998
*/

apps/mcp-server/src/services/MockLighthouseService.ts

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
*/
44

55
import { UploadResult, DownloadResult, AccessCondition, Dataset } from "@lighthouse-tooling/types";
6-
import { EnhancedAccessCondition } from "@lighthouse-tooling/sdk-wrapper";
6+
import {
7+
EnhancedAccessCondition,
8+
BatchUploadOptions,
9+
BatchDownloadOptions,
10+
BatchOperationResult,
11+
BatchDownloadFileResult,
12+
FileInfo,
13+
} from "@lighthouse-tooling/sdk-wrapper";
714
import { Logger, FileUtils } from "@lighthouse-tooling/shared";
815
import { CIDGenerator } from "../utils/cid-generator.js";
916
import { ILighthouseService, StoredFile } from "./ILighthouseService.js";
@@ -598,6 +605,167 @@ export class MockLighthouseService implements ILighthouseService {
598605
}
599606
}
600607

608+
/**
609+
* Batch upload multiple files with configurable concurrency
610+
*/
611+
async batchUploadFiles(
612+
filePaths: string[],
613+
options?: BatchUploadOptions,
614+
): Promise<BatchOperationResult<FileInfo>> {
615+
const startTime = Date.now();
616+
const results: Array<{
617+
id: string;
618+
success: boolean;
619+
data?: FileInfo;
620+
error?: string;
621+
duration: number;
622+
retries: number;
623+
}> = [];
624+
625+
this.logger.info("Starting batch upload", {
626+
fileCount: filePaths.length,
627+
concurrency: options?.concurrency || 3,
628+
});
629+
630+
for (const filePath of filePaths) {
631+
const itemStartTime = Date.now();
632+
try {
633+
const uploadResult = await this.uploadFile({
634+
filePath,
635+
encrypt: options?.encrypt,
636+
accessConditions: options?.accessConditions,
637+
tags: options?.tags,
638+
});
639+
640+
results.push({
641+
id: filePath,
642+
success: true,
643+
data: {
644+
hash: uploadResult.cid,
645+
name: filePath.split("/").pop() || filePath,
646+
size: uploadResult.size,
647+
encrypted: uploadResult.encrypted,
648+
mimeType: "application/octet-stream",
649+
uploadedAt: uploadResult.uploadedAt,
650+
},
651+
duration: Date.now() - itemStartTime,
652+
retries: 0,
653+
});
654+
} catch (error) {
655+
if (!options?.continueOnError) {
656+
throw error;
657+
}
658+
results.push({
659+
id: filePath,
660+
success: false,
661+
error: error instanceof Error ? error.message : "Unknown error",
662+
duration: Date.now() - itemStartTime,
663+
retries: 0,
664+
});
665+
}
666+
}
667+
668+
const successful = results.filter((r) => r.success).length;
669+
const failed = results.filter((r) => !r.success).length;
670+
const totalDuration = Date.now() - startTime;
671+
672+
this.logger.info("Batch upload completed", {
673+
total: filePaths.length,
674+
successful,
675+
failed,
676+
totalDuration,
677+
});
678+
679+
return {
680+
total: filePaths.length,
681+
successful,
682+
failed,
683+
successRate: filePaths.length > 0 ? (successful / filePaths.length) * 100 : 0,
684+
totalDuration,
685+
averageDuration: results.length > 0 ? totalDuration / results.length : 0,
686+
results,
687+
};
688+
}
689+
690+
/**
691+
* Batch download multiple files by CID with configurable concurrency
692+
*/
693+
async batchDownloadFiles(
694+
cids: string[],
695+
options?: BatchDownloadOptions,
696+
): Promise<BatchOperationResult<BatchDownloadFileResult>> {
697+
const startTime = Date.now();
698+
const results: Array<{
699+
id: string;
700+
success: boolean;
701+
data?: BatchDownloadFileResult;
702+
error?: string;
703+
duration: number;
704+
retries: number;
705+
}> = [];
706+
707+
this.logger.info("Starting batch download", {
708+
cidCount: cids.length,
709+
concurrency: options?.concurrency || 3,
710+
});
711+
712+
for (const cid of cids) {
713+
const itemStartTime = Date.now();
714+
try {
715+
const downloadResult = await this.fetchFile({
716+
cid,
717+
outputPath: options?.outputDir ? `${options.outputDir}/${cid}` : undefined,
718+
decrypt: options?.decrypt,
719+
});
720+
721+
results.push({
722+
id: cid,
723+
success: true,
724+
data: {
725+
cid: downloadResult.cid,
726+
filePath: downloadResult.filePath,
727+
size: downloadResult.size,
728+
decrypted: downloadResult.decrypted,
729+
},
730+
duration: Date.now() - itemStartTime,
731+
retries: 0,
732+
});
733+
} catch (error) {
734+
if (!options?.continueOnError) {
735+
throw error;
736+
}
737+
results.push({
738+
id: cid,
739+
success: false,
740+
error: error instanceof Error ? error.message : "Unknown error",
741+
duration: Date.now() - itemStartTime,
742+
retries: 0,
743+
});
744+
}
745+
}
746+
747+
const successful = results.filter((r) => r.success).length;
748+
const failed = results.filter((r) => !r.success).length;
749+
const totalDuration = Date.now() - startTime;
750+
751+
this.logger.info("Batch download completed", {
752+
total: cids.length,
753+
successful,
754+
failed,
755+
totalDuration,
756+
});
757+
758+
return {
759+
total: cids.length,
760+
successful,
761+
failed,
762+
successRate: cids.length > 0 ? (successful / cids.length) * 100 : 0,
763+
totalDuration,
764+
averageDuration: results.length > 0 ? totalDuration / results.length : 0,
765+
results,
766+
};
767+
}
768+
601769
/**
602770
* Simulate network delay for realistic behavior
603771
*/

apps/mcp-server/src/tests/integration.test.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,28 @@ describe("Lighthouse MCP Server Integration", () => {
6464

6565
it("should handle missing API key", () => {
6666
expect(() => {
67-
new LighthouseMCPServer({ lighthouseApiKey: undefined });
68-
}).toThrow("LIGHTHOUSE_API_KEY environment variable is required");
67+
new LighthouseMCPServer({
68+
lighthouseApiKey: undefined,
69+
authentication: {
70+
defaultApiKey: undefined,
71+
enablePerRequestAuth: true,
72+
requireAuthentication: true,
73+
keyValidationCache: {
74+
enabled: false,
75+
maxSize: 0,
76+
ttlSeconds: 0,
77+
cleanupIntervalSeconds: 0,
78+
},
79+
rateLimiting: {
80+
enabled: false,
81+
requestsPerMinute: 0,
82+
burstLimit: 0,
83+
keyBasedLimiting: false,
84+
},
85+
},
86+
});
87+
}).toThrow(
88+
"LIGHTHOUSE_API_KEY environment variable or authentication.defaultApiKey is required",
89+
);
6990
});
7091
});

0 commit comments

Comments
 (0)