@super-protocol/sp-cli 0.0.8 → 0.0.10-beta
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 +200 -167
- package/dist/commands/account/base.d.ts +3 -4
- package/dist/commands/account/base.js +12 -9
- package/dist/commands/account/forget.d.ts +1 -1
- package/dist/commands/account/forget.js +66 -17
- package/dist/commands/account/get-sppi.js +7 -11
- package/dist/commands/account/info.d.ts +1 -13
- package/dist/commands/account/info.js +25 -45
- package/dist/commands/account/list.js +6 -6
- package/dist/commands/account/login.d.ts +10 -3
- package/dist/commands/account/login.js +143 -87
- package/dist/commands/account/switch.d.ts +3 -0
- package/dist/commands/account/switch.js +31 -11
- package/dist/commands/assets/base.d.ts +39 -0
- package/dist/commands/assets/base.js +217 -0
- package/dist/commands/assets/create.d.ts +41 -0
- package/dist/commands/assets/create.js +277 -0
- package/dist/commands/assets/delete.d.ts +14 -0
- package/dist/commands/assets/delete.js +69 -0
- package/dist/commands/assets/get.d.ts +14 -0
- package/dist/commands/assets/get.js +79 -0
- package/dist/commands/assets/list.d.ts +7 -0
- package/dist/commands/assets/list.js +33 -0
- package/dist/commands/assets/update.d.ts +44 -0
- package/dist/commands/assets/update.js +321 -0
- package/dist/commands/base.d.ts +1 -0
- package/dist/commands/base.js +6 -0
- package/dist/config/config.schema.d.ts +2 -11
- package/dist/config/config.schema.js +2 -7
- package/dist/constants.d.ts +3 -3
- package/dist/constants.js +3 -3
- package/dist/errors.d.ts +0 -2
- package/dist/errors.js +0 -2
- package/dist/hooks/prerun/auth.js +5 -9
- package/dist/interfaces/config-manager.interface.d.ts +3 -1
- package/dist/lib/container.d.ts +4 -12
- package/dist/lib/container.js +28 -113
- package/dist/lib/swarm-client/fetch-api.d.ts +7 -0
- package/dist/lib/swarm-client/fetch-api.js +41 -0
- package/dist/lib/swarm-client/fetch-timeout.client.d.ts +1 -0
- package/dist/lib/swarm-client/fetch-timeout.client.js +32 -0
- package/dist/lib/swarm-client/index.d.ts +6 -0
- package/dist/lib/swarm-client/index.js +31 -0
- package/dist/lib/swarm-client/middlewares/authorization.middleware.d.ts +2 -0
- package/dist/lib/swarm-client/middlewares/authorization.middleware.js +12 -0
- package/dist/lib/swarm-client/middlewares/index.d.ts +6 -0
- package/dist/lib/swarm-client/middlewares/index.js +5 -0
- package/dist/lib/swarm-client/middlewares/logger.middleware.d.ts +2 -0
- package/dist/lib/swarm-client/middlewares/logger.middleware.js +30 -0
- package/dist/lib/swarm-client/middlewares/request-id.middleware.d.ts +2 -0
- package/dist/lib/swarm-client/middlewares/request-id.middleware.js +13 -0
- package/dist/lib/swarm-client/types.d.ts +23 -0
- package/dist/lib/swarm-client/types.js +1 -0
- package/dist/managers/account-manager.d.ts +1 -0
- package/dist/managers/account-manager.js +13 -18
- package/dist/managers/config-file-manager.d.ts +24 -17
- package/dist/managers/config-file-manager.js +285 -161
- package/dist/managers/config-manager.d.ts +6 -6
- package/dist/managers/config-manager.js +8 -8
- package/dist/services/account.service.d.ts +42 -0
- package/dist/services/account.service.js +140 -0
- package/dist/services/asset.service.d.ts +35 -0
- package/dist/services/asset.service.js +120 -0
- package/dist/services/auth.service.d.ts +4 -6
- package/dist/services/auth.service.js +108 -118
- package/dist/utils/helper.js +2 -2
- package/dist/utils/progress.js +1 -0
- package/dist/utils/prompt.service.d.ts +8 -1
- package/dist/utils/prompt.service.js +33 -1
- package/oclif.manifest.json +479 -215
- package/package.json +7 -8
- package/dist/commands/files/download.d.ts +0 -15
- package/dist/commands/files/download.js +0 -63
- package/dist/commands/files/upload.d.ts +0 -18
- package/dist/commands/files/upload.js +0 -83
- package/dist/commands/storage/base.d.ts +0 -13
- package/dist/commands/storage/base.js +0 -125
- package/dist/commands/storage/create.d.ts +0 -11
- package/dist/commands/storage/create.js +0 -53
- package/dist/commands/storage/select.d.ts +0 -9
- package/dist/commands/storage/select.js +0 -38
- package/dist/commands/storage/show.d.ts +0 -17
- package/dist/commands/storage/show.js +0 -34
- package/dist/commands/storage/update.d.ts +0 -14
- package/dist/commands/storage/update.js +0 -204
- package/dist/commands/workflows/extend-lease.d.ts +0 -17
- package/dist/commands/workflows/extend-lease.js +0 -102
- package/dist/hooks/finally/shutdown-blockchain.d.ts +0 -3
- package/dist/hooks/finally/shutdown-blockchain.js +0 -8
- package/dist/middlewares/auth-middleware.d.ts +0 -9
- package/dist/middlewares/auth-middleware.js +0 -91
- package/dist/middlewares/cookies-middleware.d.ts +0 -8
- package/dist/middlewares/cookies-middleware.js +0 -80
- package/dist/services/storage.service.d.ts +0 -73
- package/dist/services/storage.service.js +0 -378
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { password, text } from '@clack/prompts';
|
|
2
|
+
import { AssetService, AssetType, SourceType, } from '../../services/asset.service.js';
|
|
3
|
+
import { BaseCommand } from '../base.js';
|
|
4
|
+
const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
5
|
+
export class BaseAssetCommand extends BaseCommand {
|
|
6
|
+
static summary = 'Asset management';
|
|
7
|
+
assetService;
|
|
8
|
+
currentDir = process.cwd();
|
|
9
|
+
async init() {
|
|
10
|
+
await super.init();
|
|
11
|
+
await this.container.initSwarmClient().build();
|
|
12
|
+
this.assetService = new AssetService(this.container.swarmClient, this.logger);
|
|
13
|
+
}
|
|
14
|
+
parseCredentialsInput(raw, fieldName) {
|
|
15
|
+
let parsed;
|
|
16
|
+
try {
|
|
17
|
+
parsed = JSON.parse(raw);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
this.error(`${fieldName} must be valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
21
|
+
}
|
|
22
|
+
if (!isRecord(parsed)) {
|
|
23
|
+
this.error(`${fieldName} must be a JSON object`);
|
|
24
|
+
}
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
async promptCredentials(sourceType) {
|
|
28
|
+
switch (sourceType) {
|
|
29
|
+
case SourceType.STORJ: {
|
|
30
|
+
const bucket = await this.promptRequiredText('Bucket');
|
|
31
|
+
const path = await this.promptRequiredText('Path');
|
|
32
|
+
const token = await this.promptRequiredText('Access token');
|
|
33
|
+
return { bucket, path, token };
|
|
34
|
+
}
|
|
35
|
+
case SourceType.S3: {
|
|
36
|
+
const bucket = await this.promptRequiredText('Bucket');
|
|
37
|
+
const path = await this.promptRequiredText('Path');
|
|
38
|
+
const accessKey = await this.promptRequiredText('Access key');
|
|
39
|
+
const secretKey = await this.promptRequiredText('Secret key');
|
|
40
|
+
const region = await this.promptRequiredText('Region');
|
|
41
|
+
const endpoint = await this.promptOptionalText('Endpoint (optional)');
|
|
42
|
+
return {
|
|
43
|
+
bucket,
|
|
44
|
+
path,
|
|
45
|
+
accessKey,
|
|
46
|
+
secretKey,
|
|
47
|
+
region,
|
|
48
|
+
endpoint,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
case SourceType.DOCKERHUB: {
|
|
52
|
+
const image = await this.promptRequiredText('Image');
|
|
53
|
+
const registry = await this.promptOptionalText('Registry (optional)');
|
|
54
|
+
const username = await this.promptOptionalText('Username (optional)');
|
|
55
|
+
const passwordValue = await this.promptOptionalText('Password (optional)', true);
|
|
56
|
+
return {
|
|
57
|
+
image,
|
|
58
|
+
registry,
|
|
59
|
+
username,
|
|
60
|
+
password: passwordValue,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
case SourceType.GITHUB: {
|
|
64
|
+
const repo = await this.promptRequiredText('Repository (org/repo)');
|
|
65
|
+
const token = await this.promptOptionalText('Token (optional)', true);
|
|
66
|
+
const branch = await this.promptOptionalText('Branch (optional)');
|
|
67
|
+
return {
|
|
68
|
+
repo,
|
|
69
|
+
token,
|
|
70
|
+
branch,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
case SourceType.GOOGLE_DOCS: {
|
|
74
|
+
const documentId = await this.promptRequiredText('Document ID');
|
|
75
|
+
const apiKey = await this.promptOptionalText('API key (optional)');
|
|
76
|
+
const serviceAccountJson = await this.promptOptionalText('Service account JSON (optional)');
|
|
77
|
+
return {
|
|
78
|
+
documentId,
|
|
79
|
+
apiKey,
|
|
80
|
+
serviceAccountJson,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
default: {
|
|
84
|
+
const exhaustive = sourceType;
|
|
85
|
+
throw new Error(`Unsupported source type: ${exhaustive}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async promptOptionalText(message, secret = false) {
|
|
90
|
+
const prompt = secret ? password : text;
|
|
91
|
+
const value = this.ensurePromptValue(await prompt({ message }));
|
|
92
|
+
const trimmed = value.trim();
|
|
93
|
+
return trimmed ? trimmed : undefined;
|
|
94
|
+
}
|
|
95
|
+
async promptRequiredText(message) {
|
|
96
|
+
const value = this.ensurePromptValue(await text({
|
|
97
|
+
message,
|
|
98
|
+
validate(input) {
|
|
99
|
+
return input?.trim() ? undefined : 'Value is required';
|
|
100
|
+
},
|
|
101
|
+
})).trim();
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
parseSizeInput(raw) {
|
|
105
|
+
const trimmed = raw.trim();
|
|
106
|
+
if (!trimmed) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const size = Number(trimmed);
|
|
110
|
+
if (!Number.isFinite(size) || size < 0) {
|
|
111
|
+
this.error('Size must be a positive number');
|
|
112
|
+
}
|
|
113
|
+
return size;
|
|
114
|
+
}
|
|
115
|
+
getAllowedSourceTypes(type) {
|
|
116
|
+
if (type === AssetType.OUTPUT) {
|
|
117
|
+
return [SourceType.S3, SourceType.STORJ];
|
|
118
|
+
}
|
|
119
|
+
return Object.values(SourceType);
|
|
120
|
+
}
|
|
121
|
+
assertSourceTypeAllowed(type, sourceType) {
|
|
122
|
+
const allowed = this.getAllowedSourceTypes(type);
|
|
123
|
+
if (!allowed.includes(sourceType)) {
|
|
124
|
+
this.error('For output assets, sourceType must be "s3" or "storj".');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
hasCredentialsFlags(flags) {
|
|
128
|
+
return Boolean(flags.storjBucket ||
|
|
129
|
+
flags.storjPath ||
|
|
130
|
+
flags.storjToken ||
|
|
131
|
+
flags.s3Bucket ||
|
|
132
|
+
flags.s3Path ||
|
|
133
|
+
flags.s3AccessKey ||
|
|
134
|
+
flags.s3SecretKey ||
|
|
135
|
+
flags.s3Region ||
|
|
136
|
+
flags.s3Endpoint ||
|
|
137
|
+
flags.dockerImage ||
|
|
138
|
+
flags.dockerRegistry ||
|
|
139
|
+
flags.dockerUsername ||
|
|
140
|
+
flags.dockerPassword ||
|
|
141
|
+
flags.githubRepo ||
|
|
142
|
+
flags.githubToken ||
|
|
143
|
+
flags.githubBranch ||
|
|
144
|
+
flags.gdocsDocumentId ||
|
|
145
|
+
flags.gdocsApiKey ||
|
|
146
|
+
flags.gdocsServiceAccountJson);
|
|
147
|
+
}
|
|
148
|
+
buildCredentialsFromFlags(sourceType, flags) {
|
|
149
|
+
const missing = [];
|
|
150
|
+
const requireField = (value, flag) => {
|
|
151
|
+
const trimmed = value?.trim();
|
|
152
|
+
if (!trimmed) {
|
|
153
|
+
missing.push(flag);
|
|
154
|
+
}
|
|
155
|
+
return trimmed ?? '';
|
|
156
|
+
};
|
|
157
|
+
const optionalField = (value) => {
|
|
158
|
+
const trimmed = value?.trim();
|
|
159
|
+
return trimmed ? trimmed : undefined;
|
|
160
|
+
};
|
|
161
|
+
switch (sourceType) {
|
|
162
|
+
case SourceType.STORJ: {
|
|
163
|
+
const bucket = requireField(flags.storjBucket, '--storj-bucket');
|
|
164
|
+
const path = requireField(flags.storjPath, '--storj-path');
|
|
165
|
+
const token = requireField(flags.storjToken, '--storj-token');
|
|
166
|
+
if (missing.length > 0) {
|
|
167
|
+
this.error(`Missing required Storj credentials flags: ${missing.join(', ')}`);
|
|
168
|
+
}
|
|
169
|
+
return { bucket, path, token };
|
|
170
|
+
}
|
|
171
|
+
case SourceType.S3: {
|
|
172
|
+
const bucket = requireField(flags.s3Bucket, '--s3-bucket');
|
|
173
|
+
const path = requireField(flags.s3Path, '--s3-path');
|
|
174
|
+
const accessKey = requireField(flags.s3AccessKey, '--s3-access-key');
|
|
175
|
+
const secretKey = requireField(flags.s3SecretKey, '--s3-secret-key');
|
|
176
|
+
const region = requireField(flags.s3Region, '--s3-region');
|
|
177
|
+
const endpoint = optionalField(flags.s3Endpoint);
|
|
178
|
+
if (missing.length > 0) {
|
|
179
|
+
this.error(`Missing required S3 credentials flags: ${missing.join(', ')}`);
|
|
180
|
+
}
|
|
181
|
+
return { bucket, path, accessKey, secretKey, region, endpoint };
|
|
182
|
+
}
|
|
183
|
+
case SourceType.DOCKERHUB: {
|
|
184
|
+
const image = requireField(flags.dockerImage, '--docker-image');
|
|
185
|
+
const registry = optionalField(flags.dockerRegistry);
|
|
186
|
+
const username = optionalField(flags.dockerUsername);
|
|
187
|
+
const password = optionalField(flags.dockerPassword);
|
|
188
|
+
if (missing.length > 0) {
|
|
189
|
+
this.error(`Missing required DockerHub credentials flags: ${missing.join(', ')}`);
|
|
190
|
+
}
|
|
191
|
+
return { image, registry, username, password };
|
|
192
|
+
}
|
|
193
|
+
case SourceType.GITHUB: {
|
|
194
|
+
const repo = requireField(flags.githubRepo, '--github-repo');
|
|
195
|
+
const token = optionalField(flags.githubToken);
|
|
196
|
+
const branch = optionalField(flags.githubBranch);
|
|
197
|
+
if (missing.length > 0) {
|
|
198
|
+
this.error(`Missing required GitHub credentials flags: ${missing.join(', ')}`);
|
|
199
|
+
}
|
|
200
|
+
return { repo, token, branch };
|
|
201
|
+
}
|
|
202
|
+
case SourceType.GOOGLE_DOCS: {
|
|
203
|
+
const documentId = requireField(flags.gdocsDocumentId, '--gdocs-document-id');
|
|
204
|
+
const apiKey = optionalField(flags.gdocsApiKey);
|
|
205
|
+
const serviceAccountJson = optionalField(flags.gdocsServiceAccountJson);
|
|
206
|
+
if (missing.length > 0) {
|
|
207
|
+
this.error(`Missing required Google Docs credentials flags: ${missing.join(', ')}`);
|
|
208
|
+
}
|
|
209
|
+
return { documentId, apiKey, serviceAccountJson };
|
|
210
|
+
}
|
|
211
|
+
default: {
|
|
212
|
+
const exhaustive = sourceType;
|
|
213
|
+
throw new Error(`Unsupported source type: ${exhaustive}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Asset, AssetType, SourceType } from '../../services/asset.service.js';
|
|
2
|
+
import { BaseAssetCommand } from './base.js';
|
|
3
|
+
export default class AssetCreate extends BaseAssetCommand<typeof AssetCreate> {
|
|
4
|
+
static authenticate: boolean;
|
|
5
|
+
static description: string;
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static flags: {
|
|
8
|
+
fromFile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
type: import("@oclif/core/interfaces").OptionFlag<AssetType | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
sourceType: import("@oclif/core/interfaces").OptionFlag<SourceType | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
hash: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
size: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
storjBucket: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
storjPath: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
storjToken: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
s3Bucket: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
s3Path: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
s3AccessKey: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
s3SecretKey: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
|
+
s3Region: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
|
+
s3Endpoint: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
|
+
dockerImage: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
|
+
dockerRegistry: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
26
|
+
dockerUsername: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
27
|
+
dockerPassword: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
28
|
+
githubRepo: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
29
|
+
githubToken: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
30
|
+
githubBranch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
31
|
+
gdocsDocumentId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
32
|
+
gdocsApiKey: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
33
|
+
gdocsServiceAccountJson: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
34
|
+
};
|
|
35
|
+
run(): Promise<Asset>;
|
|
36
|
+
private loadAssetFromFile;
|
|
37
|
+
private promptAssetPayload;
|
|
38
|
+
private hasFlagPayload;
|
|
39
|
+
private buildPayloadFromFlags;
|
|
40
|
+
private parseEnumValue;
|
|
41
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { select, text } from '@clack/prompts';
|
|
4
|
+
import { Flags } from '@oclif/core';
|
|
5
|
+
import { AssetType, SourceType, } from '../../services/asset.service.js';
|
|
6
|
+
import { promptService } from '../../utils/prompt.service.js';
|
|
7
|
+
import { BaseAssetCommand } from './base.js';
|
|
8
|
+
const assetTypeOptions = Object.values(AssetType);
|
|
9
|
+
const sourceTypeOptions = Object.values(SourceType);
|
|
10
|
+
export default class AssetCreate extends BaseAssetCommand {
|
|
11
|
+
static authenticate = false;
|
|
12
|
+
static description = 'Create a new asset.';
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> assets create',
|
|
15
|
+
'<%= config.bin %> assets create --fromFile ./asset.json',
|
|
16
|
+
'<%= config.bin %> assets create --name "dataset-v1" --type data --sourceType s3 --s3Bucket b --s3Path p --s3AccessKey a --s3SecretKey s --s3Region us-east-1',
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
fromFile: Flags.string({
|
|
20
|
+
char: 'f',
|
|
21
|
+
description: 'Path to a JSON file that contains asset definition.',
|
|
22
|
+
}),
|
|
23
|
+
name: Flags.string({
|
|
24
|
+
char: 'n',
|
|
25
|
+
description: 'Asset name',
|
|
26
|
+
}),
|
|
27
|
+
type: Flags.option({
|
|
28
|
+
char: 't',
|
|
29
|
+
description: 'Asset type',
|
|
30
|
+
options: assetTypeOptions,
|
|
31
|
+
})(),
|
|
32
|
+
sourceType: Flags.option({
|
|
33
|
+
char: 's',
|
|
34
|
+
description: 'Source type',
|
|
35
|
+
options: sourceTypeOptions,
|
|
36
|
+
})(),
|
|
37
|
+
tag: Flags.string({
|
|
38
|
+
description: 'Asset tag',
|
|
39
|
+
}),
|
|
40
|
+
hash: Flags.string({
|
|
41
|
+
description: 'Asset hash',
|
|
42
|
+
}),
|
|
43
|
+
size: Flags.string({
|
|
44
|
+
description: 'Asset size in bytes',
|
|
45
|
+
}),
|
|
46
|
+
storjBucket: Flags.string({
|
|
47
|
+
description: 'Storj bucket',
|
|
48
|
+
}),
|
|
49
|
+
storjPath: Flags.string({
|
|
50
|
+
description: 'Storj path',
|
|
51
|
+
}),
|
|
52
|
+
storjToken: Flags.string({
|
|
53
|
+
description: 'Storj access token',
|
|
54
|
+
}),
|
|
55
|
+
s3Bucket: Flags.string({
|
|
56
|
+
description: 'S3 bucket',
|
|
57
|
+
}),
|
|
58
|
+
s3Path: Flags.string({
|
|
59
|
+
description: 'S3 path',
|
|
60
|
+
}),
|
|
61
|
+
s3AccessKey: Flags.string({
|
|
62
|
+
description: 'S3 access key',
|
|
63
|
+
}),
|
|
64
|
+
s3SecretKey: Flags.string({
|
|
65
|
+
description: 'S3 secret key',
|
|
66
|
+
}),
|
|
67
|
+
s3Region: Flags.string({
|
|
68
|
+
description: 'S3 region',
|
|
69
|
+
}),
|
|
70
|
+
s3Endpoint: Flags.string({
|
|
71
|
+
description: 'S3 endpoint',
|
|
72
|
+
}),
|
|
73
|
+
dockerImage: Flags.string({
|
|
74
|
+
description: 'Docker image',
|
|
75
|
+
}),
|
|
76
|
+
dockerRegistry: Flags.string({
|
|
77
|
+
description: 'Docker registry',
|
|
78
|
+
}),
|
|
79
|
+
dockerUsername: Flags.string({
|
|
80
|
+
description: 'Docker username',
|
|
81
|
+
}),
|
|
82
|
+
dockerPassword: Flags.string({
|
|
83
|
+
description: 'Docker password',
|
|
84
|
+
}),
|
|
85
|
+
githubRepo: Flags.string({
|
|
86
|
+
description: 'GitHub repo (org/repo)',
|
|
87
|
+
}),
|
|
88
|
+
githubToken: Flags.string({
|
|
89
|
+
description: 'GitHub token',
|
|
90
|
+
}),
|
|
91
|
+
githubBranch: Flags.string({
|
|
92
|
+
description: 'GitHub branch',
|
|
93
|
+
}),
|
|
94
|
+
gdocsDocumentId: Flags.string({
|
|
95
|
+
description: 'Google Docs document ID',
|
|
96
|
+
}),
|
|
97
|
+
gdocsApiKey: Flags.string({
|
|
98
|
+
description: 'Google Docs API key',
|
|
99
|
+
}),
|
|
100
|
+
gdocsServiceAccountJson: Flags.string({
|
|
101
|
+
description: 'Google Docs service account JSON',
|
|
102
|
+
}),
|
|
103
|
+
};
|
|
104
|
+
async run() {
|
|
105
|
+
const { flags } = await this.parse(AssetCreate);
|
|
106
|
+
const hasFlagPayload = this.hasFlagPayload(flags);
|
|
107
|
+
if (!flags.fromFile && !hasFlagPayload && !promptService.isInteractive) {
|
|
108
|
+
this.error('Provide --fromFile or required flags when running with --no-tty.');
|
|
109
|
+
}
|
|
110
|
+
let payload = flags.fromFile ? await this.loadAssetFromFile(flags.fromFile) : undefined;
|
|
111
|
+
if (hasFlagPayload) {
|
|
112
|
+
if (payload) {
|
|
113
|
+
const flagPayload = this.buildPayloadFromFlags(flags, {
|
|
114
|
+
allowPartial: true,
|
|
115
|
+
basePayload: payload,
|
|
116
|
+
});
|
|
117
|
+
payload = this.assetService.validateCreatePayload({ ...payload, ...flagPayload });
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
payload = this.buildPayloadFromFlags(flags);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (!payload) {
|
|
124
|
+
payload = await this.promptAssetPayload();
|
|
125
|
+
}
|
|
126
|
+
this.assertSourceTypeAllowed(payload.type, payload.sourceType);
|
|
127
|
+
try {
|
|
128
|
+
const asset = await this.assetService.createAsset(payload);
|
|
129
|
+
this.log(`Asset created: ${asset.id}`);
|
|
130
|
+
return asset;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async loadAssetFromFile(filePath) {
|
|
137
|
+
const absolutePath = path.isAbsolute(filePath)
|
|
138
|
+
? filePath
|
|
139
|
+
: path.join(this.currentDir, filePath);
|
|
140
|
+
try {
|
|
141
|
+
const fileContent = await fs.readFile(absolutePath, 'utf8');
|
|
142
|
+
const parsed = JSON.parse(fileContent);
|
|
143
|
+
return this.assetService.validateCreatePayload(parsed);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
this.error(`Failed to load asset definition: ${error instanceof Error ? error.message : String(error)}`);
|
|
147
|
+
throw error; // This will never execute, but helps TypeScript
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async promptAssetPayload() {
|
|
151
|
+
const name = this.ensurePromptValue(await text({
|
|
152
|
+
message: 'Asset name',
|
|
153
|
+
validate(value) {
|
|
154
|
+
return value?.trim() ? undefined : 'Name is required';
|
|
155
|
+
},
|
|
156
|
+
})).trim();
|
|
157
|
+
const type = this.ensurePromptValue(await select({
|
|
158
|
+
message: 'Select asset type',
|
|
159
|
+
options: [
|
|
160
|
+
{ label: 'Data', value: AssetType.DATA },
|
|
161
|
+
{ label: 'Image', value: AssetType.IMAGE },
|
|
162
|
+
{ label: 'Output', value: AssetType.OUTPUT },
|
|
163
|
+
],
|
|
164
|
+
}));
|
|
165
|
+
const allowedSourceTypes = this.getAllowedSourceTypes(type);
|
|
166
|
+
const sourceTypeOptions = [
|
|
167
|
+
{ label: 'StorJ', value: SourceType.STORJ },
|
|
168
|
+
{ label: 'S3', value: SourceType.S3 },
|
|
169
|
+
{ label: 'DockerHub', value: SourceType.DOCKERHUB },
|
|
170
|
+
{ label: 'GitHub', value: SourceType.GITHUB },
|
|
171
|
+
{ label: 'Google Docs', value: SourceType.GOOGLE_DOCS },
|
|
172
|
+
].filter((option) => allowedSourceTypes.includes(option.value));
|
|
173
|
+
const sourceType = this.ensurePromptValue(await select({
|
|
174
|
+
message: 'Select source type',
|
|
175
|
+
options: sourceTypeOptions,
|
|
176
|
+
}));
|
|
177
|
+
const credentials = await this.promptCredentials(sourceType);
|
|
178
|
+
const tag = await this.promptOptionalText('Tag (optional)');
|
|
179
|
+
const hash = this.ensurePromptValue(await text({
|
|
180
|
+
message: 'Hash (optional)',
|
|
181
|
+
})).trim();
|
|
182
|
+
const sizeRaw = this.ensurePromptValue(await text({
|
|
183
|
+
message: 'Size in bytes (optional)',
|
|
184
|
+
validate(value) {
|
|
185
|
+
if (!value?.trim()) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
const size = Number(value);
|
|
189
|
+
if (!Number.isFinite(size) || size < 0) {
|
|
190
|
+
return 'Size must be a non-negative number';
|
|
191
|
+
}
|
|
192
|
+
return undefined;
|
|
193
|
+
},
|
|
194
|
+
}));
|
|
195
|
+
const size = this.parseSizeInput(sizeRaw);
|
|
196
|
+
const payload = {
|
|
197
|
+
credentials,
|
|
198
|
+
name,
|
|
199
|
+
sourceType,
|
|
200
|
+
type,
|
|
201
|
+
...(tag ? { tag } : {}),
|
|
202
|
+
...(hash ? { hash } : {}),
|
|
203
|
+
...(size !== undefined ? { size } : {}),
|
|
204
|
+
};
|
|
205
|
+
return this.assetService.validateCreatePayload(payload);
|
|
206
|
+
}
|
|
207
|
+
hasFlagPayload(flags) {
|
|
208
|
+
return Boolean(flags.name ||
|
|
209
|
+
flags.type ||
|
|
210
|
+
flags.sourceType ||
|
|
211
|
+
flags.tag ||
|
|
212
|
+
flags.hash ||
|
|
213
|
+
flags.size ||
|
|
214
|
+
this.hasCredentialsFlags(flags));
|
|
215
|
+
}
|
|
216
|
+
buildPayloadFromFlags(flags, options) {
|
|
217
|
+
const allowPartial = options?.allowPartial ?? false;
|
|
218
|
+
const basePayload = options?.basePayload;
|
|
219
|
+
const missing = [];
|
|
220
|
+
const name = flags.name?.trim();
|
|
221
|
+
const rawType = flags.type?.trim();
|
|
222
|
+
const rawSourceType = flags.sourceType?.trim();
|
|
223
|
+
if (!allowPartial) {
|
|
224
|
+
if (!name) {
|
|
225
|
+
missing.push('--name');
|
|
226
|
+
}
|
|
227
|
+
if (!rawType) {
|
|
228
|
+
missing.push('--type');
|
|
229
|
+
}
|
|
230
|
+
if (!rawSourceType) {
|
|
231
|
+
missing.push('--sourceType');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (missing.length > 0) {
|
|
235
|
+
this.error(`Missing required flags for asset creation: ${missing.join(', ')}`);
|
|
236
|
+
}
|
|
237
|
+
const payload = {};
|
|
238
|
+
if (name) {
|
|
239
|
+
payload.name = name;
|
|
240
|
+
}
|
|
241
|
+
if (rawType) {
|
|
242
|
+
payload.type = this.parseEnumValue(rawType, assetTypeOptions, 'type');
|
|
243
|
+
}
|
|
244
|
+
if (rawSourceType) {
|
|
245
|
+
payload.sourceType = this.parseEnumValue(rawSourceType, sourceTypeOptions, 'sourceType');
|
|
246
|
+
}
|
|
247
|
+
const hasCredentialFlags = this.hasCredentialsFlags(flags);
|
|
248
|
+
if (hasCredentialFlags) {
|
|
249
|
+
const effectiveSourceType = payload.sourceType ?? basePayload?.sourceType;
|
|
250
|
+
this.assertDefined(effectiveSourceType, 'Missing --sourceType for credentials overrides.');
|
|
251
|
+
payload.credentials = this.buildCredentialsFromFlags(effectiveSourceType, flags);
|
|
252
|
+
}
|
|
253
|
+
else if (!allowPartial) {
|
|
254
|
+
this.assertDefined(payload.sourceType, 'Missing --sourceType');
|
|
255
|
+
payload.credentials = this.buildCredentialsFromFlags(payload.sourceType, flags);
|
|
256
|
+
}
|
|
257
|
+
const tag = flags.tag?.trim();
|
|
258
|
+
const hash = flags.hash?.trim();
|
|
259
|
+
const size = flags.size !== undefined ? this.parseSizeInput(flags.size) : undefined;
|
|
260
|
+
if (tag) {
|
|
261
|
+
payload.tag = tag;
|
|
262
|
+
}
|
|
263
|
+
if (hash) {
|
|
264
|
+
payload.hash = hash;
|
|
265
|
+
}
|
|
266
|
+
if (size !== undefined) {
|
|
267
|
+
payload.size = size;
|
|
268
|
+
}
|
|
269
|
+
return allowPartial ? payload : this.assetService.validateCreatePayload(payload);
|
|
270
|
+
}
|
|
271
|
+
parseEnumValue(value, allowed, field) {
|
|
272
|
+
const normalized = value.trim().toLowerCase();
|
|
273
|
+
const match = allowed.find((candidate) => candidate.toLowerCase() === normalized);
|
|
274
|
+
this.assertDefined(match, `Invalid ${field}: ${value}. Allowed: ${allowed.join(', ')}`);
|
|
275
|
+
return match;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Asset } from '../../services/asset.service.js';
|
|
2
|
+
import { BaseAssetCommand } from './base.js';
|
|
3
|
+
export default class AssetDelete extends BaseAssetCommand<typeof AssetDelete> {
|
|
4
|
+
static authenticate: boolean;
|
|
5
|
+
static description: string;
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static flags: {
|
|
8
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
id: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<Asset | undefined>;
|
|
13
|
+
private resolveAsset;
|
|
14
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { confirm, select } from '@clack/prompts';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
3
|
+
import { BaseAssetCommand } from './base.js';
|
|
4
|
+
export default class AssetDelete extends BaseAssetCommand {
|
|
5
|
+
static authenticate = false;
|
|
6
|
+
static description = 'Delete an asset.';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> assets delete --id 11111111-1111-1111-1111-111111111111',
|
|
9
|
+
'<%= config.bin %> assets delete --name "dataset-v1" --force',
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
force: Flags.boolean({
|
|
13
|
+
char: 'f',
|
|
14
|
+
default: false,
|
|
15
|
+
description: 'Delete without confirmation',
|
|
16
|
+
}),
|
|
17
|
+
id: Flags.string({
|
|
18
|
+
char: 'i',
|
|
19
|
+
description: 'Asset ID to delete',
|
|
20
|
+
}),
|
|
21
|
+
name: Flags.string({
|
|
22
|
+
char: 'n',
|
|
23
|
+
description: 'Asset name to delete',
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(AssetDelete);
|
|
28
|
+
const asset = await this.resolveAsset(flags.id, flags.name);
|
|
29
|
+
this.assertDefined(asset, 'Asset not found');
|
|
30
|
+
if (!flags.force) {
|
|
31
|
+
const confirmed = this.ensurePromptValue(await confirm({
|
|
32
|
+
initialValue: false,
|
|
33
|
+
message: `Delete asset "${asset.name}" (${asset.id})?`,
|
|
34
|
+
}));
|
|
35
|
+
if (!confirmed) {
|
|
36
|
+
this.log('Deletion cancelled');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
await this.assetService.deleteAsset(asset.id);
|
|
42
|
+
this.log(`Asset deleted: ${asset.id}`);
|
|
43
|
+
return asset;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async resolveAsset(id, name) {
|
|
50
|
+
if (id) {
|
|
51
|
+
return this.assetService.findAssetById(id);
|
|
52
|
+
}
|
|
53
|
+
if (name) {
|
|
54
|
+
return this.assetService.findAssetByName(name);
|
|
55
|
+
}
|
|
56
|
+
const assets = await this.assetService.listAssets();
|
|
57
|
+
if (assets.length === 0) {
|
|
58
|
+
this.error('No assets available');
|
|
59
|
+
}
|
|
60
|
+
const selection = this.ensurePromptValue(await select({
|
|
61
|
+
message: 'Select asset to delete',
|
|
62
|
+
options: assets.map((asset) => ({
|
|
63
|
+
label: `${asset.name} (${asset.id})`,
|
|
64
|
+
value: asset.id,
|
|
65
|
+
})),
|
|
66
|
+
}));
|
|
67
|
+
return assets.find((asset) => asset.id === selection);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Asset } from '../../services/asset.service.js';
|
|
2
|
+
import { BaseAssetCommand } from './base.js';
|
|
3
|
+
export default class AssetGet extends BaseAssetCommand<typeof AssetGet> {
|
|
4
|
+
static authenticate: boolean;
|
|
5
|
+
static description: string;
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static flags: {
|
|
8
|
+
id: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<Asset>;
|
|
12
|
+
private resolveAsset;
|
|
13
|
+
private printAssetInfo;
|
|
14
|
+
}
|