@quantum-hub/qhubctl 1.2.0 → 1.3.1
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 +136 -20
- package/dist/client/kqh/models/UpdateWorkflowServiceRequest.js +0 -11
- package/dist/commands/compress.d.ts +2 -0
- package/dist/commands/compress.js +23 -0
- package/dist/commands/datapool-upload.d.ts +2 -0
- package/dist/commands/datapool-upload.js +184 -0
- package/dist/commands/get-context.d.ts +2 -0
- package/dist/commands/get-context.js +12 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +66 -0
- package/dist/commands/list-contexts.d.ts +2 -0
- package/dist/commands/list-contexts.js +28 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +59 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.js +20 -0
- package/dist/commands/openapi.d.ts +2 -0
- package/dist/commands/openapi.js +244 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +126 -0
- package/dist/commands/serve.d.ts +2 -0
- package/dist/commands/serve.js +35 -0
- package/dist/commands/services.d.ts +2 -0
- package/dist/commands/services.js +48 -0
- package/dist/commands/set-context.d.ts +2 -0
- package/dist/commands/set-context.js +52 -0
- package/dist/commands/up.d.ts +2 -0
- package/dist/commands/up.js +161 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/config-dir.d.ts +6 -0
- package/dist/lib/config-dir.js +8 -0
- package/dist/lib/entrypoint-resolver.js +2 -2
- package/dist/lib/output.d.ts +14 -0
- package/dist/lib/output.js +22 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +8 -0
- package/dist/model/command.d.ts +14 -11
- package/dist/model/command.js +44 -40
- package/dist/program.d.ts +2 -0
- package/dist/program.js +47 -0
- package/dist/service/command-service.d.ts +1 -5
- package/dist/service/command-service.js +1 -5
- package/dist/service/quantum-hub-service.d.ts +1 -3
- package/dist/service/quantum-hub-service.js +2 -2
- package/package.json +31 -62
- package/bin/dev.cmd +0 -3
- package/bin/dev.js +0 -17
- package/bin/run.cmd +0 -3
- package/bin/run.js +0 -12
- package/dist/commands/compress/index.d.ts +0 -6
- package/dist/commands/compress/index.js +0 -25
- package/dist/commands/datapool/upload.d.ts +0 -23
- package/dist/commands/datapool/upload.js +0 -260
- package/dist/commands/get-context/index.d.ts +0 -6
- package/dist/commands/get-context/index.js +0 -10
- package/dist/commands/init/index.d.ts +0 -10
- package/dist/commands/init/index.js +0 -75
- package/dist/commands/list-contexts/index.d.ts +0 -9
- package/dist/commands/list-contexts/index.js +0 -30
- package/dist/commands/login/index.d.ts +0 -10
- package/dist/commands/login/index.js +0 -62
- package/dist/commands/logout/index.d.ts +0 -6
- package/dist/commands/logout/index.js +0 -18
- package/dist/commands/openapi/index.d.ts +0 -26
- package/dist/commands/openapi/index.js +0 -307
- package/dist/commands/run/index.d.ts +0 -18
- package/dist/commands/run/index.js +0 -150
- package/dist/commands/serve/index.d.ts +0 -11
- package/dist/commands/serve/index.js +0 -51
- package/dist/commands/services/index.d.ts +0 -14
- package/dist/commands/services/index.js +0 -65
- package/dist/commands/set-context/index.d.ts +0 -17
- package/dist/commands/set-context/index.js +0 -59
- package/dist/commands/up/index.d.ts +0 -19
- package/dist/commands/up/index.js +0 -190
- package/oclif.manifest.json +0 -548
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { ux } from '@oclif/core';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { zipProjectFolder } from '../../lib/up/zip.js';
|
|
5
|
-
import { AbstractCommand } from '../../model/command.js';
|
|
6
|
-
export default class Compress extends AbstractCommand {
|
|
7
|
-
static description = `Compresses the current project and creates a ZIP file. Use the '.dockerignore' file to exclude files and directories from the ZIP file.`;
|
|
8
|
-
static examples = [
|
|
9
|
-
'$ qhubctl compress',
|
|
10
|
-
];
|
|
11
|
-
async run() {
|
|
12
|
-
ux.action.start('Compressing project');
|
|
13
|
-
const targetDirectory = process.cwd();
|
|
14
|
-
const targetFilename = 'service.zip';
|
|
15
|
-
const targetFilePath = path.join(targetDirectory, '/', targetFilename);
|
|
16
|
-
// remove the file if it already exists
|
|
17
|
-
if (fs.existsSync(targetFilePath)) {
|
|
18
|
-
fs.unlinkSync(targetFilePath);
|
|
19
|
-
}
|
|
20
|
-
await zipProjectFolder('./', targetDirectory, targetFilename);
|
|
21
|
-
ux.action.stop('Succeeded.');
|
|
22
|
-
ux.stdout(`\nFile created at: ${targetFilePath}`);
|
|
23
|
-
this.exit();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { AuthenticatedCommand } from '../../model/command.js';
|
|
2
|
-
import { HubPlatformClient } from "@quantum-hub/qhub-api/platform";
|
|
3
|
-
export default class DatapoolUpload extends AuthenticatedCommand {
|
|
4
|
-
static readonly description = "Upload files to a data pool";
|
|
5
|
-
static readonly examples: string[];
|
|
6
|
-
static readonly flags: {
|
|
7
|
-
file: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
-
'datapool-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
-
};
|
|
10
|
-
hubPlatformClient: HubPlatformClient;
|
|
11
|
-
init(): Promise<void>;
|
|
12
|
-
run(): Promise<void>;
|
|
13
|
-
private ensureFilesExist;
|
|
14
|
-
private ensureDatapoolExists;
|
|
15
|
-
private createOrSelectDataPool;
|
|
16
|
-
private promptForDatapoolAction;
|
|
17
|
-
private selectExistingDatapool;
|
|
18
|
-
private createDatapool;
|
|
19
|
-
private uploadToDatapool;
|
|
20
|
-
private handleError;
|
|
21
|
-
private extractError;
|
|
22
|
-
private printHttpSpecificError;
|
|
23
|
-
}
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import { input, select } from '@inquirer/prompts';
|
|
2
|
-
import { Flags, ux } from '@oclif/core';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { AuthenticatedCommand } from '../../model/command.js';
|
|
6
|
-
import { HubPlatformClient, HubPlatformError } from "@quantum-hub/qhub-api/platform";
|
|
7
|
-
export default class DatapoolUpload extends AuthenticatedCommand {
|
|
8
|
-
static description = 'Upload files to a data pool';
|
|
9
|
-
static examples = [
|
|
10
|
-
'$ qhubctl datapool upload --file ./data/dataset.csv',
|
|
11
|
-
'$ qhubctl datapool upload --file ./data/dataset.csv --file ./data/metadata.json',
|
|
12
|
-
'$ qhubctl datapool upload -f ./data/dataset.csv -f ./data/metadata.json --datapool-id 550e8400-e29b-41d4-a716-446655440000',
|
|
13
|
-
];
|
|
14
|
-
static flags = {
|
|
15
|
-
file: Flags.string({
|
|
16
|
-
char: 'f',
|
|
17
|
-
description: 'Path to a file to upload (can be specified multiple times)',
|
|
18
|
-
required: true,
|
|
19
|
-
multiple: true,
|
|
20
|
-
}),
|
|
21
|
-
'datapool-id': Flags.string({
|
|
22
|
-
char: 'd',
|
|
23
|
-
description: 'ID of the data pool to upload the files to (optional - will prompt to create a new one if not provided)',
|
|
24
|
-
required: false,
|
|
25
|
-
}),
|
|
26
|
-
};
|
|
27
|
-
hubPlatformClient;
|
|
28
|
-
async init() {
|
|
29
|
-
await this.parse(DatapoolUpload);
|
|
30
|
-
await super.init();
|
|
31
|
-
// Initialize the new Quantum Hub API client with proper configuration
|
|
32
|
-
// The new SDK expects the full base path including /qc-catalog
|
|
33
|
-
const baseUrl = this.userConfig.endpoint?.basePath
|
|
34
|
-
? `${this.userConfig.endpoint.basePath}/qc-catalog`
|
|
35
|
-
: undefined; // Will use the default environment if no custom endpoint is set
|
|
36
|
-
const apiKey = this.userConfig.auth?.value;
|
|
37
|
-
this.hubPlatformClient = new HubPlatformClient({
|
|
38
|
-
baseUrl: baseUrl,
|
|
39
|
-
apiKey: apiKey,
|
|
40
|
-
organizationId: this.userConfig.context?.isOrganization ? this.userConfig.context.id : undefined,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
async run() {
|
|
44
|
-
const { flags } = await this.parse(DatapoolUpload);
|
|
45
|
-
const filePaths = flags.file;
|
|
46
|
-
let datapoolId = flags['datapool-id'];
|
|
47
|
-
this.ensureFilesExist(filePaths);
|
|
48
|
-
// Handle datapool ID - check if exists or prompt to create
|
|
49
|
-
datapoolId = await this.ensureDatapoolExists(datapoolId, filePaths);
|
|
50
|
-
for (const filePath of filePaths) {
|
|
51
|
-
const fileName = path.basename(filePath);
|
|
52
|
-
ux.action.start(`Uploading ${fileName} to data pool ${datapoolId}`);
|
|
53
|
-
await this.uploadToDatapool(datapoolId, filePath);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
ensureFilesExist(filePaths) {
|
|
57
|
-
// Validate files exist
|
|
58
|
-
for (const filePath of filePaths) {
|
|
59
|
-
if (!fs.existsSync(filePath)) {
|
|
60
|
-
this.error(`File not found: ${filePath}`);
|
|
61
|
-
}
|
|
62
|
-
// Check if file is readable
|
|
63
|
-
try {
|
|
64
|
-
fs.accessSync(filePath, fs.constants.R_OK);
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
this.error(`Cannot read file: ${filePath}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
async ensureDatapoolExists(datapoolId, filePaths) {
|
|
72
|
-
// If no datapool ID provided, prompt to create or select one
|
|
73
|
-
if (!datapoolId) {
|
|
74
|
-
this.log('No data pool ID provided. You can create a new data pool or select an existing one.');
|
|
75
|
-
return await this.createOrSelectDataPool(filePaths);
|
|
76
|
-
}
|
|
77
|
-
// If datapool ID provided, check if it exists
|
|
78
|
-
try {
|
|
79
|
-
await this.hubPlatformClient.dataPools.getDataPool({ "id": datapoolId });
|
|
80
|
-
return datapoolId;
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
if (error instanceof HubPlatformError && error.statusCode === 404) {
|
|
84
|
-
this.log(`Data pool ${datapoolId} not found. Would you like to create a new data pool instead?`);
|
|
85
|
-
return await this.createOrSelectDataPool(filePaths);
|
|
86
|
-
}
|
|
87
|
-
this.handleError(error, datapoolId);
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
async createOrSelectDataPool(filePaths) {
|
|
92
|
-
const action = await this.promptForDatapoolAction();
|
|
93
|
-
if (action === 'create') {
|
|
94
|
-
const newDatapoolId = await this.createDatapool(filePaths[0]);
|
|
95
|
-
if (!newDatapoolId) {
|
|
96
|
-
this.error('Failed to create datapool');
|
|
97
|
-
}
|
|
98
|
-
return newDatapoolId;
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
// List and select existing datapool
|
|
102
|
-
return await this.selectExistingDatapool();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
async promptForDatapoolAction() {
|
|
106
|
-
// First, check if there are any existing DataPools
|
|
107
|
-
try {
|
|
108
|
-
const dataPools = await this.hubPlatformClient.dataPools.getDataPools();
|
|
109
|
-
if (dataPools.length === 0) {
|
|
110
|
-
this.log('No data pools found. Creating a new one...');
|
|
111
|
-
return 'create';
|
|
112
|
-
}
|
|
113
|
-
// Offer choice between creating new or selecting existing using select prompt
|
|
114
|
-
return await select({
|
|
115
|
-
message: 'What would you like to do?',
|
|
116
|
-
choices: [
|
|
117
|
-
{ name: 'Create a new data pool', value: 'create' },
|
|
118
|
-
{ name: 'Select an existing data pool', value: 'select' }
|
|
119
|
-
],
|
|
120
|
-
default: 'create'
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
// If we can't list datapools, default to creating
|
|
125
|
-
return 'create';
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async selectExistingDatapool() {
|
|
129
|
-
try {
|
|
130
|
-
const dataPools = await this.hubPlatformClient.dataPools.getDataPools();
|
|
131
|
-
if (dataPools.length === 0) {
|
|
132
|
-
this.log('No existing data pools found. Creating a new one...');
|
|
133
|
-
const newDatapoolId = await this.createDatapool('upload');
|
|
134
|
-
if (!newDatapoolId) {
|
|
135
|
-
this.error('Failed to create datapool');
|
|
136
|
-
}
|
|
137
|
-
return newDatapoolId;
|
|
138
|
-
}
|
|
139
|
-
this.log('\nAvailable data pools:');
|
|
140
|
-
dataPools.forEach((pool, index) => {
|
|
141
|
-
this.log(`${index + 1}. ${pool.name} (${pool.id})`);
|
|
142
|
-
});
|
|
143
|
-
const selection = await input({
|
|
144
|
-
message: `Select a data pool (1-${dataPools.length}) or enter the ID directly:`,
|
|
145
|
-
});
|
|
146
|
-
// Check if it's a number (index selection)
|
|
147
|
-
const index = parseInt(selection, 10);
|
|
148
|
-
if (!isNaN(index) && index >= 1 && index <= dataPools.length) {
|
|
149
|
-
return dataPools[index - 1].id;
|
|
150
|
-
}
|
|
151
|
-
// Assume it's a direct ID
|
|
152
|
-
return selection;
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
this.error('Failed to retrieve data pools');
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
async createDatapool(suggestedName) {
|
|
159
|
-
try {
|
|
160
|
-
const name = await input({
|
|
161
|
-
message: 'Enter a name for the new data pool:',
|
|
162
|
-
default: `Data Pool for ${suggestedName}`,
|
|
163
|
-
});
|
|
164
|
-
const datapool = await this.hubPlatformClient.dataPools.createDataPool({
|
|
165
|
-
name,
|
|
166
|
-
});
|
|
167
|
-
this.log(`✓ Created new data pool: ${datapool.name} (${datapool.id})`);
|
|
168
|
-
return datapool.id;
|
|
169
|
-
}
|
|
170
|
-
catch (error) {
|
|
171
|
-
if (error instanceof HubPlatformError) {
|
|
172
|
-
const errorMessage = error.body || error.message || 'Unknown API error occurred';
|
|
173
|
-
this.error(`Failed to create data pool (HTTP ${error.statusCode}): ${JSON.stringify(errorMessage)}`);
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
177
|
-
this.error(`Failed to create data pool: ${errorMessage}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
async uploadToDatapool(datapoolId, filePath) {
|
|
182
|
-
const fileName = path.basename(filePath);
|
|
183
|
-
try {
|
|
184
|
-
const fileStream = fs.createReadStream(filePath);
|
|
185
|
-
const result = await this.hubPlatformClient.dataPools.addDataPoolFile({ "id": datapoolId, file: fileStream });
|
|
186
|
-
ux.action.stop('✓');
|
|
187
|
-
this.log(`Successfully uploaded ${fileName} to data pool ${datapoolId}`);
|
|
188
|
-
this.log(`File ID: ${result.id}`);
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
this.handleError(error, datapoolId);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
handleError(error, datapoolId) {
|
|
195
|
-
ux.action.stop('✗');
|
|
196
|
-
if (error instanceof HubPlatformError) {
|
|
197
|
-
const statusCode = error.statusCode ?? 500; // Default to 500 if statusCode is not set
|
|
198
|
-
let errorMessage = 'Unknown API error occurred';
|
|
199
|
-
if (error.body) {
|
|
200
|
-
if (typeof error.body === 'string') {
|
|
201
|
-
errorMessage = error.body;
|
|
202
|
-
}
|
|
203
|
-
else if (typeof error.body === 'object') {
|
|
204
|
-
errorMessage = this.extractError(error);
|
|
205
|
-
}
|
|
206
|
-
else if (error.message) {
|
|
207
|
-
errorMessage = error.message;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
else if (error.message) {
|
|
211
|
-
errorMessage = error.message;
|
|
212
|
-
}
|
|
213
|
-
this.printHttpSpecificError(datapoolId || 'unknown', statusCode, errorMessage);
|
|
214
|
-
return error;
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
218
|
-
this.error(`Unexpected Error: ${errorMessage}`);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
extractError(error) {
|
|
222
|
-
const bodyObj = error.body;
|
|
223
|
-
if (bodyObj.errorMessage) {
|
|
224
|
-
return bodyObj.errorMessage;
|
|
225
|
-
}
|
|
226
|
-
else if (bodyObj.detail) {
|
|
227
|
-
return bodyObj.detail;
|
|
228
|
-
}
|
|
229
|
-
else if (bodyObj.message) {
|
|
230
|
-
return bodyObj.message;
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
return JSON.stringify(error.body);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
printHttpSpecificError(datapoolId, statusCode, errorMessage) {
|
|
237
|
-
// Provide specific error messages based on status code
|
|
238
|
-
if (statusCode === 404) {
|
|
239
|
-
throw new Error(`Data pool not found: ${datapoolId}`);
|
|
240
|
-
}
|
|
241
|
-
else if (statusCode === 403) {
|
|
242
|
-
throw new Error(`Access denied. You don't have permission to upload to data pool: ${datapoolId}`);
|
|
243
|
-
}
|
|
244
|
-
else if (statusCode === 400) {
|
|
245
|
-
throw new Error(`Bad request. Please check the data pool ID and file format. Details: ${errorMessage}`);
|
|
246
|
-
}
|
|
247
|
-
else if (statusCode === 401) {
|
|
248
|
-
throw new Error(`Authentication failed. Please login again using 'qhubctl login'`);
|
|
249
|
-
}
|
|
250
|
-
else if (statusCode === 413) {
|
|
251
|
-
throw new Error(`File too large. The file exceeds the maximum allowed size for data pool uploads`);
|
|
252
|
-
}
|
|
253
|
-
else if (statusCode === 415) {
|
|
254
|
-
throw new Error(`Unsupported file type. Please check if the file format is supported`);
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
throw new Error(`Failed to upload file (HTTP ${statusCode}): ${errorMessage}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AuthenticatedCommand } from '../../model/command.js';
|
|
2
|
-
export default class GetContext extends AuthenticatedCommand {
|
|
3
|
-
static description = 'Get the current context, i.e., the personal or organization account you are currently working with.';
|
|
4
|
-
static examples = [
|
|
5
|
-
'$ qhubctl get-context',
|
|
6
|
-
];
|
|
7
|
-
async run() {
|
|
8
|
-
// printing the current context is done by parent 'init' method
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AbstractCommand } from '../../model/command.js';
|
|
2
|
-
export default class Init extends AbstractCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static flags: {
|
|
6
|
-
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
-
'non-interactive': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
-
};
|
|
9
|
-
run(): Promise<void>;
|
|
10
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { Flags, ux } from '@oclif/core';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { DEFAULT_STARTER, promptUserInput } from '../../lib/init-user-input.js';
|
|
5
|
-
import { randomName } from '../../lib/random-name.js';
|
|
6
|
-
import { downloadRepository, extractStarter } from '../../lib/repository.js';
|
|
7
|
-
import { AbstractCommand } from '../../model/command.js';
|
|
8
|
-
import { Runtime } from '../../model/managed-service-config.js';
|
|
9
|
-
import { writeServiceConfig } from '../../service/service-config-service.js';
|
|
10
|
-
export default class Init extends AbstractCommand {
|
|
11
|
-
static description = 'Bootstrap project to create a service.';
|
|
12
|
-
static examples = [
|
|
13
|
-
'$ qhubctl init',
|
|
14
|
-
];
|
|
15
|
-
static flags = {
|
|
16
|
-
name: Flags.string({ description: 'The name of the service', required: false }),
|
|
17
|
-
'non-interactive': Flags.boolean({
|
|
18
|
-
description: 'Run it in non-interactive mode',
|
|
19
|
-
required: false,
|
|
20
|
-
}),
|
|
21
|
-
};
|
|
22
|
-
async run() {
|
|
23
|
-
const { flags } = await this.parse(Init);
|
|
24
|
-
const nonInteractive = flags['non-interactive'];
|
|
25
|
-
let name = flags.name ?? randomName();
|
|
26
|
-
const input = nonInteractive ? {
|
|
27
|
-
cpu: 1,
|
|
28
|
-
descriptionFile: 'README.md',
|
|
29
|
-
gpu: undefined,
|
|
30
|
-
memory: 1,
|
|
31
|
-
name: name,
|
|
32
|
-
starter: DEFAULT_STARTER,
|
|
33
|
-
} : await promptUserInput(name);
|
|
34
|
-
name = input.name || name;
|
|
35
|
-
// use current directory if no template is selected
|
|
36
|
-
const targetDirectory = input.starter ? path.join(process.cwd(), name) : path.join(process.cwd());
|
|
37
|
-
// exit if the target directory already exists (only if a starter is selected)
|
|
38
|
-
if (input.starter && fs.existsSync(targetDirectory)) {
|
|
39
|
-
ux.error(`Destination ${targetDirectory} already exists. Please choose another name.`);
|
|
40
|
-
}
|
|
41
|
-
const serviceConfig = {
|
|
42
|
-
name,
|
|
43
|
-
descriptionFile: input.descriptionFile ?? 'README.md',
|
|
44
|
-
resources: {
|
|
45
|
-
cpu: input.cpu,
|
|
46
|
-
memory: input.memory,
|
|
47
|
-
},
|
|
48
|
-
runtime: input.starter ? input.starter.runtime : Runtime.PYTHON_TEMPLATE,
|
|
49
|
-
};
|
|
50
|
-
if (input.gpu) {
|
|
51
|
-
serviceConfig.resources.gpu = {
|
|
52
|
-
count: 1,
|
|
53
|
-
type: input.gpu,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
// make sure the target directory exists if a starter is selected
|
|
57
|
-
if (input.starter) {
|
|
58
|
-
fs.mkdirSync(targetDirectory);
|
|
59
|
-
}
|
|
60
|
-
writeServiceConfig(targetDirectory, serviceConfig);
|
|
61
|
-
if (input.starter) {
|
|
62
|
-
const zip = await downloadRepository(input.starter.url);
|
|
63
|
-
const starterName = path.basename(input.starter.url, '.zip');
|
|
64
|
-
extractStarter(zip, starterName, targetDirectory);
|
|
65
|
-
}
|
|
66
|
-
ux.stdout('\u{1F389} Initialized project. Happy hacking!');
|
|
67
|
-
if (input.starter) {
|
|
68
|
-
ux.stdout('\n Next steps:');
|
|
69
|
-
ux.stdout(` \u{2022} cd ${name}`);
|
|
70
|
-
ux.stdout(' \u{2022} qhubctl up (deploys your code as a service to Kipu Quantum Hub)');
|
|
71
|
-
ux.stdout(' \u{2022} qhubclt run (executes your service using the data from the input directory)');
|
|
72
|
-
}
|
|
73
|
-
ux.stdout('');
|
|
74
|
-
}
|
|
75
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { AuthenticatedCommand } from '../../model/command.js';
|
|
2
|
-
import QuantumHubService from '../../service/quantum-hub-service.js';
|
|
3
|
-
export default class ListContexts extends AuthenticatedCommand {
|
|
4
|
-
static description: string;
|
|
5
|
-
static examples: string[];
|
|
6
|
-
quantumHubService: QuantumHubService;
|
|
7
|
-
init(): Promise<void>;
|
|
8
|
-
run(): Promise<void>;
|
|
9
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { ux } from '@oclif/core';
|
|
2
|
-
import Table from 'tty-table';
|
|
3
|
-
import { AuthenticatedCommand } from '../../model/command.js';
|
|
4
|
-
import QuantumHubService from '../../service/quantum-hub-service.js';
|
|
5
|
-
export default class ListContexts extends AuthenticatedCommand {
|
|
6
|
-
static description = 'Retrieves the available contexts, i.e., the personal or organizational accounts available to you to work with.';
|
|
7
|
-
static examples = [
|
|
8
|
-
'$ qhubctl list-contexts',
|
|
9
|
-
];
|
|
10
|
-
quantumHubService;
|
|
11
|
-
async init() {
|
|
12
|
-
await super.init();
|
|
13
|
-
this.quantumHubService = new QuantumHubService(this, this.config, this.userConfig);
|
|
14
|
-
}
|
|
15
|
-
async run() {
|
|
16
|
-
ux.action.start('Loading available contexts');
|
|
17
|
-
const accounts = await this.quantumHubService.getAccounts();
|
|
18
|
-
ux.action.stop('Done');
|
|
19
|
-
const tableData = accounts
|
|
20
|
-
.map(account => ({ displayName: account.displayName, id: account.id, isOrganization: account.isOrganization }));
|
|
21
|
-
const header = [
|
|
22
|
-
{ value: "displayName", alias: "Name", align: "left" },
|
|
23
|
-
{ value: "id", alias: "ID", align: "left" },
|
|
24
|
-
{ value: "isOrganization", alias: "Account", width: 14, formatter: (value) => value ? "Organization" : "Personal" },
|
|
25
|
-
];
|
|
26
|
-
const t = Table(header, tableData);
|
|
27
|
-
ux.stdout(t.render());
|
|
28
|
-
ux.stdout();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AbstractCommand } from '../../model/command.js';
|
|
2
|
-
export default class Login extends AbstractCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static flags: {
|
|
6
|
-
token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
-
'base-path': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
-
};
|
|
9
|
-
run(): Promise<void>;
|
|
10
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { password } from '@inquirer/prompts';
|
|
2
|
-
import { Flags, ux } from '@oclif/core';
|
|
3
|
-
import { AbstractCommand } from '../../model/command.js';
|
|
4
|
-
import { AuthType, defaultBasePath } from '../../model/user-config.js';
|
|
5
|
-
import AuthService from '../../service/auth-service.js';
|
|
6
|
-
import { writeUserConfig } from '../../service/user-config-service.js';
|
|
7
|
-
export default class Login extends AbstractCommand {
|
|
8
|
-
static description = 'Login with your credentials';
|
|
9
|
-
static examples = [
|
|
10
|
-
'$ qhubctl login -t <personal access token>',
|
|
11
|
-
`$ qhubctl login -t <personal access token> --base-path ${defaultBasePath}`,
|
|
12
|
-
];
|
|
13
|
-
static flags = {
|
|
14
|
-
token: Flags.string({ char: 't', description: 'Your personal access token', required: false }),
|
|
15
|
-
'base-path': Flags.string({ description: 'Custom base path for authentication (development)', required: false }),
|
|
16
|
-
};
|
|
17
|
-
async run() {
|
|
18
|
-
const { flags } = await this.parse(Login);
|
|
19
|
-
let { token } = flags;
|
|
20
|
-
const { 'base-path': basePath } = flags;
|
|
21
|
-
if (!token) {
|
|
22
|
-
token = await password({ message: 'Enter or paste your personal access token?', mask: true });
|
|
23
|
-
}
|
|
24
|
-
if (!token) {
|
|
25
|
-
throw new Error('Invalid credentials. Please try again.');
|
|
26
|
-
}
|
|
27
|
-
// Show warning if custom base path is provided
|
|
28
|
-
if (basePath) {
|
|
29
|
-
this.showCustomBasePathWarning();
|
|
30
|
-
}
|
|
31
|
-
ux.action.start('Login');
|
|
32
|
-
// Create temporary config with custom basePath for authentication
|
|
33
|
-
const tempUserConfig = basePath ? {
|
|
34
|
-
...this.userConfig,
|
|
35
|
-
endpoint: {
|
|
36
|
-
basePath,
|
|
37
|
-
defaultHeaders: this.userConfig.endpoint?.defaultHeaders || {}
|
|
38
|
-
}
|
|
39
|
-
} : this.userConfig;
|
|
40
|
-
const user = await new AuthService(tempUserConfig).authenticate(token);
|
|
41
|
-
const userConfig = {
|
|
42
|
-
...this.userConfig,
|
|
43
|
-
auth: {
|
|
44
|
-
type: AuthType.API_KEY,
|
|
45
|
-
value: token,
|
|
46
|
-
},
|
|
47
|
-
context: {
|
|
48
|
-
id: user.id,
|
|
49
|
-
displayName: `${user.firstname} ${user.lastname}`,
|
|
50
|
-
isOrganization: false,
|
|
51
|
-
},
|
|
52
|
-
...(basePath && {
|
|
53
|
-
endpoint: {
|
|
54
|
-
basePath,
|
|
55
|
-
defaultHeaders: this.userConfig.endpoint?.defaultHeaders || {}
|
|
56
|
-
}
|
|
57
|
-
})
|
|
58
|
-
};
|
|
59
|
-
writeUserConfig(this.config.configDir, userConfig);
|
|
60
|
-
ux.action.stop('successful');
|
|
61
|
-
}
|
|
62
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ux } from '@oclif/core';
|
|
2
|
-
import { AbstractCommand } from '../../model/command.js';
|
|
3
|
-
import { writeUserConfig } from '../../service/user-config-service.js';
|
|
4
|
-
export default class Logout extends AbstractCommand {
|
|
5
|
-
static description = 'Logout';
|
|
6
|
-
static examples = [
|
|
7
|
-
'$ qhubctl logout',
|
|
8
|
-
];
|
|
9
|
-
async run() {
|
|
10
|
-
ux.action.start('Logout');
|
|
11
|
-
writeUserConfig(this.config.configDir, {
|
|
12
|
-
...this.userConfig,
|
|
13
|
-
auth: undefined,
|
|
14
|
-
context: undefined,
|
|
15
|
-
});
|
|
16
|
-
ux.action.stop('successful');
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { AbstractCommand } from '../../model/command.js';
|
|
2
|
-
export default class Openapi extends AbstractCommand {
|
|
3
|
-
static readonly description: string;
|
|
4
|
-
static readonly examples: string[];
|
|
5
|
-
static readonly flags: {
|
|
6
|
-
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
-
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
-
format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
-
entrypoint: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
package: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
method: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
};
|
|
13
|
-
run(): Promise<void>;
|
|
14
|
-
private getErrorMessage;
|
|
15
|
-
private isErrorWithMessage;
|
|
16
|
-
private checkDeprecatedFiles;
|
|
17
|
-
private determineOutputPath;
|
|
18
|
-
private generateFromCode;
|
|
19
|
-
private validateCommandResult;
|
|
20
|
-
private formatPythonError;
|
|
21
|
-
private fetchDefaultTemplate;
|
|
22
|
-
private displaySuccess;
|
|
23
|
-
private isProjectSupported;
|
|
24
|
-
private showRuntimeNotSupportedWarning;
|
|
25
|
-
private showConfigurationHelp;
|
|
26
|
-
}
|