@172ai/containers-mcp-server 1.12.3 → 1.12.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/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +642 -0
- package/dist/server.js.map +1 -1
- package/dist/services/exportService.d.ts +108 -0
- package/dist/services/exportService.d.ts.map +1 -0
- package/dist/services/exportService.js +659 -0
- package/dist/services/exportService.js.map +1 -0
- package/dist/services/importService.d.ts +90 -0
- package/dist/services/importService.d.ts.map +1 -0
- package/dist/services/importService.js +570 -0
- package/dist/services/importService.js.map +1 -0
- package/dist/services/userNotificationManager.d.ts +18 -0
- package/dist/services/userNotificationManager.d.ts.map +1 -1
- package/dist/services/userNotificationManager.js +44 -0
- package/dist/services/userNotificationManager.js.map +1 -1
- package/dist/types.d.ts +121 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -10,6 +10,8 @@ import { buildService } from './services/buildService.js';
|
|
|
10
10
|
import { fileService } from './services/fileService.js';
|
|
11
11
|
import { capabilityService } from './services/capabilityService.js';
|
|
12
12
|
import { executionService } from './services/executionService.js';
|
|
13
|
+
import { importService } from './services/importService.js';
|
|
14
|
+
import { exportService } from './services/exportService.js';
|
|
13
15
|
import { streamingService } from './services/streamingService.js';
|
|
14
16
|
import { userNotificationManager } from './services/userNotificationManager.js';
|
|
15
17
|
import { userService } from './services/userService.js';
|
|
@@ -29,6 +31,8 @@ class ContainerMCPServer {
|
|
|
29
31
|
// Initialize MCP server reference for progress notifications
|
|
30
32
|
buildService.setMCPServer(this.server);
|
|
31
33
|
executionService.setMCPServer(this.server);
|
|
34
|
+
importService.setMCPServer(this.server);
|
|
35
|
+
exportService.setMCPServer(this.server);
|
|
32
36
|
this.setupTools();
|
|
33
37
|
this.setupErrorHandlers();
|
|
34
38
|
this.initializeStreaming();
|
|
@@ -154,6 +158,32 @@ class ContainerMCPServer {
|
|
|
154
158
|
return await this.handleCancelBuildMonitoring(args);
|
|
155
159
|
case 'get_stream_analytics':
|
|
156
160
|
return await this.handleGetStreamAnalytics(args);
|
|
161
|
+
case 'import_container':
|
|
162
|
+
return await this.handleImportContainer(args);
|
|
163
|
+
case 'get_import_status':
|
|
164
|
+
return await this.handleGetImportStatus(args);
|
|
165
|
+
case 'list_import_jobs':
|
|
166
|
+
return await this.handleListImportJobs(args);
|
|
167
|
+
case 'cancel_import_monitoring':
|
|
168
|
+
return await this.handleCancelImportMonitoring(args);
|
|
169
|
+
case 'get_import_stream_analytics':
|
|
170
|
+
return await this.handleGetImportStreamAnalytics(args);
|
|
171
|
+
case 'export_container':
|
|
172
|
+
return await this.handleExportContainer(args);
|
|
173
|
+
case 'get_export_status':
|
|
174
|
+
return await this.handleGetExportStatus(args);
|
|
175
|
+
case 'list_export_jobs':
|
|
176
|
+
return await this.handleListExportJobs(args);
|
|
177
|
+
case 'get_export_download_url':
|
|
178
|
+
return await this.handleGetExportDownloadUrl(args);
|
|
179
|
+
case 'cancel_export_job':
|
|
180
|
+
return await this.handleCancelExportJob(args);
|
|
181
|
+
case 'cancel_export_monitoring':
|
|
182
|
+
return await this.handleCancelExportMonitoring(args);
|
|
183
|
+
case 'get_export_stream_analytics':
|
|
184
|
+
return await this.handleGetExportStreamAnalytics(args);
|
|
185
|
+
case 'get_export_progress_update':
|
|
186
|
+
return await this.handleGetExportProgressUpdate(args);
|
|
157
187
|
case 'list_container_files':
|
|
158
188
|
return await this.handleListContainerFiles(args);
|
|
159
189
|
case 'get_file_content':
|
|
@@ -735,6 +765,279 @@ Follow same metadata requirements as create_container.`,
|
|
|
735
765
|
}
|
|
736
766
|
}
|
|
737
767
|
},
|
|
768
|
+
// Import Management Tools
|
|
769
|
+
{
|
|
770
|
+
name: 'import_container',
|
|
771
|
+
description: 'Import a container from Docker Hub or GitHub repository with optional progress tracking',
|
|
772
|
+
inputSchema: {
|
|
773
|
+
type: 'object',
|
|
774
|
+
properties: {
|
|
775
|
+
sourceType: {
|
|
776
|
+
type: 'string',
|
|
777
|
+
enum: ['docker', 'git'],
|
|
778
|
+
description: 'Source type: "docker" for Docker Hub or "git" for GitHub repository'
|
|
779
|
+
},
|
|
780
|
+
sourceUrl: {
|
|
781
|
+
type: 'string',
|
|
782
|
+
description: 'Source URL (e.g., "nginx:latest" for Docker or "https://github.com/user/repo" for Git)'
|
|
783
|
+
},
|
|
784
|
+
name: {
|
|
785
|
+
type: 'string',
|
|
786
|
+
description: 'Optional custom name for the imported container'
|
|
787
|
+
},
|
|
788
|
+
dockerfilePath: {
|
|
789
|
+
type: 'string',
|
|
790
|
+
description: 'Optional Dockerfile path for Git imports (default: Dockerfile)'
|
|
791
|
+
},
|
|
792
|
+
dockerHubUsername: {
|
|
793
|
+
type: 'string',
|
|
794
|
+
description: 'Optional Docker Hub username for private images'
|
|
795
|
+
},
|
|
796
|
+
dockerHubToken: {
|
|
797
|
+
type: 'string',
|
|
798
|
+
description: 'Optional Docker Hub access token for private images'
|
|
799
|
+
},
|
|
800
|
+
progressToken: {
|
|
801
|
+
type: 'string',
|
|
802
|
+
description: 'Optional token for receiving real-time progress notifications during import'
|
|
803
|
+
}
|
|
804
|
+
},
|
|
805
|
+
required: ['sourceType', 'sourceUrl']
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
name: 'get_import_status',
|
|
810
|
+
description: 'Get the status of a container import job',
|
|
811
|
+
inputSchema: {
|
|
812
|
+
type: 'object',
|
|
813
|
+
properties: {
|
|
814
|
+
jobId: {
|
|
815
|
+
type: 'string',
|
|
816
|
+
description: 'ID of the import job'
|
|
817
|
+
}
|
|
818
|
+
},
|
|
819
|
+
required: ['jobId']
|
|
820
|
+
}
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
name: 'list_import_jobs',
|
|
824
|
+
description: 'List user\'s container import jobs',
|
|
825
|
+
inputSchema: {
|
|
826
|
+
type: 'object',
|
|
827
|
+
properties: {
|
|
828
|
+
status: {
|
|
829
|
+
type: 'string',
|
|
830
|
+
enum: ['pending', 'queued', 'processing', 'completed', 'failed'],
|
|
831
|
+
description: 'Filter by import status'
|
|
832
|
+
},
|
|
833
|
+
limit: {
|
|
834
|
+
type: 'number',
|
|
835
|
+
minimum: 1,
|
|
836
|
+
maximum: 100,
|
|
837
|
+
description: 'Maximum number of import jobs to return'
|
|
838
|
+
},
|
|
839
|
+
offset: {
|
|
840
|
+
type: 'number',
|
|
841
|
+
minimum: 0,
|
|
842
|
+
description: 'Number of import jobs to skip'
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
name: 'cancel_import_monitoring',
|
|
849
|
+
description: 'Cancel progress monitoring for a specific import using its progress token',
|
|
850
|
+
inputSchema: {
|
|
851
|
+
type: 'object',
|
|
852
|
+
properties: {
|
|
853
|
+
progressToken: {
|
|
854
|
+
type: 'string',
|
|
855
|
+
description: 'Progress token of the import monitoring to cancel'
|
|
856
|
+
},
|
|
857
|
+
reason: {
|
|
858
|
+
type: 'string',
|
|
859
|
+
description: 'Optional reason for cancellation'
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
required: ['progressToken']
|
|
863
|
+
}
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
name: 'get_import_stream_analytics',
|
|
867
|
+
description: 'Get real-time analytics and statistics for import streaming operations',
|
|
868
|
+
inputSchema: {
|
|
869
|
+
type: 'object',
|
|
870
|
+
properties: {
|
|
871
|
+
includeActiveMonitors: {
|
|
872
|
+
type: 'boolean',
|
|
873
|
+
description: 'Whether to include details of currently active import monitors'
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
},
|
|
878
|
+
// Export Management Tools
|
|
879
|
+
{
|
|
880
|
+
name: 'export_container',
|
|
881
|
+
description: 'Export a container to Docker Hub, GitHub, or as an archive with optional progress tracking',
|
|
882
|
+
inputSchema: {
|
|
883
|
+
type: 'object',
|
|
884
|
+
properties: {
|
|
885
|
+
containerId: {
|
|
886
|
+
type: 'string',
|
|
887
|
+
description: 'ID of the container to export'
|
|
888
|
+
},
|
|
889
|
+
destinationType: {
|
|
890
|
+
type: 'string',
|
|
891
|
+
enum: ['docker', 'git', 'archive'],
|
|
892
|
+
description: 'Export destination type: "docker" for Docker Hub, "git" for GitHub, "archive" for downloadable tar.gz/zip'
|
|
893
|
+
},
|
|
894
|
+
destination: {
|
|
895
|
+
type: 'object',
|
|
896
|
+
description: 'Destination configuration (registry/repository/tag for Docker, repositoryName/branch/token for GitHub, format for archive)',
|
|
897
|
+
properties: {
|
|
898
|
+
registry: { type: 'string', description: 'Docker registry (e.g., docker.io, gcr.io, ghcr.io)' },
|
|
899
|
+
repository: { type: 'string', description: 'Docker repository (e.g., username/imagename)' },
|
|
900
|
+
tag: { type: 'string', description: 'Docker image tag (e.g., latest, v1.0.0)' },
|
|
901
|
+
username: { type: 'string', description: 'Docker Hub username' },
|
|
902
|
+
token: { type: 'string', description: 'Docker Hub token/password' },
|
|
903
|
+
repositoryName: { type: 'string', description: 'GitHub repository (e.g., username/repo-name)' },
|
|
904
|
+
branch: { type: 'string', description: 'GitHub branch (e.g., main, develop)' },
|
|
905
|
+
githubToken: { type: 'string', description: 'GitHub Personal Access Token' },
|
|
906
|
+
archiveFormat: { type: 'string', enum: ['tar.gz', 'zip'], description: 'Archive format' }
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
options: {
|
|
910
|
+
type: 'object',
|
|
911
|
+
description: 'Export options',
|
|
912
|
+
properties: {
|
|
913
|
+
includeDockerfile: { type: 'boolean', description: 'Include Dockerfile in export' },
|
|
914
|
+
includeMetadata: { type: 'boolean', description: 'Include metadata.json in export' }
|
|
915
|
+
}
|
|
916
|
+
},
|
|
917
|
+
progressToken: {
|
|
918
|
+
type: 'string',
|
|
919
|
+
description: 'Optional token for receiving real-time progress notifications during export'
|
|
920
|
+
}
|
|
921
|
+
},
|
|
922
|
+
required: ['containerId', 'destinationType', 'destination']
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
name: 'get_export_status',
|
|
927
|
+
description: 'Get the status of a container export job',
|
|
928
|
+
inputSchema: {
|
|
929
|
+
type: 'object',
|
|
930
|
+
properties: {
|
|
931
|
+
jobId: {
|
|
932
|
+
type: 'string',
|
|
933
|
+
description: 'ID of the export job'
|
|
934
|
+
}
|
|
935
|
+
},
|
|
936
|
+
required: ['jobId']
|
|
937
|
+
}
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
name: 'list_export_jobs',
|
|
941
|
+
description: 'List user\'s container export jobs',
|
|
942
|
+
inputSchema: {
|
|
943
|
+
type: 'object',
|
|
944
|
+
properties: {
|
|
945
|
+
containerId: {
|
|
946
|
+
type: 'string',
|
|
947
|
+
description: 'Filter by container ID (optional)'
|
|
948
|
+
},
|
|
949
|
+
status: {
|
|
950
|
+
type: 'string',
|
|
951
|
+
enum: ['pending', 'validating', 'building', 'preparing', 'authenticating', 'uploading', 'pushing', 'verifying', 'completed', 'failed', 'cancelled'],
|
|
952
|
+
description: 'Filter by export status'
|
|
953
|
+
},
|
|
954
|
+
limit: {
|
|
955
|
+
type: 'number',
|
|
956
|
+
minimum: 1,
|
|
957
|
+
maximum: 100,
|
|
958
|
+
description: 'Maximum number of export jobs to return'
|
|
959
|
+
},
|
|
960
|
+
offset: {
|
|
961
|
+
type: 'number',
|
|
962
|
+
minimum: 0,
|
|
963
|
+
description: 'Number of export jobs to skip'
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
name: 'get_export_download_url',
|
|
970
|
+
description: 'Get download URL for an archive export',
|
|
971
|
+
inputSchema: {
|
|
972
|
+
type: 'object',
|
|
973
|
+
properties: {
|
|
974
|
+
jobId: {
|
|
975
|
+
type: 'string',
|
|
976
|
+
description: 'ID of the export job'
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
required: ['jobId']
|
|
980
|
+
}
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
name: 'cancel_export_job',
|
|
984
|
+
description: 'Cancel an in-progress export job',
|
|
985
|
+
inputSchema: {
|
|
986
|
+
type: 'object',
|
|
987
|
+
properties: {
|
|
988
|
+
jobId: {
|
|
989
|
+
type: 'string',
|
|
990
|
+
description: 'ID of the export job to cancel'
|
|
991
|
+
}
|
|
992
|
+
},
|
|
993
|
+
required: ['jobId']
|
|
994
|
+
}
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
name: 'cancel_export_monitoring',
|
|
998
|
+
description: 'Cancel progress monitoring for a specific export using its progress token',
|
|
999
|
+
inputSchema: {
|
|
1000
|
+
type: 'object',
|
|
1001
|
+
properties: {
|
|
1002
|
+
progressToken: {
|
|
1003
|
+
type: 'string',
|
|
1004
|
+
description: 'Progress token of the export monitoring to cancel'
|
|
1005
|
+
},
|
|
1006
|
+
reason: {
|
|
1007
|
+
type: 'string',
|
|
1008
|
+
description: 'Optional reason for cancellation'
|
|
1009
|
+
}
|
|
1010
|
+
},
|
|
1011
|
+
required: ['progressToken']
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
name: 'get_export_stream_analytics',
|
|
1016
|
+
description: 'Get real-time analytics and statistics for export streaming operations',
|
|
1017
|
+
inputSchema: {
|
|
1018
|
+
type: 'object',
|
|
1019
|
+
properties: {
|
|
1020
|
+
includeActiveMonitors: {
|
|
1021
|
+
type: 'boolean',
|
|
1022
|
+
description: 'Whether to include details of currently active export monitors'
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
name: 'get_export_progress_update',
|
|
1029
|
+
description: 'Get the latest progress update for an export progress token',
|
|
1030
|
+
inputSchema: {
|
|
1031
|
+
type: 'object',
|
|
1032
|
+
properties: {
|
|
1033
|
+
progressToken: {
|
|
1034
|
+
type: 'string',
|
|
1035
|
+
description: 'Progress token to check for updates'
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
required: ['progressToken']
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
738
1041
|
// File Management Tools
|
|
739
1042
|
{
|
|
740
1043
|
name: 'list_container_files',
|
|
@@ -1636,6 +1939,345 @@ Follow same metadata requirements as create_container.`,
|
|
|
1636
1939
|
],
|
|
1637
1940
|
};
|
|
1638
1941
|
}
|
|
1942
|
+
// Import handler methods
|
|
1943
|
+
async handleImportContainer(args) {
|
|
1944
|
+
const { progressToken, ...importParams } = args;
|
|
1945
|
+
const result = await importService.importContainer(importParams, progressToken);
|
|
1946
|
+
return {
|
|
1947
|
+
content: [
|
|
1948
|
+
{
|
|
1949
|
+
type: 'text',
|
|
1950
|
+
text: `Container import started successfully!\n\n` +
|
|
1951
|
+
`Job ID: ${result.jobId}\n` +
|
|
1952
|
+
`Source Type: ${result.sourceType}\n` +
|
|
1953
|
+
`Source URL: ${result.sourceUrl}\n` +
|
|
1954
|
+
`Status: ${result.status}\n` +
|
|
1955
|
+
`${result.tokenCost ? `Token Cost: ${result.tokenCost}\n` : ''}` +
|
|
1956
|
+
`${result.estimatedDuration ? `Estimated Duration: ${result.estimatedDuration}\n` : ''}` +
|
|
1957
|
+
`Created: ${result.createdAt}\n` +
|
|
1958
|
+
`${progressToken ? `\n🔄 Progress updates will be sent via notifications using token: ${progressToken}` : ''}`,
|
|
1959
|
+
},
|
|
1960
|
+
],
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
async handleGetImportStatus(args) {
|
|
1964
|
+
const result = await importService.getImportStatus(args.jobId);
|
|
1965
|
+
let statusText = `**Import Job Status**\n\n` +
|
|
1966
|
+
`Job ID: ${result.jobId}\n` +
|
|
1967
|
+
`Status: ${result.status}\n` +
|
|
1968
|
+
`Source Type: ${result.sourceType}\n` +
|
|
1969
|
+
`Source URL: ${result.sourceUrl}\n` +
|
|
1970
|
+
`Created: ${result.createdAt}\n` +
|
|
1971
|
+
`${result.updatedAt ? `Updated: ${result.updatedAt}\n` : ''}` +
|
|
1972
|
+
`${result.completedAt ? `Completed: ${result.completedAt}\n` : ''}`;
|
|
1973
|
+
if (result.progress) {
|
|
1974
|
+
statusText += `\n**Progress**\n` +
|
|
1975
|
+
`Step: ${result.progress.step} (${result.progress.stepNumber}/${result.progress.totalSteps})\n` +
|
|
1976
|
+
`Message: ${result.progress.message}\n`;
|
|
1977
|
+
}
|
|
1978
|
+
if (result.containerId) {
|
|
1979
|
+
statusText += `\n✅ **Container Created**: ${result.containerId}\n`;
|
|
1980
|
+
}
|
|
1981
|
+
if (result.errors && result.errors.length > 0) {
|
|
1982
|
+
statusText += `\n❌ **Errors**:\n${result.errors.map(e => ` • ${e}`).join('\n')}\n`;
|
|
1983
|
+
}
|
|
1984
|
+
return {
|
|
1985
|
+
content: [
|
|
1986
|
+
{
|
|
1987
|
+
type: 'text',
|
|
1988
|
+
text: statusText,
|
|
1989
|
+
},
|
|
1990
|
+
],
|
|
1991
|
+
};
|
|
1992
|
+
}
|
|
1993
|
+
async handleListImportJobs(args) {
|
|
1994
|
+
const result = await importService.listImportJobs(args);
|
|
1995
|
+
let listText = `**Import Jobs** (${result.imports.length} found)\n\n`;
|
|
1996
|
+
if (result.imports.length === 0) {
|
|
1997
|
+
listText += `No import jobs found.\n`;
|
|
1998
|
+
}
|
|
1999
|
+
else {
|
|
2000
|
+
result.imports.forEach((job, index) => {
|
|
2001
|
+
listText += `${index + 1}. **${job.jobId}**\n` +
|
|
2002
|
+
` • Status: ${job.status}\n` +
|
|
2003
|
+
` • Source: ${job.sourceType} - ${job.sourceUrl}\n` +
|
|
2004
|
+
` • Created: ${job.createdAt}\n` +
|
|
2005
|
+
` ${job.containerId ? `• Container: ${job.containerId}\n` : ''}` +
|
|
2006
|
+
` ${job.errors && job.errors.length > 0 ? `• Errors: ${job.errors.length}\n` : ''}\n`;
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
if (result.pagination) {
|
|
2010
|
+
listText += `\n**Pagination**\n` +
|
|
2011
|
+
`• Showing: ${result.pagination.offset + 1}-${Math.min(result.pagination.offset + result.pagination.limit, result.pagination.total)}\n` +
|
|
2012
|
+
`• Total: ${result.pagination.total}\n` +
|
|
2013
|
+
`• Has More: ${result.pagination.hasMore ? 'Yes' : 'No'}\n`;
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
content: [
|
|
2017
|
+
{
|
|
2018
|
+
type: 'text',
|
|
2019
|
+
text: listText,
|
|
2020
|
+
},
|
|
2021
|
+
],
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
async handleCancelImportMonitoring(args) {
|
|
2025
|
+
const success = await importService.cancelImportMonitoring(args.progressToken, args.reason);
|
|
2026
|
+
return {
|
|
2027
|
+
content: [
|
|
2028
|
+
{
|
|
2029
|
+
type: 'text',
|
|
2030
|
+
text: success
|
|
2031
|
+
? `✅ Import monitoring cancelled for progress token: ${args.progressToken}\n${args.reason ? `Reason: ${args.reason}` : ''}`
|
|
2032
|
+
: `❌ No active monitoring found for progress token: ${args.progressToken}`,
|
|
2033
|
+
},
|
|
2034
|
+
],
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
async handleGetImportStreamAnalytics(args) {
|
|
2038
|
+
const analytics = importService.getStreamAnalytics();
|
|
2039
|
+
const includeMonitors = args.includeActiveMonitors !== false; // Default to true
|
|
2040
|
+
let analyticsText = `**Import Streaming Analytics**\n\n` +
|
|
2041
|
+
`📊 **Overview**\n` +
|
|
2042
|
+
`• Total Imports: ${analytics.totalImports}\n` +
|
|
2043
|
+
`• Active Imports: ${analytics.activeImports}\n` +
|
|
2044
|
+
`• Completed Imports: ${analytics.completedImports}\n` +
|
|
2045
|
+
`• Failed Imports: ${analytics.failedImports}\n` +
|
|
2046
|
+
`• Average Import Time: ${Math.round(analytics.averageImportTime / 1000)}s\n` +
|
|
2047
|
+
`• Notifications Sent: ${analytics.notificationsSent}\n` +
|
|
2048
|
+
`• Errors Encountered: ${analytics.errorsEncountered}\n` +
|
|
2049
|
+
`• Last Activity: ${analytics.lastActivity}\n`;
|
|
2050
|
+
if (includeMonitors && analytics.activeStreams?.length > 0) {
|
|
2051
|
+
analyticsText += `\n🔄 **Active Monitors (${analytics.activeStreams.length})**\n`;
|
|
2052
|
+
analytics.activeStreams.forEach((stream, index) => {
|
|
2053
|
+
const elapsed = Math.round((Date.now() - stream.startTime) / 1000);
|
|
2054
|
+
analyticsText += `${index + 1}. **${stream.jobId}**\n` +
|
|
2055
|
+
` • Source Type: ${stream.sourceType}\n` +
|
|
2056
|
+
` • Source URL: ${stream.sourceUrl}\n` +
|
|
2057
|
+
` • Progress Token: ${stream.progressToken}\n` +
|
|
2058
|
+
` • Status: ${stream.status}\n` +
|
|
2059
|
+
` • Progress: ${stream.lastProgress}%\n` +
|
|
2060
|
+
` • Elapsed: ${elapsed}s\n\n`;
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
else if (includeMonitors) {
|
|
2064
|
+
analyticsText += `\n✅ **No Active Monitors**\n`;
|
|
2065
|
+
}
|
|
2066
|
+
return {
|
|
2067
|
+
content: [
|
|
2068
|
+
{
|
|
2069
|
+
type: 'text',
|
|
2070
|
+
text: analyticsText,
|
|
2071
|
+
},
|
|
2072
|
+
],
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
// Export Handler Methods
|
|
2076
|
+
async handleExportContainer(args) {
|
|
2077
|
+
const { progressToken, ...exportParams } = args;
|
|
2078
|
+
const result = await exportService.exportContainer(exportParams, progressToken);
|
|
2079
|
+
return {
|
|
2080
|
+
content: [
|
|
2081
|
+
{
|
|
2082
|
+
type: 'text',
|
|
2083
|
+
text: `Container export started successfully!\n\n` +
|
|
2084
|
+
`Job ID: ${result.jobId}\n` +
|
|
2085
|
+
`Container: ${result.containerName}\n` +
|
|
2086
|
+
`Destination Type: ${result.destinationType}\n` +
|
|
2087
|
+
`Status: ${result.status}\n` +
|
|
2088
|
+
`Token Cost: ${result.tokenCost}\n` +
|
|
2089
|
+
`${result.estimatedDuration ? `Estimated Duration: ${result.estimatedDuration}\n` : ''}` +
|
|
2090
|
+
`Created: ${result.createdAt}\n` +
|
|
2091
|
+
`${progressToken ? `\n🔄 Progress updates will be sent via notifications using token: ${progressToken}` : ''}`,
|
|
2092
|
+
},
|
|
2093
|
+
],
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
async handleGetExportStatus(args) {
|
|
2097
|
+
const result = await exportService.getExportStatus(args.jobId);
|
|
2098
|
+
let statusText = `**Export Job Status**\n\n` +
|
|
2099
|
+
`Job ID: ${result.jobId}\n` +
|
|
2100
|
+
`Container: ${result.containerName}\n` +
|
|
2101
|
+
`Status: ${result.status}\n` +
|
|
2102
|
+
`Destination Type: ${result.destinationType}\n` +
|
|
2103
|
+
`Token Cost: ${result.tokenCost}\n` +
|
|
2104
|
+
`Created: ${result.createdAt}\n` +
|
|
2105
|
+
`${result.updatedAt ? `Updated: ${result.updatedAt}\n` : ''}` +
|
|
2106
|
+
`${result.completedAt ? `Completed: ${result.completedAt}\n` : ''}`;
|
|
2107
|
+
if (result.progress) {
|
|
2108
|
+
statusText += `\n**Progress**\n` +
|
|
2109
|
+
`Stage: ${result.progress.stage} (${result.progress.stageNumber}/${result.progress.totalStages})\n` +
|
|
2110
|
+
`Progress: ${result.progress.percent}%\n` +
|
|
2111
|
+
`Message: ${result.progress.message}\n`;
|
|
2112
|
+
}
|
|
2113
|
+
if (result.result) {
|
|
2114
|
+
statusText += `\n✅ **Export Result**:\n`;
|
|
2115
|
+
if (result.result.imageUrl) {
|
|
2116
|
+
statusText += ` • Image URL: ${result.result.imageUrl}\n`;
|
|
2117
|
+
statusText += ` • Digest: ${result.result.digest || 'N/A'}\n`;
|
|
2118
|
+
statusText += ` • Registry: ${result.result.registry || 'N/A'}\n`;
|
|
2119
|
+
}
|
|
2120
|
+
if (result.result.repositoryUrl) {
|
|
2121
|
+
statusText += ` • Repository: ${result.result.repositoryUrl}\n`;
|
|
2122
|
+
statusText += ` • Branch: ${result.result.branch || 'N/A'}\n`;
|
|
2123
|
+
statusText += ` • Commit: ${result.result.commit || 'N/A'}\n`;
|
|
2124
|
+
}
|
|
2125
|
+
if (result.result.downloadUrl) {
|
|
2126
|
+
statusText += ` • Download URL: ${result.result.downloadUrl}\n`;
|
|
2127
|
+
statusText += ` • Expires: ${result.result.expiresAt || 'N/A'}\n`;
|
|
2128
|
+
statusText += ` • Format: ${result.result.format || 'N/A'}\n`;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
if (result.errors && result.errors.length > 0) {
|
|
2132
|
+
statusText += `\n❌ **Errors**:\n${result.errors.map(e => ` • ${e}`).join('\n')}\n`;
|
|
2133
|
+
}
|
|
2134
|
+
return {
|
|
2135
|
+
content: [
|
|
2136
|
+
{
|
|
2137
|
+
type: 'text',
|
|
2138
|
+
text: statusText,
|
|
2139
|
+
},
|
|
2140
|
+
],
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
async handleListExportJobs(args) {
|
|
2144
|
+
const result = await exportService.listExportJobs(args);
|
|
2145
|
+
let listText = `**Export Jobs** (${result.exports.length} found)\n\n`;
|
|
2146
|
+
if (result.exports.length === 0) {
|
|
2147
|
+
listText += `No export jobs found.\n`;
|
|
2148
|
+
}
|
|
2149
|
+
else {
|
|
2150
|
+
result.exports.forEach((job, index) => {
|
|
2151
|
+
listText += `${index + 1}. **${job.jobId}**\n` +
|
|
2152
|
+
` • Container: ${job.containerName}\n` +
|
|
2153
|
+
` • Status: ${job.status}\n` +
|
|
2154
|
+
` • Destination: ${job.destinationType}\n` +
|
|
2155
|
+
` • Progress: ${job.progress.percent}%\n` +
|
|
2156
|
+
` • Created: ${job.createdAt}\n` +
|
|
2157
|
+
` ${job.errors && job.errors.length > 0 ? `• Errors: ${job.errors.length}\n` : ''}\n`;
|
|
2158
|
+
});
|
|
2159
|
+
}
|
|
2160
|
+
if (result.pagination) {
|
|
2161
|
+
listText += `\n**Pagination**\n` +
|
|
2162
|
+
`• Showing: ${result.pagination.offset + 1}-${Math.min(result.pagination.offset + result.pagination.limit, result.pagination.total)}\n` +
|
|
2163
|
+
`• Total: ${result.pagination.total}\n` +
|
|
2164
|
+
`• Has More: ${result.pagination.hasMore ? 'Yes' : 'No'}\n`;
|
|
2165
|
+
}
|
|
2166
|
+
return {
|
|
2167
|
+
content: [
|
|
2168
|
+
{
|
|
2169
|
+
type: 'text',
|
|
2170
|
+
text: listText,
|
|
2171
|
+
},
|
|
2172
|
+
],
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
async handleGetExportDownloadUrl(args) {
|
|
2176
|
+
const result = await exportService.getExportDownloadUrl(args.jobId);
|
|
2177
|
+
return {
|
|
2178
|
+
content: [
|
|
2179
|
+
{
|
|
2180
|
+
type: 'text',
|
|
2181
|
+
text: `**Export Download URL**\n\n` +
|
|
2182
|
+
`Download URL: ${result.downloadUrl}\n` +
|
|
2183
|
+
`Expires: ${result.expiresAt}\n` +
|
|
2184
|
+
`Format: ${result.format}\n` +
|
|
2185
|
+
`Size: ${result.size} bytes\n`,
|
|
2186
|
+
},
|
|
2187
|
+
],
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
async handleCancelExportJob(args) {
|
|
2191
|
+
const result = await exportService.cancelExportJob(args.jobId);
|
|
2192
|
+
return {
|
|
2193
|
+
content: [
|
|
2194
|
+
{
|
|
2195
|
+
type: 'text',
|
|
2196
|
+
text: `✅ Export job cancelled\n\n` +
|
|
2197
|
+
`Job ID: ${result.jobId}\n` +
|
|
2198
|
+
`Status: ${result.status}\n` +
|
|
2199
|
+
`Updated: ${result.updatedAt}`,
|
|
2200
|
+
},
|
|
2201
|
+
],
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
async handleCancelExportMonitoring(args) {
|
|
2205
|
+
const success = await exportService.cancelExportMonitoring(args.progressToken, args.reason);
|
|
2206
|
+
return {
|
|
2207
|
+
content: [
|
|
2208
|
+
{
|
|
2209
|
+
type: 'text',
|
|
2210
|
+
text: success
|
|
2211
|
+
? `✅ Export monitoring cancelled for progress token: ${args.progressToken}\n${args.reason ? `Reason: ${args.reason}` : ''}`
|
|
2212
|
+
: `❌ No active monitoring found for progress token: ${args.progressToken}`,
|
|
2213
|
+
},
|
|
2214
|
+
],
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
async handleGetExportStreamAnalytics(args) {
|
|
2218
|
+
const analytics = exportService.getStreamAnalytics();
|
|
2219
|
+
const includeMonitors = args.includeActiveMonitors !== false; // Default to true
|
|
2220
|
+
let analyticsText = `**Export Streaming Analytics**\n\n` +
|
|
2221
|
+
`📊 **Overview**\n` +
|
|
2222
|
+
`• Total Exports: ${analytics.totalExports}\n` +
|
|
2223
|
+
`• Active Exports: ${analytics.activeExports}\n` +
|
|
2224
|
+
`• Completed Exports: ${analytics.completedExports}\n` +
|
|
2225
|
+
`• Failed Exports: ${analytics.failedExports}\n` +
|
|
2226
|
+
`• Average Export Time: ${Math.round(analytics.averageExportTime / 1000)}s\n` +
|
|
2227
|
+
`• Notifications Sent: ${analytics.notificationsSent}\n` +
|
|
2228
|
+
`• Errors Encountered: ${analytics.errorsEncountered}\n` +
|
|
2229
|
+
`• Last Activity: ${analytics.lastActivity}\n`;
|
|
2230
|
+
if (includeMonitors && analytics.activeStreams?.length > 0) {
|
|
2231
|
+
analyticsText += `\n🔄 **Active Monitors (${analytics.activeStreams.length})**\n`;
|
|
2232
|
+
analytics.activeStreams.forEach((stream, index) => {
|
|
2233
|
+
const elapsed = Math.round((Date.now() - stream.startTime) / 1000);
|
|
2234
|
+
analyticsText += `${index + 1}. **${stream.jobId}**\n` +
|
|
2235
|
+
` • Container: ${stream.containerName}\n` +
|
|
2236
|
+
` • Destination: ${stream.destinationType}\n` +
|
|
2237
|
+
` • Progress Token: ${stream.progressToken}\n` +
|
|
2238
|
+
` • Status: ${stream.status}\n` +
|
|
2239
|
+
` • Progress: ${stream.lastProgress}%\n` +
|
|
2240
|
+
` • Elapsed: ${elapsed}s\n\n`;
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
else if (includeMonitors) {
|
|
2244
|
+
analyticsText += `\n✅ **No Active Monitors**\n`;
|
|
2245
|
+
}
|
|
2246
|
+
return {
|
|
2247
|
+
content: [
|
|
2248
|
+
{
|
|
2249
|
+
type: 'text',
|
|
2250
|
+
text: analyticsText,
|
|
2251
|
+
},
|
|
2252
|
+
],
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
async handleGetExportProgressUpdate(args) {
|
|
2256
|
+
const update = exportService.getProgressUpdate(args.progressToken);
|
|
2257
|
+
if (!update) {
|
|
2258
|
+
return {
|
|
2259
|
+
content: [
|
|
2260
|
+
{
|
|
2261
|
+
type: 'text',
|
|
2262
|
+
text: `No progress update available for token: ${args.progressToken}`,
|
|
2263
|
+
},
|
|
2264
|
+
],
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
return {
|
|
2268
|
+
content: [
|
|
2269
|
+
{
|
|
2270
|
+
type: 'text',
|
|
2271
|
+
text: `**Export Progress Update**\n\n` +
|
|
2272
|
+
`Progress Token: ${args.progressToken}\n` +
|
|
2273
|
+
`Status: ${update.status || 'unknown'}\n` +
|
|
2274
|
+
`Progress: ${update.progress || 0}%\n` +
|
|
2275
|
+
`Message: ${update.message || 'No message'}\n` +
|
|
2276
|
+
`Timestamp: ${update.timestamp || new Date().toISOString()}`,
|
|
2277
|
+
},
|
|
2278
|
+
],
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
1639
2281
|
async handleListContainerFiles(args) {
|
|
1640
2282
|
const result = await fileService.listContainerFiles(args);
|
|
1641
2283
|
return {
|