aparavi-client 1.0.8 → 1.0.10
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/aparavi-client-1.0.10.tgz +0 -0
- package/dist/cjs/client.js +638 -107
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/schema/DocMetadata.js +1 -0
- package/dist/cjs/schema/DocMetadata.js.map +1 -1
- package/dist/cli/src/client.js +638 -107
- package/dist/cli/src/client.js.map +1 -1
- package/dist/cli/src/schema/DocMetadata.js +1 -0
- package/dist/cli/src/schema/DocMetadata.js.map +1 -1
- package/dist/esm/client.js +638 -107
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/schema/DocMetadata.js +1 -0
- package/dist/esm/schema/DocMetadata.js.map +1 -1
- package/dist/types/client.d.ts +425 -18
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/schema/DocMetadata.d.ts.map +1 -1
- package/dist/types/types/task.d.ts +49 -0
- package/dist/types/types/task.d.ts.map +1 -1
- package/package.json +4 -7
package/dist/esm/client.js
CHANGED
|
@@ -325,11 +325,6 @@ export class AparaviClient extends DAPClient {
|
|
|
325
325
|
static _convertToWebSocketUri(uri) {
|
|
326
326
|
try {
|
|
327
327
|
const url = new URL(uri);
|
|
328
|
-
// If already a WebSocket URI, preserve it
|
|
329
|
-
if (url.protocol === 'wss:' || url.protocol === 'ws:') {
|
|
330
|
-
return `${url.protocol}//${url.host}`;
|
|
331
|
-
}
|
|
332
|
-
// Convert HTTP/HTTPS to WS/WSS
|
|
333
328
|
const wsScheme = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
334
329
|
return `${wsScheme}//${url.host}`;
|
|
335
330
|
}
|
|
@@ -486,6 +481,7 @@ export class AparaviClient extends DAPClient {
|
|
|
486
481
|
* @param options.threads - Number of threads for execution (default: 1)
|
|
487
482
|
* @param options.useExisting - Use existing pipeline instance
|
|
488
483
|
* @param options.args - Command line arguments to pass to pipeline
|
|
484
|
+
* @param options.ttl - Time-to-live in seconds for idle pipelines (optional, server default if not provided; use 0 for no timeout)
|
|
489
485
|
*
|
|
490
486
|
* @returns Promise resolving to an object containing the task token and other metadata
|
|
491
487
|
* @throws Error if neither pipeline nor filepath is provided
|
|
@@ -506,7 +502,7 @@ export class AparaviClient extends DAPClient {
|
|
|
506
502
|
* ```
|
|
507
503
|
*/
|
|
508
504
|
async use(options = {}) {
|
|
509
|
-
const { token, filepath, pipeline, source, threads, useExisting, args } = options;
|
|
505
|
+
const { token, filepath, pipeline, source, threads, useExisting, args, ttl } = options;
|
|
510
506
|
// Validate required parameters
|
|
511
507
|
if (!pipeline && !filepath) {
|
|
512
508
|
throw new Error('Pipeline configuration or file path is required and must be specified');
|
|
@@ -530,8 +526,6 @@ export class AparaviClient extends DAPClient {
|
|
|
530
526
|
let processedConfig = JSON.parse(JSON.stringify(pipelineConfig));
|
|
531
527
|
// Perform environment variable substitution on the pipeline configuration
|
|
532
528
|
processedConfig = this.processEnvSubstitution(processedConfig);
|
|
533
|
-
// Sanitize webhook configs for remote servers (remove port parameter)
|
|
534
|
-
processedConfig = this.sanitizePipelineForRemote(processedConfig);
|
|
535
529
|
// Override source if specified (after substitution)
|
|
536
530
|
if (source !== undefined) {
|
|
537
531
|
processedConfig.pipeline.source = source;
|
|
@@ -541,6 +535,10 @@ export class AparaviClient extends DAPClient {
|
|
|
541
535
|
pipeline: processedConfig,
|
|
542
536
|
args: args || [],
|
|
543
537
|
};
|
|
538
|
+
// Add TTL if provided (server uses its default if not specified)
|
|
539
|
+
if (ttl !== undefined) {
|
|
540
|
+
arguments_.ttl = ttl;
|
|
541
|
+
}
|
|
544
542
|
// Add optional parameters if specified
|
|
545
543
|
if (token !== undefined) {
|
|
546
544
|
arguments_.token = token;
|
|
@@ -570,105 +568,6 @@ export class AparaviClient extends DAPClient {
|
|
|
570
568
|
// Type assertion to ensure token is present
|
|
571
569
|
return responseBody;
|
|
572
570
|
}
|
|
573
|
-
/**
|
|
574
|
-
* Check if the client is connected to a remote server (not localhost).
|
|
575
|
-
* @returns True if connecting to a remote server, False if localhost
|
|
576
|
-
*/
|
|
577
|
-
isRemoteServer() {
|
|
578
|
-
try {
|
|
579
|
-
const uri = this._uri || '';
|
|
580
|
-
if (!uri) {
|
|
581
|
-
return true; // Assume remote if URI not available
|
|
582
|
-
}
|
|
583
|
-
// Normalize URI - remove protocol and check hostname
|
|
584
|
-
let uriLower = uri.toLowerCase();
|
|
585
|
-
// Remove ws://, wss://, http://, https://
|
|
586
|
-
uriLower = uriLower.replace(/^(ws|wss|http|https):\/\//, '');
|
|
587
|
-
// Remove port if present
|
|
588
|
-
uriLower = uriLower.replace(/:\d+/, '');
|
|
589
|
-
// Remove path
|
|
590
|
-
uriLower = uriLower.split('/')[0];
|
|
591
|
-
// Check if it's localhost or local
|
|
592
|
-
const localhostPatterns = ['localhost', '127.0.0.1', '::1', '0.0.0.0', 'local'];
|
|
593
|
-
return !localhostPatterns.some(pattern => uriLower.includes(pattern));
|
|
594
|
-
}
|
|
595
|
-
catch {
|
|
596
|
-
// If we can't determine, assume remote to be safe
|
|
597
|
-
return true;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
/**
|
|
601
|
-
* Remove port parameter from webhook config for remote servers.
|
|
602
|
-
* @param config Webhook component configuration object
|
|
603
|
-
* @returns Sanitized configuration object
|
|
604
|
-
*/
|
|
605
|
-
sanitizeWebhookConfig(config) {
|
|
606
|
-
if (!config || typeof config !== 'object') {
|
|
607
|
-
return config;
|
|
608
|
-
}
|
|
609
|
-
// Check if this is a webhook component
|
|
610
|
-
if (config.type === 'webhook' || String(config.provider || '').toLowerCase().includes('webhook')) {
|
|
611
|
-
// Check parameters
|
|
612
|
-
const parameters = config.parameters;
|
|
613
|
-
if (parameters && typeof parameters === 'object' && 'port' in parameters) {
|
|
614
|
-
// Remove port for remote servers
|
|
615
|
-
if (this.isRemoteServer()) {
|
|
616
|
-
// Create a copy to avoid modifying original
|
|
617
|
-
const sanitized = JSON.parse(JSON.stringify(config));
|
|
618
|
-
const { port, ...restParams } = sanitized.parameters;
|
|
619
|
-
sanitized.parameters = restParams;
|
|
620
|
-
this.debugMessage('Removed port parameter from webhook config for remote server');
|
|
621
|
-
return sanitized;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
return config;
|
|
626
|
-
}
|
|
627
|
-
/**
|
|
628
|
-
* Recursively sanitize pipeline configuration for remote servers.
|
|
629
|
-
* Removes port parameters from webhook components.
|
|
630
|
-
* @param config Pipeline configuration object
|
|
631
|
-
* @returns Sanitized pipeline configuration
|
|
632
|
-
*/
|
|
633
|
-
sanitizePipelineForRemote(config) {
|
|
634
|
-
if (!config || typeof config !== 'object') {
|
|
635
|
-
return config;
|
|
636
|
-
}
|
|
637
|
-
// Create a deep copy to avoid modifying original
|
|
638
|
-
const sanitized = JSON.parse(JSON.stringify(config));
|
|
639
|
-
// Check if this is a pipeline config with components
|
|
640
|
-
if (sanitized.pipeline) {
|
|
641
|
-
const pipeline = sanitized.pipeline;
|
|
642
|
-
if (pipeline.components && Array.isArray(pipeline.components)) {
|
|
643
|
-
// Process each component
|
|
644
|
-
for (let i = 0; i < pipeline.components.length; i++) {
|
|
645
|
-
const component = pipeline.components[i];
|
|
646
|
-
if (component && typeof component === 'object') {
|
|
647
|
-
// Check if this is a webhook component by provider
|
|
648
|
-
const provider = component.provider || '';
|
|
649
|
-
if (provider === 'webhook' && component.config) {
|
|
650
|
-
// Sanitize webhook configs
|
|
651
|
-
pipeline.components[i].config = this.sanitizeWebhookConfig(component.config);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
// Also check direct component arrays (for nested structures)
|
|
658
|
-
if (sanitized.components && Array.isArray(sanitized.components)) {
|
|
659
|
-
for (let i = 0; i < sanitized.components.length; i++) {
|
|
660
|
-
const component = sanitized.components[i];
|
|
661
|
-
if (component && typeof component === 'object') {
|
|
662
|
-
// Check if this is a webhook component by provider
|
|
663
|
-
const provider = component.provider || '';
|
|
664
|
-
if (provider === 'webhook' && component.config) {
|
|
665
|
-
component.config = this.sanitizeWebhookConfig(component.config);
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
return sanitized;
|
|
671
|
-
}
|
|
672
571
|
/**
|
|
673
572
|
* Terminate a running pipeline.
|
|
674
573
|
*/
|
|
@@ -1056,6 +955,638 @@ export class AparaviClient extends DAPClient {
|
|
|
1056
955
|
}
|
|
1057
956
|
}
|
|
1058
957
|
// ============================================================================
|
|
958
|
+
// PROJECT STORAGE MANAGEMENT
|
|
959
|
+
// ============================================================================
|
|
960
|
+
/**
|
|
961
|
+
* Save or update a project pipeline.
|
|
962
|
+
*
|
|
963
|
+
* Stores a project pipeline configuration on the server. If the project
|
|
964
|
+
* already exists, it will be updated. Use expectedVersion to ensure
|
|
965
|
+
* you're updating the version you expect (prevents conflicts).
|
|
966
|
+
*
|
|
967
|
+
* @param options - Save project options
|
|
968
|
+
* @param options.projectId - Unique identifier for the project
|
|
969
|
+
* @param options.pipeline - Pipeline configuration object
|
|
970
|
+
* @param options.expectedVersion - Expected current version for atomic updates (optional)
|
|
971
|
+
* @returns Promise resolving to save result with success status, projectId, and new version
|
|
972
|
+
* @throws Error if save fails due to version mismatch, storage error, or invalid input
|
|
973
|
+
*
|
|
974
|
+
* @example
|
|
975
|
+
* ```typescript
|
|
976
|
+
* // Save a new project
|
|
977
|
+
* const result = await client.saveProject({
|
|
978
|
+
* projectId: 'proj-123',
|
|
979
|
+
* pipeline: {
|
|
980
|
+
* name: 'Data Processor',
|
|
981
|
+
* source: 'source_1',
|
|
982
|
+
* components: [...]
|
|
983
|
+
* }
|
|
984
|
+
* });
|
|
985
|
+
* console.log(`Saved version: ${result.version}`);
|
|
986
|
+
*
|
|
987
|
+
* // Update existing project with version check
|
|
988
|
+
* const existing = await client.getProject({ projectId: 'proj-123' });
|
|
989
|
+
* existing.pipeline.name = 'Updated Name';
|
|
990
|
+
* const updated = await client.saveProject({
|
|
991
|
+
* projectId: 'proj-123',
|
|
992
|
+
* pipeline: existing.pipeline,
|
|
993
|
+
* expectedVersion: existing.version
|
|
994
|
+
* });
|
|
995
|
+
* ```
|
|
996
|
+
*/
|
|
997
|
+
async saveProject(options) {
|
|
998
|
+
const { projectId, pipeline, expectedVersion } = options;
|
|
999
|
+
// Validate inputs
|
|
1000
|
+
if (!projectId) {
|
|
1001
|
+
throw new Error('projectId is required');
|
|
1002
|
+
}
|
|
1003
|
+
if (!pipeline || typeof pipeline !== 'object') {
|
|
1004
|
+
throw new Error('pipeline must be a non-empty object');
|
|
1005
|
+
}
|
|
1006
|
+
// Build request arguments
|
|
1007
|
+
const args = {
|
|
1008
|
+
subcommand: 'save_project',
|
|
1009
|
+
projectId,
|
|
1010
|
+
pipeline,
|
|
1011
|
+
};
|
|
1012
|
+
// Add optional version for atomic updates
|
|
1013
|
+
if (expectedVersion !== undefined) {
|
|
1014
|
+
args.expectedVersion = expectedVersion;
|
|
1015
|
+
}
|
|
1016
|
+
// Send request to server
|
|
1017
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1018
|
+
const response = await this.request(request);
|
|
1019
|
+
// Check for errors
|
|
1020
|
+
if (this.didFail(response)) {
|
|
1021
|
+
const errorMsg = response.message || 'Unknown error saving project';
|
|
1022
|
+
this.debugMessage(`Project save failed: ${errorMsg}`);
|
|
1023
|
+
throw new Error(errorMsg);
|
|
1024
|
+
}
|
|
1025
|
+
// Extract and return response
|
|
1026
|
+
this.debugMessage(`Project saved successfully: ${projectId}, version: ${response.body?.version}`);
|
|
1027
|
+
return response.body;
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Retrieve a project by its ID.
|
|
1031
|
+
*
|
|
1032
|
+
* Fetches the complete pipeline configuration and current version for
|
|
1033
|
+
* the specified project. Use this before updating to get the current
|
|
1034
|
+
* version for atomic updates.
|
|
1035
|
+
*
|
|
1036
|
+
* @param options - Get project options
|
|
1037
|
+
* @param options.projectId - Unique identifier of the project to retrieve
|
|
1038
|
+
* @returns Promise resolving to project data with success status, pipeline, and version
|
|
1039
|
+
* @throws Error if project doesn't exist or retrieval fails
|
|
1040
|
+
*
|
|
1041
|
+
* @example
|
|
1042
|
+
* ```typescript
|
|
1043
|
+
* // Get a project
|
|
1044
|
+
* try {
|
|
1045
|
+
* const project = await client.getProject({ projectId: 'proj-123' });
|
|
1046
|
+
* console.log(`Project: ${project.pipeline.name}`);
|
|
1047
|
+
* console.log(`Version: ${project.version}`);
|
|
1048
|
+
* } catch (error) {
|
|
1049
|
+
* if (error.message.includes('NOT_FOUND')) {
|
|
1050
|
+
* console.log("Project doesn't exist");
|
|
1051
|
+
* }
|
|
1052
|
+
* }
|
|
1053
|
+
*
|
|
1054
|
+
* // Before updating - get current version
|
|
1055
|
+
* const project = await client.getProject({ projectId: 'proj-123' });
|
|
1056
|
+
* project.pipeline.name = 'Updated';
|
|
1057
|
+
* await client.saveProject({
|
|
1058
|
+
* projectId: 'proj-123',
|
|
1059
|
+
* pipeline: project.pipeline,
|
|
1060
|
+
* expectedVersion: project.version
|
|
1061
|
+
* });
|
|
1062
|
+
* ```
|
|
1063
|
+
*/
|
|
1064
|
+
async getProject(options) {
|
|
1065
|
+
const { projectId } = options;
|
|
1066
|
+
// Validate inputs
|
|
1067
|
+
if (!projectId) {
|
|
1068
|
+
throw new Error('projectId is required');
|
|
1069
|
+
}
|
|
1070
|
+
// Build request
|
|
1071
|
+
const args = {
|
|
1072
|
+
subcommand: 'get_project',
|
|
1073
|
+
projectId,
|
|
1074
|
+
};
|
|
1075
|
+
// Send request to server
|
|
1076
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1077
|
+
const response = await this.request(request);
|
|
1078
|
+
// Check for errors
|
|
1079
|
+
if (this.didFail(response)) {
|
|
1080
|
+
const errorMsg = response.message || 'Unknown error retrieving project';
|
|
1081
|
+
this.debugMessage(`Project retrieval failed: ${errorMsg}`);
|
|
1082
|
+
throw new Error(errorMsg);
|
|
1083
|
+
}
|
|
1084
|
+
// Extract and return response
|
|
1085
|
+
this.debugMessage(`Project retrieved successfully: ${projectId}`);
|
|
1086
|
+
return response.body;
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Delete a project by its ID.
|
|
1090
|
+
*
|
|
1091
|
+
* Permanently removes a project from storage. Optionally verify the
|
|
1092
|
+
* version before deletion to ensure you're deleting the version you
|
|
1093
|
+
* expect (prevents accidental deletion of modified projects).
|
|
1094
|
+
*
|
|
1095
|
+
* @param options - Delete project options
|
|
1096
|
+
* @param options.projectId - Unique identifier of the project to delete
|
|
1097
|
+
* @param options.expectedVersion - Expected current version for atomic deletion (required)
|
|
1098
|
+
* @returns Promise resolving to deletion result with success status and message
|
|
1099
|
+
* @throws Error if project doesn't exist, version mismatch, or deletion fails
|
|
1100
|
+
*
|
|
1101
|
+
* @example
|
|
1102
|
+
* ```typescript
|
|
1103
|
+
* // Safe deletion with version check
|
|
1104
|
+
* const project = await client.getProject({ projectId: 'proj-123' });
|
|
1105
|
+
* try {
|
|
1106
|
+
* const result = await client.deleteProject({
|
|
1107
|
+
* projectId: 'proj-123',
|
|
1108
|
+
* expectedVersion: project.version
|
|
1109
|
+
* });
|
|
1110
|
+
* console.log('Project deleted successfully');
|
|
1111
|
+
* } catch (error) {
|
|
1112
|
+
* if (error.message.includes('CONFLICT')) {
|
|
1113
|
+
* console.log('Project was modified, deletion cancelled');
|
|
1114
|
+
* }
|
|
1115
|
+
* }
|
|
1116
|
+
* ```
|
|
1117
|
+
*/
|
|
1118
|
+
async deleteProject(options) {
|
|
1119
|
+
const { projectId, expectedVersion } = options;
|
|
1120
|
+
// Validate inputs
|
|
1121
|
+
if (!projectId) {
|
|
1122
|
+
throw new Error('projectId is required');
|
|
1123
|
+
}
|
|
1124
|
+
// Build request
|
|
1125
|
+
const args = {
|
|
1126
|
+
subcommand: 'delete_project',
|
|
1127
|
+
projectId,
|
|
1128
|
+
};
|
|
1129
|
+
// Add optional version for atomic deletion
|
|
1130
|
+
if (expectedVersion !== undefined) {
|
|
1131
|
+
args.expectedVersion = expectedVersion;
|
|
1132
|
+
}
|
|
1133
|
+
// Send request to server
|
|
1134
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1135
|
+
const response = await this.request(request);
|
|
1136
|
+
// Check for errors
|
|
1137
|
+
if (this.didFail(response)) {
|
|
1138
|
+
const errorMsg = response.message || 'Unknown error deleting project';
|
|
1139
|
+
this.debugMessage(`Project deletion failed: ${errorMsg}`);
|
|
1140
|
+
throw new Error(errorMsg);
|
|
1141
|
+
}
|
|
1142
|
+
// Extract and return response
|
|
1143
|
+
this.debugMessage(`Project deleted successfully: ${projectId}`);
|
|
1144
|
+
return response.body;
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* List all projects for the current user.
|
|
1148
|
+
*
|
|
1149
|
+
* Retrieves a summary of all projects stored for the authenticated user.
|
|
1150
|
+
* Each project summary includes the ID, name, list of data sources, and total component count.
|
|
1151
|
+
*
|
|
1152
|
+
* @returns Promise resolving to list result with success status, projects array, and count
|
|
1153
|
+
* @throws Error if retrieval fails
|
|
1154
|
+
*
|
|
1155
|
+
* @example
|
|
1156
|
+
* ```typescript
|
|
1157
|
+
* // List all projects
|
|
1158
|
+
* const result = await client.getAllProjects();
|
|
1159
|
+
* console.log(`Found ${result.count} projects:`);
|
|
1160
|
+
* for (const project of result.projects) {
|
|
1161
|
+
* console.log(`- ${project.id}: ${project.name} (${project.totalComponents} components)`);
|
|
1162
|
+
* for (const source of project.sources) {
|
|
1163
|
+
* console.log(` * ${source.name} (${source.provider})`);
|
|
1164
|
+
* }
|
|
1165
|
+
* }
|
|
1166
|
+
*
|
|
1167
|
+
* // Find specific project
|
|
1168
|
+
* const result = await client.getAllProjects();
|
|
1169
|
+
* const myProject = result.projects.find(p => p.id === 'proj-123');
|
|
1170
|
+
* ```
|
|
1171
|
+
*/
|
|
1172
|
+
async getAllProjects() {
|
|
1173
|
+
// Build request
|
|
1174
|
+
const args = {
|
|
1175
|
+
subcommand: 'get_all_projects',
|
|
1176
|
+
};
|
|
1177
|
+
// Send request to server
|
|
1178
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1179
|
+
const response = await this.request(request);
|
|
1180
|
+
// Check for errors
|
|
1181
|
+
if (this.didFail(response)) {
|
|
1182
|
+
const errorMsg = response.message || 'Unknown error listing projects';
|
|
1183
|
+
this.debugMessage(`Project list retrieval failed: ${errorMsg}`);
|
|
1184
|
+
throw new Error(errorMsg);
|
|
1185
|
+
}
|
|
1186
|
+
// Extract and return response
|
|
1187
|
+
const projectCount = response.body?.count || 0;
|
|
1188
|
+
this.debugMessage(`Projects retrieved successfully: ${projectCount} projects`);
|
|
1189
|
+
return response.body;
|
|
1190
|
+
}
|
|
1191
|
+
// ============================================================================
|
|
1192
|
+
// TEMPLATE STORAGE MANAGEMENT (System-wide templates)
|
|
1193
|
+
// ============================================================================
|
|
1194
|
+
/**
|
|
1195
|
+
* Save or update a template pipeline.
|
|
1196
|
+
*
|
|
1197
|
+
* Stores a template pipeline configuration on the server. Templates are system-wide
|
|
1198
|
+
* and accessible to all users. If the template already exists, it will be updated.
|
|
1199
|
+
* Use expectedVersion to ensure you're updating the version you expect.
|
|
1200
|
+
*
|
|
1201
|
+
* @param options - Save template options
|
|
1202
|
+
* @param options.templateId - Unique identifier for the template
|
|
1203
|
+
* @param options.pipeline - Pipeline configuration object
|
|
1204
|
+
* @param options.expectedVersion - Expected current version for atomic updates (optional)
|
|
1205
|
+
* @returns Promise resolving to save result with success status, templateId, and new version
|
|
1206
|
+
* @throws Error if save fails due to version mismatch, storage error, or invalid input
|
|
1207
|
+
*
|
|
1208
|
+
* @example
|
|
1209
|
+
* ```typescript
|
|
1210
|
+
* // Save a new template
|
|
1211
|
+
* const result = await client.saveTemplate({
|
|
1212
|
+
* templateId: 'tmpl-123',
|
|
1213
|
+
* pipeline: {
|
|
1214
|
+
* source: 'source_1',
|
|
1215
|
+
* pipeline: {
|
|
1216
|
+
* name: 'Data Processor Template',
|
|
1217
|
+
* components: [...]
|
|
1218
|
+
* }
|
|
1219
|
+
* }
|
|
1220
|
+
* });
|
|
1221
|
+
* console.log(`Saved version: ${result.version}`);
|
|
1222
|
+
* ```
|
|
1223
|
+
*/
|
|
1224
|
+
async saveTemplate(options) {
|
|
1225
|
+
const { templateId, pipeline, expectedVersion } = options;
|
|
1226
|
+
// Validate inputs
|
|
1227
|
+
if (!templateId) {
|
|
1228
|
+
throw new Error('templateId is required');
|
|
1229
|
+
}
|
|
1230
|
+
if (!pipeline || typeof pipeline !== 'object') {
|
|
1231
|
+
throw new Error('pipeline must be a non-empty object');
|
|
1232
|
+
}
|
|
1233
|
+
// Build request arguments
|
|
1234
|
+
const args = {
|
|
1235
|
+
subcommand: 'save_template',
|
|
1236
|
+
templateId,
|
|
1237
|
+
pipeline,
|
|
1238
|
+
};
|
|
1239
|
+
// Add optional version for atomic updates
|
|
1240
|
+
if (expectedVersion !== undefined) {
|
|
1241
|
+
args.expectedVersion = expectedVersion;
|
|
1242
|
+
}
|
|
1243
|
+
// Send request to server
|
|
1244
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1245
|
+
const response = await this.request(request);
|
|
1246
|
+
// Check for errors
|
|
1247
|
+
if (this.didFail(response)) {
|
|
1248
|
+
const errorMsg = response.message || 'Unknown error saving template';
|
|
1249
|
+
this.debugMessage(`Template save failed: ${errorMsg}`);
|
|
1250
|
+
throw new Error(errorMsg);
|
|
1251
|
+
}
|
|
1252
|
+
// Extract and return response
|
|
1253
|
+
this.debugMessage(`Template saved successfully: ${templateId}, version: ${response.body?.version}`);
|
|
1254
|
+
return response.body;
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Retrieve a template by its ID.
|
|
1258
|
+
*
|
|
1259
|
+
* Fetches the complete pipeline configuration and current version for
|
|
1260
|
+
* the specified template.
|
|
1261
|
+
*
|
|
1262
|
+
* @param options - Get template options
|
|
1263
|
+
* @param options.templateId - Unique identifier of the template to retrieve
|
|
1264
|
+
* @returns Promise resolving to template data with success status, pipeline, and version
|
|
1265
|
+
* @throws Error if template doesn't exist or retrieval fails
|
|
1266
|
+
*
|
|
1267
|
+
* @example
|
|
1268
|
+
* ```typescript
|
|
1269
|
+
* try {
|
|
1270
|
+
* const template = await client.getTemplate({ templateId: 'tmpl-123' });
|
|
1271
|
+
* console.log(`Template: ${template.pipeline.pipeline.name}`);
|
|
1272
|
+
* console.log(`Version: ${template.version}`);
|
|
1273
|
+
* } catch (error) {
|
|
1274
|
+
* if (error.message.includes('NOT_FOUND')) {
|
|
1275
|
+
* console.log("Template doesn't exist");
|
|
1276
|
+
* }
|
|
1277
|
+
* }
|
|
1278
|
+
* ```
|
|
1279
|
+
*/
|
|
1280
|
+
async getTemplate(options) {
|
|
1281
|
+
const { templateId } = options;
|
|
1282
|
+
// Validate inputs
|
|
1283
|
+
if (!templateId) {
|
|
1284
|
+
throw new Error('templateId is required');
|
|
1285
|
+
}
|
|
1286
|
+
// Build request
|
|
1287
|
+
const args = {
|
|
1288
|
+
subcommand: 'get_template',
|
|
1289
|
+
templateId,
|
|
1290
|
+
};
|
|
1291
|
+
// Send request to server
|
|
1292
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1293
|
+
const response = await this.request(request);
|
|
1294
|
+
// Check for errors
|
|
1295
|
+
if (this.didFail(response)) {
|
|
1296
|
+
const errorMsg = response.message || 'Unknown error retrieving template';
|
|
1297
|
+
this.debugMessage(`Template retrieval failed: ${errorMsg}`);
|
|
1298
|
+
throw new Error(errorMsg);
|
|
1299
|
+
}
|
|
1300
|
+
// Extract and return response
|
|
1301
|
+
this.debugMessage(`Template retrieved successfully: ${templateId}`);
|
|
1302
|
+
return response.body;
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* Delete a template by its ID.
|
|
1306
|
+
*
|
|
1307
|
+
* Permanently removes a template from storage. Optionally verify the
|
|
1308
|
+
* version before deletion to ensure you're deleting the version you expect.
|
|
1309
|
+
*
|
|
1310
|
+
* @param options - Delete template options
|
|
1311
|
+
* @param options.templateId - Unique identifier of the template to delete
|
|
1312
|
+
* @param options.expectedVersion - Expected current version for atomic deletion (optional)
|
|
1313
|
+
* @returns Promise resolving to deletion result with success status and message
|
|
1314
|
+
* @throws Error if template doesn't exist, version mismatch, or deletion fails
|
|
1315
|
+
*
|
|
1316
|
+
* @example
|
|
1317
|
+
* ```typescript
|
|
1318
|
+
* // Safe deletion with version check
|
|
1319
|
+
* const template = await client.getTemplate({ templateId: 'tmpl-123' });
|
|
1320
|
+
* try {
|
|
1321
|
+
* const result = await client.deleteTemplate({
|
|
1322
|
+
* templateId: 'tmpl-123',
|
|
1323
|
+
* expectedVersion: template.version
|
|
1324
|
+
* });
|
|
1325
|
+
* console.log('Template deleted successfully');
|
|
1326
|
+
* } catch (error) {
|
|
1327
|
+
* if (error.message.includes('CONFLICT')) {
|
|
1328
|
+
* console.log('Template was modified, deletion cancelled');
|
|
1329
|
+
* }
|
|
1330
|
+
* }
|
|
1331
|
+
* ```
|
|
1332
|
+
*/
|
|
1333
|
+
async deleteTemplate(options) {
|
|
1334
|
+
const { templateId, expectedVersion } = options;
|
|
1335
|
+
// Validate inputs
|
|
1336
|
+
if (!templateId) {
|
|
1337
|
+
throw new Error('templateId is required');
|
|
1338
|
+
}
|
|
1339
|
+
// Build request
|
|
1340
|
+
const args = {
|
|
1341
|
+
subcommand: 'delete_template',
|
|
1342
|
+
templateId,
|
|
1343
|
+
};
|
|
1344
|
+
// Add optional version for atomic deletion
|
|
1345
|
+
if (expectedVersion !== undefined) {
|
|
1346
|
+
args.expectedVersion = expectedVersion;
|
|
1347
|
+
}
|
|
1348
|
+
// Send request to server
|
|
1349
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1350
|
+
const response = await this.request(request);
|
|
1351
|
+
// Check for errors
|
|
1352
|
+
if (this.didFail(response)) {
|
|
1353
|
+
const errorMsg = response.message || 'Unknown error deleting template';
|
|
1354
|
+
this.debugMessage(`Template deletion failed: ${errorMsg}`);
|
|
1355
|
+
throw new Error(errorMsg);
|
|
1356
|
+
}
|
|
1357
|
+
// Extract and return response
|
|
1358
|
+
this.debugMessage(`Template deleted successfully: ${templateId}`);
|
|
1359
|
+
return response.body;
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* List all templates.
|
|
1363
|
+
*
|
|
1364
|
+
* Retrieves a summary of all templates stored in the system.
|
|
1365
|
+
* Each template summary includes the ID, name, list of data sources, and total component count.
|
|
1366
|
+
*
|
|
1367
|
+
* @returns Promise resolving to list result with success status, templates array, and count
|
|
1368
|
+
* @throws Error if retrieval fails
|
|
1369
|
+
*
|
|
1370
|
+
* @example
|
|
1371
|
+
* ```typescript
|
|
1372
|
+
* // List all templates
|
|
1373
|
+
* const result = await client.getAllTemplates();
|
|
1374
|
+
* console.log(`Found ${result.count} templates:`);
|
|
1375
|
+
* for (const template of result.templates) {
|
|
1376
|
+
* console.log(`- ${template.id}: ${template.name} (${template.totalComponents} components)`);
|
|
1377
|
+
* for (const source of template.sources) {
|
|
1378
|
+
* console.log(` * ${source.name} (${source.provider})`);
|
|
1379
|
+
* }
|
|
1380
|
+
* }
|
|
1381
|
+
* ```
|
|
1382
|
+
*/
|
|
1383
|
+
async getAllTemplates() {
|
|
1384
|
+
// Build request
|
|
1385
|
+
const args = {
|
|
1386
|
+
subcommand: 'get_all_templates',
|
|
1387
|
+
};
|
|
1388
|
+
// Send request to server
|
|
1389
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1390
|
+
const response = await this.request(request);
|
|
1391
|
+
// Check for errors
|
|
1392
|
+
if (this.didFail(response)) {
|
|
1393
|
+
const errorMsg = response.message || 'Unknown error listing templates';
|
|
1394
|
+
this.debugMessage(`Template list retrieval failed: ${errorMsg}`);
|
|
1395
|
+
throw new Error(errorMsg);
|
|
1396
|
+
}
|
|
1397
|
+
// Extract and return response
|
|
1398
|
+
const templateCount = response.body?.count || 0;
|
|
1399
|
+
this.debugMessage(`Templates retrieved successfully: ${templateCount} templates`);
|
|
1400
|
+
return response.body;
|
|
1401
|
+
}
|
|
1402
|
+
// ============================================================================
|
|
1403
|
+
// LOG STORAGE MANAGEMENT (Per-project log files for historical tracking)
|
|
1404
|
+
// ============================================================================
|
|
1405
|
+
/**
|
|
1406
|
+
* Save a log file for a source run.
|
|
1407
|
+
*
|
|
1408
|
+
* Creates or overwrites a log file in the project's log directory.
|
|
1409
|
+
* The filename is constructed as <source>-<startTime>.log where startTime
|
|
1410
|
+
* is extracted from contents.body.startTime.
|
|
1411
|
+
*
|
|
1412
|
+
* @param options - Save log options
|
|
1413
|
+
* @param options.projectId - Project ID
|
|
1414
|
+
* @param options.source - Name of the source
|
|
1415
|
+
* @param options.contents - Log contents object containing body.startTime
|
|
1416
|
+
* @returns Promise resolving to save result with success status and filename
|
|
1417
|
+
* @throws Error if save fails
|
|
1418
|
+
*
|
|
1419
|
+
* @example
|
|
1420
|
+
* ```typescript
|
|
1421
|
+
* const logContents = {
|
|
1422
|
+
* type: 'event',
|
|
1423
|
+
* event: 'apaevt_status_update',
|
|
1424
|
+
* body: {
|
|
1425
|
+
* source: 'source_1',
|
|
1426
|
+
* startTime: 1764337626.6564875,
|
|
1427
|
+
* status: 'Completed',
|
|
1428
|
+
* completed: true,
|
|
1429
|
+
* totalCount: 100,
|
|
1430
|
+
* completedCount: 100
|
|
1431
|
+
* }
|
|
1432
|
+
* };
|
|
1433
|
+
* const result = await client.saveLog({
|
|
1434
|
+
* projectId: 'proj-123',
|
|
1435
|
+
* source: 'source_1',
|
|
1436
|
+
* contents: logContents
|
|
1437
|
+
* });
|
|
1438
|
+
* console.log(`Saved: ${result.filename}`);
|
|
1439
|
+
* ```
|
|
1440
|
+
*/
|
|
1441
|
+
async saveLog(options) {
|
|
1442
|
+
const { projectId, source, contents } = options;
|
|
1443
|
+
// Validate inputs
|
|
1444
|
+
if (!projectId) {
|
|
1445
|
+
throw new Error('projectId is required');
|
|
1446
|
+
}
|
|
1447
|
+
if (!source) {
|
|
1448
|
+
throw new Error('source is required');
|
|
1449
|
+
}
|
|
1450
|
+
if (!contents || typeof contents !== 'object') {
|
|
1451
|
+
throw new Error('contents must be a non-empty object');
|
|
1452
|
+
}
|
|
1453
|
+
// Build request arguments
|
|
1454
|
+
const args = {
|
|
1455
|
+
subcommand: 'save_log',
|
|
1456
|
+
projectId,
|
|
1457
|
+
source,
|
|
1458
|
+
contents,
|
|
1459
|
+
};
|
|
1460
|
+
// Send request to server
|
|
1461
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1462
|
+
const response = await this.request(request);
|
|
1463
|
+
// Check for errors
|
|
1464
|
+
if (this.didFail(response)) {
|
|
1465
|
+
const errorMsg = response.message || 'Unknown error saving log';
|
|
1466
|
+
this.debugMessage(`Log save failed: ${errorMsg}`);
|
|
1467
|
+
throw new Error(errorMsg);
|
|
1468
|
+
}
|
|
1469
|
+
// Extract and return response
|
|
1470
|
+
this.debugMessage(`Log saved successfully: ${response.body?.filename}`);
|
|
1471
|
+
return response.body;
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Get a log file by source name and start time.
|
|
1475
|
+
*
|
|
1476
|
+
* @param options - Get log options
|
|
1477
|
+
* @param options.projectId - Project ID
|
|
1478
|
+
* @param options.source - Name of the source
|
|
1479
|
+
* @param options.startTime - Start time of the run
|
|
1480
|
+
* @returns Promise resolving to log data with success status and contents
|
|
1481
|
+
* @throws Error if log not found or retrieval fails
|
|
1482
|
+
*
|
|
1483
|
+
* @example
|
|
1484
|
+
* ```typescript
|
|
1485
|
+
* const log = await client.getLog({
|
|
1486
|
+
* projectId: 'proj-123',
|
|
1487
|
+
* source: 'source_1',
|
|
1488
|
+
* startTime: 1764337626.6564875
|
|
1489
|
+
* });
|
|
1490
|
+
* console.log(`Status: ${log.contents.body.status}`);
|
|
1491
|
+
* ```
|
|
1492
|
+
*/
|
|
1493
|
+
async getLog(options) {
|
|
1494
|
+
const { projectId, source, startTime } = options;
|
|
1495
|
+
// Validate inputs
|
|
1496
|
+
if (!projectId) {
|
|
1497
|
+
throw new Error('projectId is required');
|
|
1498
|
+
}
|
|
1499
|
+
if (!source) {
|
|
1500
|
+
throw new Error('source is required');
|
|
1501
|
+
}
|
|
1502
|
+
if (startTime === undefined || startTime === null) {
|
|
1503
|
+
throw new Error('startTime is required');
|
|
1504
|
+
}
|
|
1505
|
+
// Build request
|
|
1506
|
+
const args = {
|
|
1507
|
+
subcommand: 'get_log',
|
|
1508
|
+
projectId,
|
|
1509
|
+
source,
|
|
1510
|
+
startTime,
|
|
1511
|
+
};
|
|
1512
|
+
// Send request to server
|
|
1513
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1514
|
+
const response = await this.request(request);
|
|
1515
|
+
// Check for errors
|
|
1516
|
+
if (this.didFail(response)) {
|
|
1517
|
+
const errorMsg = response.message || 'Unknown error retrieving log';
|
|
1518
|
+
this.debugMessage(`Log retrieval failed: ${errorMsg}`);
|
|
1519
|
+
throw new Error(errorMsg);
|
|
1520
|
+
}
|
|
1521
|
+
// Extract and return response
|
|
1522
|
+
this.debugMessage(`Log retrieved successfully: ${projectId}/${source}`);
|
|
1523
|
+
return response.body;
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* List log files for a project.
|
|
1527
|
+
*
|
|
1528
|
+
* @param options - List logs options
|
|
1529
|
+
* @param options.projectId - Project ID
|
|
1530
|
+
* @param options.source - Optional source name to filter logs (filters files starting with '<source>-')
|
|
1531
|
+
* @param options.page - Page number (0-indexed). If negative or undefined, defaults to 0. Page size is 100.
|
|
1532
|
+
* @returns Promise resolving to list result with success status, logs array, counts, and pagination info
|
|
1533
|
+
* @throws Error if retrieval fails
|
|
1534
|
+
*
|
|
1535
|
+
* @example
|
|
1536
|
+
* ```typescript
|
|
1537
|
+
* // List all logs
|
|
1538
|
+
* const result = await client.listLogs({ projectId: 'proj-123' });
|
|
1539
|
+
* console.log(`Found ${result.total_count} logs`);
|
|
1540
|
+
* for (const log of result.logs) {
|
|
1541
|
+
* console.log(` - ${log}`);
|
|
1542
|
+
* }
|
|
1543
|
+
*
|
|
1544
|
+
* // Filter by source
|
|
1545
|
+
* const filtered = await client.listLogs({
|
|
1546
|
+
* projectId: 'proj-123',
|
|
1547
|
+
* source: 'source_1'
|
|
1548
|
+
* });
|
|
1549
|
+
*
|
|
1550
|
+
* // With pagination
|
|
1551
|
+
* const page2 = await client.listLogs({
|
|
1552
|
+
* projectId: 'proj-123',
|
|
1553
|
+
* page: 1
|
|
1554
|
+
* });
|
|
1555
|
+
* ```
|
|
1556
|
+
*/
|
|
1557
|
+
async listLogs(options) {
|
|
1558
|
+
const { projectId, source, page } = options;
|
|
1559
|
+
// Validate inputs
|
|
1560
|
+
if (!projectId) {
|
|
1561
|
+
throw new Error('projectId is required');
|
|
1562
|
+
}
|
|
1563
|
+
// Build request
|
|
1564
|
+
const args = {
|
|
1565
|
+
subcommand: 'list_logs',
|
|
1566
|
+
projectId,
|
|
1567
|
+
};
|
|
1568
|
+
// Add optional parameters
|
|
1569
|
+
if (source !== undefined) {
|
|
1570
|
+
args.source = source;
|
|
1571
|
+
}
|
|
1572
|
+
if (page !== undefined) {
|
|
1573
|
+
args.page = page;
|
|
1574
|
+
}
|
|
1575
|
+
// Send request to server
|
|
1576
|
+
const request = this.buildRequest('apaext_store', { arguments: args });
|
|
1577
|
+
const response = await this.request(request);
|
|
1578
|
+
// Check for errors
|
|
1579
|
+
if (this.didFail(response)) {
|
|
1580
|
+
const errorMsg = response.message || 'Unknown error listing logs';
|
|
1581
|
+
this.debugMessage(`Log list retrieval failed: ${errorMsg}`);
|
|
1582
|
+
throw new Error(errorMsg);
|
|
1583
|
+
}
|
|
1584
|
+
// Extract and return response
|
|
1585
|
+
const logCount = response.body?.total_count || 0;
|
|
1586
|
+
this.debugMessage(`Logs retrieved successfully: ${logCount} logs`);
|
|
1587
|
+
return response.body;
|
|
1588
|
+
}
|
|
1589
|
+
// ============================================================================
|
|
1059
1590
|
// CONTEXT MANAGER SUPPORT - Python-style async context manager
|
|
1060
1591
|
// ============================================================================
|
|
1061
1592
|
/**
|