@quantum-hub/qhubctl 1.8.1 → 1.10.0

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.
Files changed (153) hide show
  1. package/README.md +6 -35
  2. package/dist/commands/datapool-upload.js +7 -3
  3. package/dist/commands/init.js +26 -5
  4. package/dist/commands/list-contexts.js +1 -1
  5. package/dist/commands/services-build-status.js +1 -1
  6. package/dist/commands/set-context.js +6 -2
  7. package/dist/commands/up.js +11 -15
  8. package/dist/lib/error.js +25 -8
  9. package/dist/service/quantum-hub-service.d.ts +9 -13
  10. package/dist/service/quantum-hub-service.js +67 -139
  11. package/package.json +2 -2
  12. package/dist/client/kqh/apis/ServicePlatformApplicationsApi.d.ts +0 -89
  13. package/dist/client/kqh/apis/ServicePlatformApplicationsApi.js +0 -222
  14. package/dist/client/kqh/apis/ServicePlatformDashboardApi.d.ts +0 -44
  15. package/dist/client/kqh/apis/ServicePlatformDashboardApi.js +0 -106
  16. package/dist/client/kqh/apis/ServicePlatformDataPoolsApi.d.ts +0 -133
  17. package/dist/client/kqh/apis/ServicePlatformDataPoolsApi.js +0 -373
  18. package/dist/client/kqh/apis/ServicePlatformJobsApi.d.ts +0 -101
  19. package/dist/client/kqh/apis/ServicePlatformJobsApi.js +0 -261
  20. package/dist/client/kqh/apis/ServicePlatformMarketplaceApi.d.ts +0 -48
  21. package/dist/client/kqh/apis/ServicePlatformMarketplaceApi.js +0 -115
  22. package/dist/client/kqh/apis/ServicePlatformMeteringAPIApi.d.ts +0 -29
  23. package/dist/client/kqh/apis/ServicePlatformMeteringAPIApi.js +0 -53
  24. package/dist/client/kqh/apis/ServicePlatformServicesApi.d.ts +0 -501
  25. package/dist/client/kqh/apis/ServicePlatformServicesApi.js +0 -1472
  26. package/dist/client/kqh/apis/ServicePlatformSubscriptionsApi.d.ts +0 -66
  27. package/dist/client/kqh/apis/ServicePlatformSubscriptionsApi.js +0 -157
  28. package/dist/client/kqh/apis/TaxonomiesApi.d.ts +0 -90
  29. package/dist/client/kqh/apis/TaxonomiesApi.js +0 -272
  30. package/dist/client/kqh/apis/index.d.ts +0 -9
  31. package/dist/client/kqh/apis/index.js +0 -11
  32. package/dist/client/kqh/index.d.ts +0 -3
  33. package/dist/client/kqh/index.js +0 -5
  34. package/dist/client/kqh/models/AccessTokenDto.d.ts +0 -81
  35. package/dist/client/kqh/models/AccessTokenDto.js +0 -59
  36. package/dist/client/kqh/models/AmountMajorDto.d.ts +0 -37
  37. package/dist/client/kqh/models/AmountMajorDto.js +0 -40
  38. package/dist/client/kqh/models/ApiDto.d.ts +0 -144
  39. package/dist/client/kqh/models/ApiDto.js +0 -92
  40. package/dist/client/kqh/models/ApiOverviewDto.d.ts +0 -119
  41. package/dist/client/kqh/models/ApiOverviewDto.js +0 -83
  42. package/dist/client/kqh/models/ApplicationDto.d.ts +0 -64
  43. package/dist/client/kqh/models/ApplicationDto.js +0 -53
  44. package/dist/client/kqh/models/BuildJobDto.d.ts +0 -75
  45. package/dist/client/kqh/models/BuildJobDto.js +0 -64
  46. package/dist/client/kqh/models/CpuConfiguration.d.ts +0 -38
  47. package/dist/client/kqh/models/CpuConfiguration.js +0 -43
  48. package/dist/client/kqh/models/CreateApplicationRequest.d.ts +0 -31
  49. package/dist/client/kqh/models/CreateApplicationRequest.js +0 -38
  50. package/dist/client/kqh/models/CreateDataPoolRequest.d.ts +0 -31
  51. package/dist/client/kqh/models/CreateDataPoolRequest.js +0 -40
  52. package/dist/client/kqh/models/CreateExternalServicePricingPlanRequest.d.ts +0 -46
  53. package/dist/client/kqh/models/CreateExternalServicePricingPlanRequest.js +0 -50
  54. package/dist/client/kqh/models/CreateJobRequest.d.ts +0 -62
  55. package/dist/client/kqh/models/CreateJobRequest.js +0 -51
  56. package/dist/client/kqh/models/CreateManagedServicePricingPlanRequest.d.ts +0 -73
  57. package/dist/client/kqh/models/CreateManagedServicePricingPlanRequest.js +0 -72
  58. package/dist/client/kqh/models/CreateProductRequest.d.ts +0 -43
  59. package/dist/client/kqh/models/CreateProductRequest.js +0 -46
  60. package/dist/client/kqh/models/CreateSubscriptionRequest.d.ts +0 -37
  61. package/dist/client/kqh/models/CreateSubscriptionRequest.js +0 -44
  62. package/dist/client/kqh/models/CreateWorkflowServiceRequest.d.ts +0 -37
  63. package/dist/client/kqh/models/CreateWorkflowServiceRequest.js +0 -42
  64. package/dist/client/kqh/models/DataPoolDto.d.ts +0 -166
  65. package/dist/client/kqh/models/DataPoolDto.js +0 -98
  66. package/dist/client/kqh/models/DataPoolRef.d.ts +0 -43
  67. package/dist/client/kqh/models/DataPoolRef.js +0 -42
  68. package/dist/client/kqh/models/DataSourceDescriptorDto.d.ts +0 -89
  69. package/dist/client/kqh/models/DataSourceDescriptorDto.js +0 -62
  70. package/dist/client/kqh/models/FileDto.d.ts +0 -73
  71. package/dist/client/kqh/models/FileDto.js +0 -45
  72. package/dist/client/kqh/models/FileSource.d.ts +0 -26
  73. package/dist/client/kqh/models/FileSource.js +0 -28
  74. package/dist/client/kqh/models/GpuConfiguration.d.ts +0 -49
  75. package/dist/client/kqh/models/GpuConfiguration.js +0 -53
  76. package/dist/client/kqh/models/JobDto.d.ts +0 -117
  77. package/dist/client/kqh/models/JobDto.js +0 -75
  78. package/dist/client/kqh/models/JobOverviewDto.d.ts +0 -74
  79. package/dist/client/kqh/models/JobOverviewDto.js +0 -60
  80. package/dist/client/kqh/models/MemoryConfiguration.d.ts +0 -38
  81. package/dist/client/kqh/models/MemoryConfiguration.js +0 -43
  82. package/dist/client/kqh/models/MeteringRequest.d.ts +0 -43
  83. package/dist/client/kqh/models/MeteringRequest.js +0 -42
  84. package/dist/client/kqh/models/MeteringResponse.d.ts +0 -49
  85. package/dist/client/kqh/models/MeteringResponse.js +0 -44
  86. package/dist/client/kqh/models/MonthAmountDto.d.ts +0 -51
  87. package/dist/client/kqh/models/MonthAmountDto.js +0 -46
  88. package/dist/client/kqh/models/OwnerDto.d.ts +0 -67
  89. package/dist/client/kqh/models/OwnerDto.js +0 -50
  90. package/dist/client/kqh/models/PageDataPoolDto.d.ts +0 -94
  91. package/dist/client/kqh/models/PageDataPoolDto.js +0 -61
  92. package/dist/client/kqh/models/PageableObject.d.ts +0 -62
  93. package/dist/client/kqh/models/PageableObject.js +0 -49
  94. package/dist/client/kqh/models/PricingPlanDto.d.ts +0 -52
  95. package/dist/client/kqh/models/PricingPlanDto.js +0 -50
  96. package/dist/client/kqh/models/ProductAmountDto.d.ts +0 -38
  97. package/dist/client/kqh/models/ProductAmountDto.js +0 -41
  98. package/dist/client/kqh/models/ProductDto.d.ts +0 -83
  99. package/dist/client/kqh/models/ProductDto.js +0 -69
  100. package/dist/client/kqh/models/ProductGroupDto.d.ts +0 -45
  101. package/dist/client/kqh/models/ProductGroupDto.js +0 -44
  102. package/dist/client/kqh/models/ProductUsageDto.d.ts +0 -61
  103. package/dist/client/kqh/models/ProductUsageDto.js +0 -48
  104. package/dist/client/kqh/models/RegistrySource.d.ts +0 -52
  105. package/dist/client/kqh/models/RegistrySource.js +0 -49
  106. package/dist/client/kqh/models/RepositorySource.d.ts +0 -32
  107. package/dist/client/kqh/models/RepositorySource.js +0 -41
  108. package/dist/client/kqh/models/ResourceConfiguration.d.ts +0 -31
  109. package/dist/client/kqh/models/ResourceConfiguration.js +0 -52
  110. package/dist/client/kqh/models/ReviewDto.d.ts +0 -51
  111. package/dist/client/kqh/models/ReviewDto.js +0 -49
  112. package/dist/client/kqh/models/RuntimeConfiguration.d.ts +0 -53
  113. package/dist/client/kqh/models/RuntimeConfiguration.js +0 -49
  114. package/dist/client/kqh/models/ServiceDefinitionCommand.d.ts +0 -39
  115. package/dist/client/kqh/models/ServiceDefinitionCommand.js +0 -45
  116. package/dist/client/kqh/models/ServiceDefinitionDto.d.ts +0 -180
  117. package/dist/client/kqh/models/ServiceDefinitionDto.js +0 -101
  118. package/dist/client/kqh/models/ServiceDefinitionDtoSource.d.ts +0 -23
  119. package/dist/client/kqh/models/ServiceDefinitionDtoSource.js +0 -49
  120. package/dist/client/kqh/models/ServiceDefinitionOverviewDto.d.ts +0 -79
  121. package/dist/client/kqh/models/ServiceDefinitionOverviewDto.js +0 -64
  122. package/dist/client/kqh/models/ServiceDto.d.ts +0 -59
  123. package/dist/client/kqh/models/ServiceDto.js +0 -52
  124. package/dist/client/kqh/models/ServiceExecutionConsumerDto.d.ts +0 -67
  125. package/dist/client/kqh/models/ServiceExecutionConsumerDto.js +0 -57
  126. package/dist/client/kqh/models/ServiceExecutionDto.d.ts +0 -49
  127. package/dist/client/kqh/models/ServiceExecutionDto.js +0 -44
  128. package/dist/client/kqh/models/ServiceOverviewDto.d.ts +0 -115
  129. package/dist/client/kqh/models/ServiceOverviewDto.js +0 -85
  130. package/dist/client/kqh/models/ServiceSource.d.ts +0 -31
  131. package/dist/client/kqh/models/ServiceSource.js +0 -52
  132. package/dist/client/kqh/models/SortObject.d.ts +0 -43
  133. package/dist/client/kqh/models/SortObject.js +0 -42
  134. package/dist/client/kqh/models/SubscriptionDto.d.ts +0 -58
  135. package/dist/client/kqh/models/SubscriptionDto.js +0 -49
  136. package/dist/client/kqh/models/TaxonomyElement.d.ts +0 -43
  137. package/dist/client/kqh/models/TaxonomyElement.js +0 -42
  138. package/dist/client/kqh/models/UpdateDataPoolRequest.d.ts +0 -109
  139. package/dist/client/kqh/models/UpdateDataPoolRequest.js +0 -66
  140. package/dist/client/kqh/models/UpdateResourceConfigurationRequest.d.ts +0 -23
  141. package/dist/client/kqh/models/UpdateResourceConfigurationRequest.js +0 -49
  142. package/dist/client/kqh/models/UpdateVersionRequest.d.ts +0 -44
  143. package/dist/client/kqh/models/UpdateVersionRequest.js +0 -43
  144. package/dist/client/kqh/models/UpdateWorkflowServiceRequest.d.ts +0 -31
  145. package/dist/client/kqh/models/UpdateWorkflowServiceRequest.js +0 -27
  146. package/dist/client/kqh/models/ValidationResult.d.ts +0 -52
  147. package/dist/client/kqh/models/ValidationResult.js +0 -50
  148. package/dist/client/kqh/models/index.d.ts +0 -57
  149. package/dist/client/kqh/models/index.js +0 -59
  150. package/dist/client/kqh/runtime.d.ts +0 -181
  151. package/dist/client/kqh/runtime.js +0 -325
  152. package/dist/model/account.d.ts +0 -5
  153. package/dist/model/account.js +0 -1
package/README.md CHANGED
@@ -85,16 +85,19 @@ node dist/main.js login --base-path https://api.hub.34.90.225.20.nip.io -t <pers
85
85
 
86
86
  ### Debug output
87
87
 
88
- Set `DEBUG=*` to print all debug output:
88
+ Set `DEBUG=1` to print all debug output:
89
89
 
90
90
  ```bash
91
- DEBUG=* node dist/main.js <command>
91
+ DEBUG=1 node dist/main.js <command>
92
+
93
+ # or
94
+ DEBUG=1 qhubctl <command>
92
95
  ```
93
96
 
94
97
  On Windows, set the variable separately:
95
98
 
96
99
  ```cmd
97
- set DEBUG=*
100
+ set DEBUG=1
98
101
  node dist\main.js <command>
99
102
  ```
100
103
 
@@ -120,38 +123,6 @@ npm run build
120
123
  npm run docs:generate
121
124
  ```
122
125
 
123
- ## Regenerating the API client
124
-
125
- The generated clients in `src/client/` are produced from the platform OpenAPI specs using [openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli).
126
-
127
- ```bash
128
- npm install -g @openapitools/openapi-generator-cli
129
- ```
130
-
131
- Against production:
132
-
133
- ```bash
134
- openapi-generator-cli generate -g typescript-fetch \
135
- -i https://api.hub.kipu-quantum.com/qc-catalog/docs \
136
- -o src/client/kqh
137
-
138
- openapi-generator-cli generate -g typescript-fetch \
139
- -i https://api.hub.kipu-quantum.com/user-service/docs \
140
- -o src/client/users
141
- ```
142
-
143
- Against staging:
144
-
145
- ```bash
146
- openapi-generator-cli generate -g typescript-fetch \
147
- -i https://api.hub.34.90.225.20.nip.io/qc-catalog/docs \
148
- -o src/client/kqh
149
-
150
- openapi-generator-cli generate -g typescript-fetch \
151
- -i https://api.hub.34.90.225.20.nip.io/user-service/docs \
152
- -o src/client/users
153
- ```
154
-
155
126
  ## License
156
127
 
157
128
  [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) | Copyright 2026-now Kipu Quantum GmbH
@@ -122,7 +122,7 @@ async function ensureDatapoolExists(client, datapoolId, filePaths) {
122
122
  : selectExistingDatapool(client);
123
123
  }
124
124
  try {
125
- await client.dataPools.getDataPool({ id: datapoolId });
125
+ await client.dataPools.getDataPool(datapoolId);
126
126
  return datapoolId;
127
127
  }
128
128
  catch (err) {
@@ -153,7 +153,11 @@ export const datapoolUploadCommand = new Command('upload')
153
153
  : undefined;
154
154
  const apiKey = userConfig.auth?.value;
155
155
  const organizationId = userConfig.context?.isOrganization ? userConfig.context.id : undefined;
156
- const client = new HubPlatformClient({ baseUrl, apiKey, organizationId });
156
+ const client = new HubPlatformClient({
157
+ baseUrl,
158
+ apiKey,
159
+ headers: { 'X-OrganizationId': organizationId },
160
+ });
157
161
  const filePaths = opts.file;
158
162
  for (const filePath of filePaths) {
159
163
  if (!fs.existsSync(filePath))
@@ -171,7 +175,7 @@ export const datapoolUploadCommand = new Command('upload')
171
175
  const spin = spinner(`Uploading ${fileName} to data pool ${datapoolId}`);
172
176
  try {
173
177
  const fileStream = fs.createReadStream(filePath);
174
- const result = await client.dataPools.addDataPoolFile({ id: datapoolId, file: fileStream });
178
+ const result = await client.dataPools.addDataPoolFile(datapoolId, { file: fileStream });
175
179
  spin.succeed('\u2713');
176
180
  stdout(`Successfully uploaded ${fileName} to data pool ${datapoolId}`);
177
181
  stdout(`File ID: ${result.id}`);
@@ -8,12 +8,33 @@ import { error, stdout } from '../lib/output.js';
8
8
  import { Runtime } from '../model/managed-service-config.js';
9
9
  import { writeServiceConfig } from '../service/service-config-service.js';
10
10
  import { getUserConfig } from '../model/command.js';
11
+ /**
12
+ * Environment variable name for providing a personal access token.
13
+ * This can be used as an alternative to the `login` command for CI/CD scenarios.
14
+ */
15
+ const ENV_PERSONAL_ACCESS_TOKEN = 'KQH_PERSONAL_ACCESS_TOKEN';
16
+ /**
17
+ * Gets the API key with the following priority:
18
+ * 1. CLI parameter (if provided)
19
+ * 2. KQH_PERSONAL_ACCESS_TOKEN environment variable
20
+ * 3. Stored config (from `login` command)
21
+ */
22
+ function getApiKey(tokenParam) {
23
+ if (tokenParam) {
24
+ return tokenParam;
25
+ }
26
+ const userConfig = getUserConfig();
27
+ return process.env[ENV_PERSONAL_ACCESS_TOKEN] ?? userConfig.auth?.value;
28
+ }
11
29
  const EXAMPLES = `
12
30
  Examples:
13
- $ qhubctl init`;
31
+ $ qhubctl init
32
+ $ qhubctl init --token <personal access token>
33
+ $ qhubctl init --non-interactive --token <personal access token>`;
14
34
  export const initCommand = new Command('init')
15
35
  .description('Bootstrap project to create a service.')
16
36
  .option('--name <value>', 'The name of the service')
37
+ .option('-t, --token <value>', 'Personal access token for authentication')
17
38
  .option('--non-interactive', 'Run it in non-interactive mode', false)
18
39
  .addHelpText('after', EXAMPLES)
19
40
  .action(async (opts) => {
@@ -52,11 +73,11 @@ export const initCommand = new Command('init')
52
73
  }
53
74
  writeServiceConfig(targetDirectory, serviceConfig);
54
75
  if (input.starter) {
55
- const userConfig = getUserConfig();
56
- if (!userConfig.auth?.value) {
57
- error('Starter templates require authentication. Use the "login" command to log in first.');
76
+ const apiKey = getApiKey(opts.token);
77
+ if (!apiKey) {
78
+ error(`Starter templates require authentication. Provide a token via --token, use the "login" command, or set the ${ENV_PERSONAL_ACCESS_TOKEN} environment variable.`);
58
79
  }
59
- await cloneRepository(input.starter.url, targetDirectory, userConfig.auth.value);
80
+ await cloneRepository(input.starter.url, targetDirectory, apiKey);
60
81
  }
61
82
  stdout('\u{1F389} Initialized project. Happy hacking!');
62
83
  if (input.starter) {
@@ -16,7 +16,7 @@ export const listContextsCommand = new Command('list-contexts')
16
16
  const accounts = await quantumHubService.getAccounts();
17
17
  spin.succeed('Contexts loaded');
18
18
  const tableData = accounts
19
- .map(account => ({ displayName: account.displayName, id: account.id, isOrganization: account.isOrganization }));
19
+ .map(account => ({ displayName: account.displayName ?? '', id: account.id ?? '', isOrganization: account.isOrganization ?? false }));
20
20
  const header = [
21
21
  { value: "displayName", alias: "Name", align: "left" },
22
22
  { value: "id", alias: "ID", align: "left" },
@@ -42,7 +42,7 @@ export const servicesBuildStatusCommand = new Command('build-status')
42
42
  const spin = spinner('Loading build status');
43
43
  let buildJob;
44
44
  try {
45
- buildJob = await quantumHubService.getBuildJobByServiceId(serviceId);
45
+ buildJob = await quantumHubService.getBuildJob(serviceId);
46
46
  }
47
47
  catch (err) {
48
48
  spin.fail('Failed to load build status');
@@ -6,7 +6,7 @@ import QuantumHubService from '../service/quantum-hub-service.js';
6
6
  import { writeUserConfig } from '../service/user-config-service.js';
7
7
  function getPromptOptions(accounts) {
8
8
  return accounts.map(account => ({
9
- name: account.isOrganization ? `${account.displayName} (Organization)` : account.displayName,
9
+ name: account.isOrganization ? `${account.displayName} (Organization)` : (account.displayName ?? account.id ?? ''),
10
10
  value: account,
11
11
  }));
12
12
  }
@@ -46,7 +46,11 @@ export const setContextCommand = new Command('set-context')
46
46
  }
47
47
  writeUserConfig(configDir, {
48
48
  ...userConfig,
49
- context: selectedContext,
49
+ context: {
50
+ id: selectedContext.id,
51
+ displayName: selectedContext.displayName,
52
+ isOrganization: selectedContext.isOrganization ?? false,
53
+ },
50
54
  });
51
55
  stdout(`Switched to context "${selectedContext?.displayName}".`);
52
56
  });
@@ -3,7 +3,7 @@ import { Command } from 'commander';
3
3
  import { waitUntil } from 'async-wait-until';
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
- import { BuildJobDtoStatusEnum, BuildJobDtoStepEnum, ValidationResultStateEnum } from '../client/kqh/index.js';
6
+ import { HubPlatform } from '@quantum-hub/qhub-api/platform';
7
7
  import { streamToBlob } from '../lib/stream-to-blob.js';
8
8
  import { getDescriptionText } from '../lib/up/get-description.js';
9
9
  import { zipProjectFolder } from '../lib/up/zip.js';
@@ -24,20 +24,20 @@ async function getOpenapi() {
24
24
  return streamToBlob(openapi);
25
25
  return undefined;
26
26
  }
27
- async function buildServiceJob(qhubService, service, serviceDefinition, silentMode) {
27
+ async function buildServiceJob(qhubService, service, silentMode) {
28
28
  let buildJob;
29
29
  let spin;
30
30
  let currentStep;
31
31
  try {
32
32
  await waitUntil(async () => {
33
- buildJob = await qhubService.getBuildJob(service, serviceDefinition);
33
+ buildJob = await qhubService.getBuildJob(service.id);
34
34
  if (!silentMode) {
35
35
  if (buildJob.step !== currentStep) {
36
- if (buildJob.step === BuildJobDtoStepEnum.BuildImage) {
36
+ if (buildJob.step === HubPlatform.BuildJobDto.Step.BuildImage) {
37
37
  if (!spin)
38
38
  spin = spinner('Building Image (1/2)');
39
39
  }
40
- else if (buildJob.step === BuildJobDtoStepEnum.PushImage) {
40
+ else if (buildJob.step === HubPlatform.BuildJobDto.Step.PushImage) {
41
41
  if (spin) {
42
42
  spin.succeed();
43
43
  spin = undefined;
@@ -47,7 +47,7 @@ async function buildServiceJob(qhubService, service, serviceDefinition, silentMo
47
47
  currentStep = buildJob.step;
48
48
  }
49
49
  }
50
- return buildJob.status === BuildJobDtoStatusEnum.Success || buildJob.status === BuildJobDtoStatusEnum.Failure;
50
+ return buildJob.status === HubPlatform.BuildJobDto.Status.Success || buildJob.status === HubPlatform.BuildJobDto.Status.Failure;
51
51
  }, {
52
52
  intervalBetweenAttempts: 5000,
53
53
  timeout: silentMode ? 60 * 60 * 1000 : 20 * 60 * 1000,
@@ -58,7 +58,7 @@ async function buildServiceJob(qhubService, service, serviceDefinition, silentMo
58
58
  throw new Error('Error while waiting for the build job to finish: ' + err);
59
59
  }
60
60
  if (spin) {
61
- if (buildJob?.status === BuildJobDtoStatusEnum.Success)
61
+ if (buildJob?.status === HubPlatform.BuildJobDto.Status.Success)
62
62
  spin.succeed();
63
63
  else
64
64
  spin.fail();
@@ -145,19 +145,15 @@ export const upCommand = new Command('up')
145
145
  catch (err) {
146
146
  error(await handleError(err));
147
147
  }
148
- const serviceDefinition = service?.serviceDefinitions?.[0];
149
- if (!serviceDefinition) {
150
- error('Service definition not found');
151
- }
152
- const buildJob = await buildServiceJob(qhubService, service, serviceDefinition, silentMode);
153
- if (buildJob?.status === BuildJobDtoStatusEnum.Success) {
148
+ const buildJob = await buildServiceJob(qhubService, service, silentMode);
149
+ if (buildJob?.status === HubPlatform.BuildJobDto.Status.Success) {
154
150
  const msg = `Service ${serviceExistsInPlatform ? 'updated' : 'created'} \u{1F680}`;
155
151
  stdout(msg);
156
152
  }
157
- else if (buildJob?.status === BuildJobDtoStatusEnum.Failure) {
153
+ else if (buildJob?.status === HubPlatform.BuildJobDto.Status.Failure) {
158
154
  const { validationResult } = buildJob;
159
155
  let reason = 'Failed creating service';
160
- if (validationResult?.state === ValidationResultStateEnum.Error && validationResult.summary) {
156
+ if (validationResult?.state === HubPlatform.ValidationResult.State.Error && validationResult.summary) {
161
157
  reason = `${reason}: ${validationResult.summary}`;
162
158
  }
163
159
  error(reason);
package/dist/lib/error.js CHANGED
@@ -1,16 +1,33 @@
1
- import { FetchError, ResponseError } from '../client/kqh/index.js';
1
+ import { HubPlatformError, HubPlatformTimeoutError } from '@quantum-hub/qhub-api/platform';
2
2
  import { debugEnabled } from './debug.js';
3
- import { getErrorMessage } from './fetch.js';
4
3
  export const handleError = async (error) => {
5
- if (error instanceof ResponseError) {
6
- return getErrorMessage(error.response);
4
+ if (error instanceof HubPlatformError) {
5
+ const httpStatus = error.statusCode;
6
+ // message is either in body.errorMessage, body.error or body.detail
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ const body = error.body;
9
+ let errorMessage;
10
+ if (body?.errorMessage)
11
+ errorMessage = body.errorMessage;
12
+ if (!errorMessage && body?.error)
13
+ errorMessage = body.error;
14
+ if (!errorMessage && body?.detail)
15
+ errorMessage = body.detail;
16
+ if (debugEnabled()) {
17
+ console.error(error.stack ?? error.message);
18
+ }
19
+ return errorMessage ? `${errorMessage} (${httpStatus})` : `${httpStatus}`;
7
20
  }
8
- if (error instanceof FetchError) {
9
- const errorMessage = error.cause ? error.cause.message : error.message;
10
- return `Internal error occurred, please try again later (${errorMessage})`;
21
+ if (error instanceof HubPlatformTimeoutError) {
22
+ return `Internal error occurred, please try again later (${error.message})`;
11
23
  }
12
24
  if (debugEnabled()) {
13
- console.error(JSON.stringify(error));
25
+ if (error instanceof Error) {
26
+ console.error(error.stack ?? error.message);
27
+ }
28
+ else {
29
+ console.error(JSON.stringify(error));
30
+ }
14
31
  }
15
32
  return 'Internal error occurred, please contact your KQH administrator';
16
33
  };
@@ -1,23 +1,19 @@
1
- import { HubPlatformClient } from "@quantum-hub/qhub-api/platform";
2
- import { BuildJobDto, ServiceDefinitionDto, ServiceDto, ServiceOverviewDto, ServicePlatformServicesApi } from '../client/kqh/index.js';
3
- import Account from '../model/account.js';
1
+ import { HubPlatform, HubPlatformClient } from "@quantum-hub/qhub-api/platform";
4
2
  import ManagedServiceConfig from '../model/managed-service-config.js';
5
3
  import UserConfig from '../model/user-config.js';
6
4
  import { CommandService } from './command-service.js';
7
- import { HubPlatform } from "@quantum-hub/qhub-api/platform";
8
5
  export default class QuantumHubService extends CommandService {
9
- serviceApi: ServicePlatformServicesApi;
10
6
  apiClient: HubPlatformClient;
11
7
  constructor(userConfig: UserConfig);
12
- createService(serviceConfig: ManagedServiceConfig, sourceCode: Blob, apiDefinition?: Blob): Promise<ServiceDto>;
13
- getAccounts(): Promise<Account[]>;
14
- getBuildJob(service: ServiceDto, serviceDefinition: ServiceDefinitionDto): Promise<BuildJobDto>;
15
- getBuildJobByServiceId(serviceId: string): Promise<BuildJobDto>;
8
+ getAccounts(): Promise<HubPlatform.AccountDto[]>;
9
+ createService(serviceConfig: ManagedServiceConfig, sourceCode: Blob, apiDefinition?: Blob): Promise<HubPlatform.ServiceDto>;
10
+ getBuildJob(id: string): Promise<HubPlatform.BuildJobDto>;
16
11
  getJobById(id: string): Promise<HubPlatform.ServiceJobDto>;
17
- getService(id: string): Promise<ServiceDto>;
18
- getServices(): Promise<ServiceOverviewDto[]>;
12
+ getService(id: string): Promise<HubPlatform.ServiceDto>;
13
+ getServices(): Promise<HubPlatform.ServiceOverviewDto[]>;
19
14
  runJob(request: HubPlatform.CreateServiceJobRequest): Promise<HubPlatform.ServiceJobDto>;
20
15
  updateDescription(serviceConfig: ManagedServiceConfig, description: string): Promise<void>;
21
- updateOpenapi(serviceConfig: ManagedServiceConfig, apiDefinition: Blob): Promise<void>;
22
- updateService(serviceConfig: ManagedServiceConfig, description: string, sourceCode: Blob, apiDefinition?: Blob): Promise<ServiceDto>;
16
+ updateOpenapi(serviceConfig: ManagedServiceConfig, openapi: Blob): Promise<void>;
17
+ updateService(serviceConfig: ManagedServiceConfig, description: string, sourceCode: Blob, openapi?: Blob): Promise<HubPlatform.ServiceDto>;
18
+ private uploadSourceCode;
23
19
  }
@@ -1,93 +1,68 @@
1
1
  import { HubPlatformClient } from "@quantum-hub/qhub-api/platform";
2
- import { Configuration, ServicePlatformServicesApi, } from '../client/kqh/index.js';
3
2
  import { handleError } from '../lib/error.js';
4
- import { fetchOrThrow } from '../lib/fetch.js';
5
3
  import { defaultBasePath } from '../model/user-config.js';
6
4
  import { CommandService } from './command-service.js';
7
5
  export default class QuantumHubService extends CommandService {
8
- serviceApi;
9
6
  apiClient;
10
7
  constructor(userConfig) {
11
8
  super(userConfig);
12
9
  const basePath = (userConfig.endpoint?.basePath || defaultBasePath) + '/qc-catalog';
13
- const defaultHeaders = userConfig.endpoint?.defaultHeaders || {};
14
10
  const apiKey = userConfig.auth?.value;
15
- const organizationId = this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined;
16
- this.serviceApi = new ServicePlatformServicesApi(new Configuration({
17
- apiKey,
18
- basePath,
19
- headers: defaultHeaders,
20
- }));
11
+ const organizationId = userConfig.context?.isOrganization ? userConfig.context.id : undefined;
21
12
  this.apiClient = new HubPlatformClient({
22
13
  apiKey,
23
14
  baseUrl: basePath,
24
- organizationId
15
+ headers: {
16
+ 'X-OrganizationId': organizationId,
17
+ },
25
18
  });
26
19
  }
27
- async createService(serviceConfig, sourceCode, apiDefinition) {
28
- const organizationId = this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined;
29
- const milliCpus = (serviceConfig.resources?.cpu || 1) * 1000;
30
- const memoryInMegabytes = (serviceConfig.resources?.memory || 2) * 1024;
31
- const gpuAccelerator = serviceConfig.resources?.gpu?.type || 'NONE';
32
- const gpuCount = serviceConfig.resources?.gpu?.count || 0;
33
- try {
34
- return await this.serviceApi.createManagedService({
35
- apiDefinition,
36
- gpuAccelerator,
37
- gpuCount,
38
- memoryInMegabytes,
39
- milliCpus,
40
- name: serviceConfig.name,
41
- runtime: serviceConfig.runtime,
42
- userCode: sourceCode,
43
- xOrganizationId: organizationId,
44
- });
45
- }
46
- catch (error) {
47
- const errorMessage = await handleError(error);
48
- throw new Error(errorMessage);
49
- }
50
- }
51
20
  async getAccounts() {
52
- const basePath = (this.userConfig.endpoint?.basePath || defaultBasePath) + '/qc-catalog';
53
21
  try {
54
- const response = await fetchOrThrow(basePath + '/my/accounts', {
55
- headers: { 'X-Auth-Token': this.userConfig.auth.value },
56
- });
57
- return (await response.json());
22
+ return await this.apiClient.users.getAccounts();
58
23
  }
59
24
  catch (error) {
60
25
  const errorMessage = await handleError(error);
61
26
  throw new Error(errorMessage);
62
27
  }
63
28
  }
64
- async getBuildJob(service, serviceDefinition) {
65
- const organizationId = this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined;
29
+ async createService(serviceConfig, sourceCode, apiDefinition) {
30
+ const milliCpus = (serviceConfig.resources?.cpu || 1) * 1000;
31
+ const memoryInMegabytes = (serviceConfig.resources?.memory || 2) * 1024;
32
+ const gpuAccelerator = serviceConfig.resources?.gpu?.type || 'NONE';
33
+ const gpuCount = serviceConfig.resources?.gpu?.count || 0;
66
34
  try {
67
- return await this.serviceApi.getBuildStatus({
68
- serviceId: service.id,
69
- versionId: serviceDefinition.id,
70
- xOrganizationId: organizationId,
35
+ const service = await this.apiClient.managedServices.createManagedService({
36
+ name: serviceConfig.name,
37
+ runtimeConfiguration: {
38
+ runtime: serviceConfig.runtime,
39
+ },
40
+ resourceConfigurations: [
41
+ { type: 'cpu', amount: milliCpus, unit: 'm' },
42
+ { type: 'memory', amount: memoryInMegabytes, unit: 'M' },
43
+ {
44
+ type: 'gpu',
45
+ amount: gpuCount,
46
+ accelerator: gpuAccelerator
47
+ },
48
+ ],
49
+ source: { type: 'file' },
50
+ summary: serviceConfig.summary,
71
51
  });
52
+ await this.uploadSourceCode(service.id, sourceCode);
53
+ if (apiDefinition) {
54
+ await this.apiClient.services.updateServiceOpenApi(service.id, { file: apiDefinition });
55
+ }
56
+ return service;
72
57
  }
73
58
  catch (error) {
74
59
  const errorMessage = await handleError(error);
75
60
  throw new Error(errorMessage);
76
61
  }
77
62
  }
78
- async getBuildJobByServiceId(serviceId) {
79
- const organizationId = this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined;
80
- const service = await this.getService(serviceId);
81
- const serviceDefinition = service?.serviceDefinitions?.[0];
82
- if (!serviceDefinition?.id) {
83
- throw new Error(`No service version found for service '${serviceId}'`);
84
- }
63
+ async getBuildJob(id) {
85
64
  try {
86
- return await this.serviceApi.getBuildStatus({
87
- serviceId: service.id,
88
- versionId: serviceDefinition.id,
89
- xOrganizationId: organizationId,
90
- });
65
+ return await this.apiClient.managedServices.getManagedServiceBuildStatus(id);
91
66
  }
92
67
  catch (error) {
93
68
  const errorMessage = await handleError(error);
@@ -96,7 +71,7 @@ export default class QuantumHubService extends CommandService {
96
71
  }
97
72
  async getJobById(id) {
98
73
  try {
99
- return await this.apiClient.serviceJobs.getServiceJob({ "id": id });
74
+ return await this.apiClient.serviceJobs.getServiceJob(id);
100
75
  }
101
76
  catch (error) {
102
77
  const errorMessage = await handleError(error);
@@ -104,9 +79,8 @@ export default class QuantumHubService extends CommandService {
104
79
  }
105
80
  }
106
81
  async getService(id) {
107
- const organizationId = this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined;
108
82
  try {
109
- return await this.serviceApi.getService({ id, xOrganizationId: organizationId });
83
+ return await this.apiClient.services.getService(id);
110
84
  }
111
85
  catch (error) {
112
86
  const errorMessage = await handleError(error);
@@ -114,9 +88,8 @@ export default class QuantumHubService extends CommandService {
114
88
  }
115
89
  }
116
90
  async getServices() {
117
- const organizationId = this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined;
118
91
  try {
119
- return await this.serviceApi.getServices({ xOrganizationId: organizationId });
92
+ return await this.apiClient.services.getServices();
120
93
  }
121
94
  catch (error) {
122
95
  const errorMessage = await handleError(error);
@@ -133,106 +106,61 @@ export default class QuantumHubService extends CommandService {
133
106
  }
134
107
  }
135
108
  async updateDescription(serviceConfig, description) {
136
- const organizationId = this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined;
137
- const service = await this.serviceApi.getService({ id: serviceConfig.serviceId, xOrganizationId: organizationId });
138
- const serviceDefinition = service?.serviceDefinitions?.[0];
139
109
  try {
140
- await this.serviceApi.updateServiceVersion({
141
- serviceId: service.id,
142
- updateVersionRequest: { description },
143
- versionId: serviceDefinition.id,
144
- xOrganizationId: organizationId,
145
- });
110
+ await this.apiClient.services.updateServiceDescription(serviceConfig.serviceId, { description });
146
111
  }
147
112
  catch (error) {
148
113
  const errorMessage = await handleError(error);
149
114
  throw new Error(errorMessage);
150
115
  }
151
116
  }
152
- async updateOpenapi(serviceConfig, apiDefinition) {
153
- const organizationId = this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined;
154
- const service = await this.serviceApi.getService({ id: serviceConfig.serviceId, xOrganizationId: organizationId });
155
- const serviceDefinition = service?.serviceDefinitions?.[0];
156
- if (apiDefinition) {
157
- await this.serviceApi.updateApiDefinition({
158
- file: apiDefinition,
159
- serviceId: service.id,
160
- versionId: serviceDefinition.id,
161
- xOrganizationId: organizationId,
162
- });
117
+ async updateOpenapi(serviceConfig, openapi) {
118
+ try {
119
+ await this.apiClient.services.updateServiceOpenApi(serviceConfig.serviceId, { file: openapi });
120
+ }
121
+ catch (error) {
122
+ const errorMessage = await handleError(error);
123
+ throw new Error(errorMessage);
163
124
  }
164
125
  }
165
- async updateService(serviceConfig, description, sourceCode, apiDefinition) {
166
- const organizationId = this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined;
167
- const service = await this.serviceApi.getService({ id: serviceConfig.serviceId, xOrganizationId: organizationId });
168
- const serviceDefinition = service?.serviceDefinitions?.[0];
126
+ async updateService(serviceConfig, description, sourceCode, openapi) {
127
+ const serviceId = serviceConfig.serviceId;
169
128
  const milliCpus = (serviceConfig.resources?.cpu || 1) * 1000;
170
- const cpuConfiguration = {
171
- amount: milliCpus,
172
- type: 'cpu',
173
- unit: 'm',
174
- };
175
129
  const memoryInMegabytes = (serviceConfig.resources?.memory || 2) * 1024;
176
- const memoryConfiguration = {
177
- amount: memoryInMegabytes,
178
- type: 'memory',
179
- unit: 'M',
180
- };
181
130
  const gpuAccelerator = serviceConfig.resources?.gpu?.type || 'NONE';
182
131
  const gpuCount = serviceConfig.resources?.gpu?.count || 0;
183
- const gpuConfiguration = {
184
- accelerator: gpuAccelerator,
185
- amount: gpuCount,
186
- type: 'gpu',
187
- };
188
132
  try {
189
133
  if (description) {
190
- await this.serviceApi.updateServiceVersion({
191
- serviceId: service.id,
192
- updateVersionRequest: {
193
- description,
194
- },
195
- versionId: serviceDefinition.id,
196
- xOrganizationId: organizationId,
197
- });
134
+ await this.apiClient.services.updateServiceDescription(serviceId, { description });
198
135
  }
199
- await this.serviceApi.updateResourceConfiguration({
200
- serviceId: service.id,
201
- updateResourceConfigurationRequest: cpuConfiguration,
202
- versionId: serviceDefinition.id,
203
- xOrganizationId: organizationId,
136
+ if (serviceConfig.summary) {
137
+ await this.apiClient.services.updateServiceSummary(serviceId, { summary: serviceConfig.summary });
138
+ }
139
+ await this.apiClient.managedServices.updateManagedServiceResourceConfiguration(serviceId, {
140
+ body: { type: 'cpu', amount: milliCpus, unit: 'm' },
204
141
  });
205
- await this.serviceApi.updateResourceConfiguration({
206
- serviceId: service.id,
207
- updateResourceConfigurationRequest: memoryConfiguration,
208
- versionId: serviceDefinition.id,
209
- xOrganizationId: organizationId,
142
+ await this.apiClient.managedServices.updateManagedServiceResourceConfiguration(serviceId, {
143
+ body: { type: 'memory', amount: memoryInMegabytes, unit: 'M' },
210
144
  });
211
- await this.serviceApi.updateResourceConfiguration({
212
- serviceId: service.id,
213
- updateResourceConfigurationRequest: gpuConfiguration,
214
- versionId: serviceDefinition.id,
215
- xOrganizationId: organizationId,
145
+ await this.apiClient.managedServices.updateManagedServiceResourceConfiguration(serviceId, {
146
+ body: {
147
+ type: 'gpu',
148
+ amount: gpuCount,
149
+ accelerator: gpuAccelerator
150
+ },
216
151
  });
217
- if (apiDefinition) {
218
- await this.serviceApi.updateApiDefinition({
219
- file: apiDefinition,
220
- serviceId: service.id,
221
- versionId: serviceDefinition.id,
222
- xOrganizationId: organizationId,
223
- });
152
+ if (openapi) {
153
+ await this.apiClient.services.updateServiceOpenApi(serviceId, { file: openapi });
224
154
  }
225
- await this.serviceApi.updateSourceCodePost({
226
- file: sourceCode,
227
- serviceId: service.id,
228
- versionId: serviceDefinition.id,
229
- xOrganizationId: organizationId,
230
- });
231
- return service;
155
+ await this.uploadSourceCode(serviceId, sourceCode);
156
+ return await this.getService(serviceId);
232
157
  }
233
158
  catch (error) {
234
159
  const errorMessage = await handleError(error);
235
160
  throw new Error(errorMessage);
236
161
  }
237
162
  }
163
+ async uploadSourceCode(serviceId, sourceCode) {
164
+ await this.apiClient.managedServices.uploadManagedServiceSource(serviceId, { file: sourceCode });
165
+ }
238
166
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantum-hub/qhubctl",
3
- "version": "1.8.1",
3
+ "version": "1.10.0",
4
4
  "description": "Kipu Quantum Hub CLI",
5
5
  "author": "Kipu Quantum GmbH",
6
6
  "contributors": [
@@ -32,7 +32,7 @@
32
32
  "commander": "^14",
33
33
  "fs-extra": "^11",
34
34
  "ignore": "^7",
35
- "isomorphic-git": "^1.37.2",
35
+ "isomorphic-git": "^1",
36
36
  "ora": "^9",
37
37
  "tty-table": "^5",
38
38
  "update-notifier": "^7",