@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.
- package/README.md +6 -35
- package/dist/commands/datapool-upload.js +7 -3
- package/dist/commands/init.js +26 -5
- package/dist/commands/list-contexts.js +1 -1
- package/dist/commands/services-build-status.js +1 -1
- package/dist/commands/set-context.js +6 -2
- package/dist/commands/up.js +11 -15
- package/dist/lib/error.js +25 -8
- package/dist/service/quantum-hub-service.d.ts +9 -13
- package/dist/service/quantum-hub-service.js +67 -139
- package/package.json +2 -2
- package/dist/client/kqh/apis/ServicePlatformApplicationsApi.d.ts +0 -89
- package/dist/client/kqh/apis/ServicePlatformApplicationsApi.js +0 -222
- package/dist/client/kqh/apis/ServicePlatformDashboardApi.d.ts +0 -44
- package/dist/client/kqh/apis/ServicePlatformDashboardApi.js +0 -106
- package/dist/client/kqh/apis/ServicePlatformDataPoolsApi.d.ts +0 -133
- package/dist/client/kqh/apis/ServicePlatformDataPoolsApi.js +0 -373
- package/dist/client/kqh/apis/ServicePlatformJobsApi.d.ts +0 -101
- package/dist/client/kqh/apis/ServicePlatformJobsApi.js +0 -261
- package/dist/client/kqh/apis/ServicePlatformMarketplaceApi.d.ts +0 -48
- package/dist/client/kqh/apis/ServicePlatformMarketplaceApi.js +0 -115
- package/dist/client/kqh/apis/ServicePlatformMeteringAPIApi.d.ts +0 -29
- package/dist/client/kqh/apis/ServicePlatformMeteringAPIApi.js +0 -53
- package/dist/client/kqh/apis/ServicePlatformServicesApi.d.ts +0 -501
- package/dist/client/kqh/apis/ServicePlatformServicesApi.js +0 -1472
- package/dist/client/kqh/apis/ServicePlatformSubscriptionsApi.d.ts +0 -66
- package/dist/client/kqh/apis/ServicePlatformSubscriptionsApi.js +0 -157
- package/dist/client/kqh/apis/TaxonomiesApi.d.ts +0 -90
- package/dist/client/kqh/apis/TaxonomiesApi.js +0 -272
- package/dist/client/kqh/apis/index.d.ts +0 -9
- package/dist/client/kqh/apis/index.js +0 -11
- package/dist/client/kqh/index.d.ts +0 -3
- package/dist/client/kqh/index.js +0 -5
- package/dist/client/kqh/models/AccessTokenDto.d.ts +0 -81
- package/dist/client/kqh/models/AccessTokenDto.js +0 -59
- package/dist/client/kqh/models/AmountMajorDto.d.ts +0 -37
- package/dist/client/kqh/models/AmountMajorDto.js +0 -40
- package/dist/client/kqh/models/ApiDto.d.ts +0 -144
- package/dist/client/kqh/models/ApiDto.js +0 -92
- package/dist/client/kqh/models/ApiOverviewDto.d.ts +0 -119
- package/dist/client/kqh/models/ApiOverviewDto.js +0 -83
- package/dist/client/kqh/models/ApplicationDto.d.ts +0 -64
- package/dist/client/kqh/models/ApplicationDto.js +0 -53
- package/dist/client/kqh/models/BuildJobDto.d.ts +0 -75
- package/dist/client/kqh/models/BuildJobDto.js +0 -64
- package/dist/client/kqh/models/CpuConfiguration.d.ts +0 -38
- package/dist/client/kqh/models/CpuConfiguration.js +0 -43
- package/dist/client/kqh/models/CreateApplicationRequest.d.ts +0 -31
- package/dist/client/kqh/models/CreateApplicationRequest.js +0 -38
- package/dist/client/kqh/models/CreateDataPoolRequest.d.ts +0 -31
- package/dist/client/kqh/models/CreateDataPoolRequest.js +0 -40
- package/dist/client/kqh/models/CreateExternalServicePricingPlanRequest.d.ts +0 -46
- package/dist/client/kqh/models/CreateExternalServicePricingPlanRequest.js +0 -50
- package/dist/client/kqh/models/CreateJobRequest.d.ts +0 -62
- package/dist/client/kqh/models/CreateJobRequest.js +0 -51
- package/dist/client/kqh/models/CreateManagedServicePricingPlanRequest.d.ts +0 -73
- package/dist/client/kqh/models/CreateManagedServicePricingPlanRequest.js +0 -72
- package/dist/client/kqh/models/CreateProductRequest.d.ts +0 -43
- package/dist/client/kqh/models/CreateProductRequest.js +0 -46
- package/dist/client/kqh/models/CreateSubscriptionRequest.d.ts +0 -37
- package/dist/client/kqh/models/CreateSubscriptionRequest.js +0 -44
- package/dist/client/kqh/models/CreateWorkflowServiceRequest.d.ts +0 -37
- package/dist/client/kqh/models/CreateWorkflowServiceRequest.js +0 -42
- package/dist/client/kqh/models/DataPoolDto.d.ts +0 -166
- package/dist/client/kqh/models/DataPoolDto.js +0 -98
- package/dist/client/kqh/models/DataPoolRef.d.ts +0 -43
- package/dist/client/kqh/models/DataPoolRef.js +0 -42
- package/dist/client/kqh/models/DataSourceDescriptorDto.d.ts +0 -89
- package/dist/client/kqh/models/DataSourceDescriptorDto.js +0 -62
- package/dist/client/kqh/models/FileDto.d.ts +0 -73
- package/dist/client/kqh/models/FileDto.js +0 -45
- package/dist/client/kqh/models/FileSource.d.ts +0 -26
- package/dist/client/kqh/models/FileSource.js +0 -28
- package/dist/client/kqh/models/GpuConfiguration.d.ts +0 -49
- package/dist/client/kqh/models/GpuConfiguration.js +0 -53
- package/dist/client/kqh/models/JobDto.d.ts +0 -117
- package/dist/client/kqh/models/JobDto.js +0 -75
- package/dist/client/kqh/models/JobOverviewDto.d.ts +0 -74
- package/dist/client/kqh/models/JobOverviewDto.js +0 -60
- package/dist/client/kqh/models/MemoryConfiguration.d.ts +0 -38
- package/dist/client/kqh/models/MemoryConfiguration.js +0 -43
- package/dist/client/kqh/models/MeteringRequest.d.ts +0 -43
- package/dist/client/kqh/models/MeteringRequest.js +0 -42
- package/dist/client/kqh/models/MeteringResponse.d.ts +0 -49
- package/dist/client/kqh/models/MeteringResponse.js +0 -44
- package/dist/client/kqh/models/MonthAmountDto.d.ts +0 -51
- package/dist/client/kqh/models/MonthAmountDto.js +0 -46
- package/dist/client/kqh/models/OwnerDto.d.ts +0 -67
- package/dist/client/kqh/models/OwnerDto.js +0 -50
- package/dist/client/kqh/models/PageDataPoolDto.d.ts +0 -94
- package/dist/client/kqh/models/PageDataPoolDto.js +0 -61
- package/dist/client/kqh/models/PageableObject.d.ts +0 -62
- package/dist/client/kqh/models/PageableObject.js +0 -49
- package/dist/client/kqh/models/PricingPlanDto.d.ts +0 -52
- package/dist/client/kqh/models/PricingPlanDto.js +0 -50
- package/dist/client/kqh/models/ProductAmountDto.d.ts +0 -38
- package/dist/client/kqh/models/ProductAmountDto.js +0 -41
- package/dist/client/kqh/models/ProductDto.d.ts +0 -83
- package/dist/client/kqh/models/ProductDto.js +0 -69
- package/dist/client/kqh/models/ProductGroupDto.d.ts +0 -45
- package/dist/client/kqh/models/ProductGroupDto.js +0 -44
- package/dist/client/kqh/models/ProductUsageDto.d.ts +0 -61
- package/dist/client/kqh/models/ProductUsageDto.js +0 -48
- package/dist/client/kqh/models/RegistrySource.d.ts +0 -52
- package/dist/client/kqh/models/RegistrySource.js +0 -49
- package/dist/client/kqh/models/RepositorySource.d.ts +0 -32
- package/dist/client/kqh/models/RepositorySource.js +0 -41
- package/dist/client/kqh/models/ResourceConfiguration.d.ts +0 -31
- package/dist/client/kqh/models/ResourceConfiguration.js +0 -52
- package/dist/client/kqh/models/ReviewDto.d.ts +0 -51
- package/dist/client/kqh/models/ReviewDto.js +0 -49
- package/dist/client/kqh/models/RuntimeConfiguration.d.ts +0 -53
- package/dist/client/kqh/models/RuntimeConfiguration.js +0 -49
- package/dist/client/kqh/models/ServiceDefinitionCommand.d.ts +0 -39
- package/dist/client/kqh/models/ServiceDefinitionCommand.js +0 -45
- package/dist/client/kqh/models/ServiceDefinitionDto.d.ts +0 -180
- package/dist/client/kqh/models/ServiceDefinitionDto.js +0 -101
- package/dist/client/kqh/models/ServiceDefinitionDtoSource.d.ts +0 -23
- package/dist/client/kqh/models/ServiceDefinitionDtoSource.js +0 -49
- package/dist/client/kqh/models/ServiceDefinitionOverviewDto.d.ts +0 -79
- package/dist/client/kqh/models/ServiceDefinitionOverviewDto.js +0 -64
- package/dist/client/kqh/models/ServiceDto.d.ts +0 -59
- package/dist/client/kqh/models/ServiceDto.js +0 -52
- package/dist/client/kqh/models/ServiceExecutionConsumerDto.d.ts +0 -67
- package/dist/client/kqh/models/ServiceExecutionConsumerDto.js +0 -57
- package/dist/client/kqh/models/ServiceExecutionDto.d.ts +0 -49
- package/dist/client/kqh/models/ServiceExecutionDto.js +0 -44
- package/dist/client/kqh/models/ServiceOverviewDto.d.ts +0 -115
- package/dist/client/kqh/models/ServiceOverviewDto.js +0 -85
- package/dist/client/kqh/models/ServiceSource.d.ts +0 -31
- package/dist/client/kqh/models/ServiceSource.js +0 -52
- package/dist/client/kqh/models/SortObject.d.ts +0 -43
- package/dist/client/kqh/models/SortObject.js +0 -42
- package/dist/client/kqh/models/SubscriptionDto.d.ts +0 -58
- package/dist/client/kqh/models/SubscriptionDto.js +0 -49
- package/dist/client/kqh/models/TaxonomyElement.d.ts +0 -43
- package/dist/client/kqh/models/TaxonomyElement.js +0 -42
- package/dist/client/kqh/models/UpdateDataPoolRequest.d.ts +0 -109
- package/dist/client/kqh/models/UpdateDataPoolRequest.js +0 -66
- package/dist/client/kqh/models/UpdateResourceConfigurationRequest.d.ts +0 -23
- package/dist/client/kqh/models/UpdateResourceConfigurationRequest.js +0 -49
- package/dist/client/kqh/models/UpdateVersionRequest.d.ts +0 -44
- package/dist/client/kqh/models/UpdateVersionRequest.js +0 -43
- package/dist/client/kqh/models/UpdateWorkflowServiceRequest.d.ts +0 -31
- package/dist/client/kqh/models/UpdateWorkflowServiceRequest.js +0 -27
- package/dist/client/kqh/models/ValidationResult.d.ts +0 -52
- package/dist/client/kqh/models/ValidationResult.js +0 -50
- package/dist/client/kqh/models/index.d.ts +0 -57
- package/dist/client/kqh/models/index.js +0 -59
- package/dist/client/kqh/runtime.d.ts +0 -181
- package/dist/client/kqh/runtime.js +0 -325
- package/dist/model/account.d.ts +0 -5
- 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
|
|
88
|
+
Set `DEBUG=1` to print all debug output:
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
|
-
DEBUG
|
|
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(
|
|
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({
|
|
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(
|
|
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}`);
|
package/dist/commands/init.js
CHANGED
|
@@ -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
|
|
56
|
-
if (!
|
|
57
|
-
error(
|
|
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,
|
|
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.
|
|
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:
|
|
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
|
});
|
package/dist/commands/up.js
CHANGED
|
@@ -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 {
|
|
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,
|
|
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
|
|
33
|
+
buildJob = await qhubService.getBuildJob(service.id);
|
|
34
34
|
if (!silentMode) {
|
|
35
35
|
if (buildJob.step !== currentStep) {
|
|
36
|
-
if (buildJob.step ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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
|
|
149
|
-
if (
|
|
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 ===
|
|
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 ===
|
|
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 {
|
|
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
|
|
6
|
-
|
|
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
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
getBuildJob(
|
|
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,
|
|
22
|
-
updateService(serviceConfig: ManagedServiceConfig, description: string, sourceCode: Blob,
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
65
|
-
const
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
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.
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
await
|
|
158
|
-
|
|
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,
|
|
166
|
-
const
|
|
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.
|
|
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
|
-
|
|
200
|
-
serviceId:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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.
|
|
206
|
-
|
|
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.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
145
|
+
await this.apiClient.managedServices.updateManagedServiceResourceConfiguration(serviceId, {
|
|
146
|
+
body: {
|
|
147
|
+
type: 'gpu',
|
|
148
|
+
amount: gpuCount,
|
|
149
|
+
accelerator: gpuAccelerator
|
|
150
|
+
},
|
|
216
151
|
});
|
|
217
|
-
if (
|
|
218
|
-
await this.
|
|
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.
|
|
226
|
-
|
|
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.
|
|
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
|
|
35
|
+
"isomorphic-git": "^1",
|
|
36
36
|
"ora": "^9",
|
|
37
37
|
"tty-table": "^5",
|
|
38
38
|
"update-notifier": "^7",
|