@signageos/cli 2.8.0 → 3.0.0-rc.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 +0 -6
- package/dist/Applet/Build/appletBuildCommand.js +2 -0
- package/dist/Applet/Generate/appletGenerateCommand.js +51 -59
- package/dist/Applet/Start/appletStartCommand.js +3 -1
- package/dist/Applet/Test/Upload/appletTestRunCommand.js +11 -6
- package/dist/Applet/Test/Upload/appletTestUploadCommand.js +7 -4
- package/dist/Applet/Upload/appletUploadCommand.d.ts +15 -1
- package/dist/Applet/Upload/appletUploadCommand.js +82 -13
- package/dist/Applet/Upload/appletUploadCommandHelper.js +3 -3
- package/dist/Applet/Upload/appletUploadFacade.js +124 -52
- package/dist/Applet/Upload/appletUploadFacadeHelper.d.ts +1 -1
- package/dist/Applet/Upload/appletUploadFacadeHelper.js +4 -3
- package/dist/Applet/appletErrors.d.ts +3 -0
- package/dist/Applet/appletErrors.js +8 -1
- package/dist/Applet/appletFacade.js +13 -2
- package/dist/Applet/appletValidation.d.ts +17 -0
- package/dist/Applet/appletValidation.js +62 -0
- package/dist/Auth/loginCommand.js +30 -4
- package/dist/Command/commandProcessor.js +5 -0
- package/dist/Command/globalArgs.d.ts +21 -0
- package/dist/Command/globalArgs.js +30 -0
- package/dist/CommandLine/progressBarFactory.js +51 -10
- package/dist/CustomScript/Upload/customScriptUploadCommand.js +2 -2
- package/dist/Device/Content/setContentCommand.js +2 -2
- package/dist/Device/deviceFacade.js +10 -1
- package/dist/Emulator/emulatorFacade.js +9 -2
- package/dist/Organization/organizationFacade.d.ts +1 -1
- package/dist/Organization/organizationFacade.js +50 -13
- package/dist/Plugin/Upload/pluginUploadCommand.js +2 -1
- package/dist/RunControl/runControlHelper.d.ts +7 -1
- package/dist/RunControl/runControlHelper.js +19 -1
- package/dist/Runner/Upload/runnerUploadCommand.js +2 -1
- package/dist/Timing/List/timingListCommand.js +1 -3
- package/dist/helper/paginationHelper.d.ts +7 -0
- package/dist/helper/paginationHelper.js +32 -0
- package/dist/helper.d.ts +18 -1
- package/dist/helper.js +31 -23
- package/dist/index.js +1 -2
- package/dist/parameters.d.ts +0 -1
- package/dist/parameters.js +3 -6
- package/docs/applet/generate/index.md +1 -1
- package/docs/applet/upload/index.md +15 -1
- package/package.json +12 -11
- package/.env +0 -3
- package/dist/Firmware/Upload/firmwareUploadCommand.d.ts +0 -90
- package/dist/Firmware/Upload/firmwareUploadCommand.js +0 -210
- package/dist/Firmware/Upload/firmwareUploadFacade.d.ts +0 -10
- package/dist/Firmware/Upload/firmwareUploadFacade.js +0 -90
- package/dist/Firmware/Upload/firmwareUploadHelper.d.ts +0 -1
- package/dist/Firmware/Upload/firmwareUploadHelper.js +0 -48
- package/dist/Firmware/firmwareCommand.d.ts +0 -76
- package/dist/Firmware/firmwareCommand.js +0 -42
|
@@ -60,9 +60,17 @@ function updateSingleFileApplet(parameters) {
|
|
|
60
60
|
return __awaiter(this, void 0, void 0, function* () {
|
|
61
61
|
const { restApi, applet } = parameters;
|
|
62
62
|
const appletBinary = fs.createReadStream(applet.binaryFilePath, { encoding: 'utf8' });
|
|
63
|
-
yield restApi.applet.version
|
|
63
|
+
yield restApi.applet.version
|
|
64
|
+
.update(applet.uid, applet.version, {
|
|
64
65
|
binary: appletBinary,
|
|
65
66
|
frontAppletVersion: applet.frontAppletVersion,
|
|
67
|
+
})
|
|
68
|
+
.catch((error) => {
|
|
69
|
+
if (error instanceof Error) {
|
|
70
|
+
throw new Error(`Failed to update applet version "${applet.version}" with binary file "${applet.binaryFilePath}": ${error.message}\n` +
|
|
71
|
+
`Please check that the file exists, is readable, and that you have permissions to update the applet.`);
|
|
72
|
+
}
|
|
73
|
+
throw error;
|
|
66
74
|
});
|
|
67
75
|
});
|
|
68
76
|
}
|
|
@@ -91,40 +99,44 @@ const updateMultiFileApplet = (parameters) => __awaiter(void 0, void 0, void 0,
|
|
|
91
99
|
}
|
|
92
100
|
else {
|
|
93
101
|
changedFilesCounter++;
|
|
94
|
-
(0, log_1.log)('info', chalk_1.default.yellow(` Uploading ${fileAbsolutePath}`));
|
|
95
102
|
}
|
|
96
103
|
if (progressBar) {
|
|
97
|
-
progressBar.init({ size: fileSize, name:
|
|
104
|
+
progressBar.init({ size: fileSize, name: fileRelativePosixPath });
|
|
98
105
|
}
|
|
99
106
|
const fileStream = fs.createReadStream(fileAbsolutePath);
|
|
100
107
|
fileStream.pause();
|
|
101
108
|
fileStream.on('data', (chunk) => {
|
|
102
109
|
if (progressBar) {
|
|
103
|
-
progressBar.update({ add: chunk.length });
|
|
110
|
+
progressBar.update({ add: chunk.length, name: fileRelativePosixPath });
|
|
104
111
|
}
|
|
105
112
|
});
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
catch (error) {
|
|
113
|
+
// update file is just alias to create file (both are idempotent)
|
|
114
|
+
yield restApi.applet.version.file
|
|
115
|
+
.update(applet.uid, applet.version, fileRelativePosixPath, {
|
|
116
|
+
content: fileStream,
|
|
117
|
+
hash: fileHash,
|
|
118
|
+
size: fileSize,
|
|
119
|
+
type: fileType,
|
|
120
|
+
}, { build: false })
|
|
121
|
+
.catch((error) => {
|
|
116
122
|
if (fileSize === 0) {
|
|
117
123
|
throw new Error(`Empty files are temporarily disallowed ${fileAbsolutePath}`);
|
|
118
124
|
}
|
|
125
|
+
// Enhance error message with more context
|
|
126
|
+
if (error instanceof Error) {
|
|
127
|
+
const enhancedError = new Error(`Failed to upload file "${fileAbsolutePath}": ${error.message}\n` + `File details: Size=${fileSize} bytes, Type=${fileType}`);
|
|
128
|
+
// Preserve the original stack trace if available
|
|
129
|
+
if (error.stack) {
|
|
130
|
+
enhancedError.stack = error.stack;
|
|
131
|
+
}
|
|
132
|
+
throw enhancedError;
|
|
133
|
+
}
|
|
119
134
|
throw error;
|
|
120
|
-
}
|
|
135
|
+
});
|
|
121
136
|
}
|
|
122
137
|
for (const fileRelativePath in currentAppletFiles) {
|
|
123
138
|
if (Object.prototype.hasOwnProperty.call(currentAppletFiles, fileRelativePath)) {
|
|
124
|
-
|
|
125
|
-
yield restApi.applet.version.file.remove(applet.uid, applet.version, fileRelativePath, { build: false });
|
|
126
|
-
}
|
|
127
|
-
catch (error) {
|
|
139
|
+
yield restApi.applet.version.file.remove(applet.uid, applet.version, fileRelativePath, { build: false }).catch((error) => {
|
|
128
140
|
if (error instanceof NotFoundError_1.default) {
|
|
129
141
|
/*
|
|
130
142
|
* This means that the file we are trying to remove somehow already got removed.
|
|
@@ -134,9 +146,17 @@ const updateMultiFileApplet = (parameters) => __awaiter(void 0, void 0, void 0,
|
|
|
134
146
|
Debug(`remove old file ${fileRelativePath} failed`);
|
|
135
147
|
}
|
|
136
148
|
else {
|
|
149
|
+
// Add more context to file removal errors
|
|
150
|
+
if (error instanceof Error) {
|
|
151
|
+
const enhancedError = new Error(`Failed to remove obsolete file "${fileRelativePath}": ${error.message}`);
|
|
152
|
+
if (error.stack) {
|
|
153
|
+
enhancedError.stack = error.stack;
|
|
154
|
+
}
|
|
155
|
+
throw enhancedError;
|
|
156
|
+
}
|
|
137
157
|
throw error;
|
|
138
158
|
}
|
|
139
|
-
}
|
|
159
|
+
});
|
|
140
160
|
changedFilesCounter++;
|
|
141
161
|
}
|
|
142
162
|
}
|
|
@@ -144,8 +164,16 @@ const updateMultiFileApplet = (parameters) => __awaiter(void 0, void 0, void 0,
|
|
|
144
164
|
const appletEntryFilePosixPath = path.posix.normalize(applet.entryFilePath.replace(/\\/g, '/'));
|
|
145
165
|
if (changedFilesCounter > 0 || appletVersion.entryFile !== appletEntryFilePosixPath) {
|
|
146
166
|
// The update applet version has to be the last after upload all files to trigger applet version build
|
|
147
|
-
yield restApi.applet.version
|
|
167
|
+
yield restApi.applet.version
|
|
168
|
+
.update(applet.uid, applet.version, {
|
|
148
169
|
entryFile: appletEntryFilePosixPath,
|
|
170
|
+
})
|
|
171
|
+
.catch((error) => {
|
|
172
|
+
if (error instanceof Error) {
|
|
173
|
+
throw new Error(`Failed to update applet version with entry file "${appletEntryFilePosixPath}": ${error.message}\n` +
|
|
174
|
+
`This usually happens when the entry file is missing, has errors, or the applet version cannot be built.`);
|
|
175
|
+
}
|
|
176
|
+
throw error;
|
|
149
177
|
});
|
|
150
178
|
}
|
|
151
179
|
if (progressBar) {
|
|
@@ -159,10 +187,18 @@ exports.updateMultiFileApplet = updateMultiFileApplet;
|
|
|
159
187
|
const createSingleFileApplet = (parameters) => __awaiter(void 0, void 0, void 0, function* () {
|
|
160
188
|
const { restApi, applet } = parameters;
|
|
161
189
|
const appletBinary = fs.createReadStream(applet.binaryFilePath, { encoding: 'utf8' });
|
|
162
|
-
yield restApi.applet.version
|
|
190
|
+
yield restApi.applet.version
|
|
191
|
+
.create(applet.uid, {
|
|
163
192
|
binary: appletBinary,
|
|
164
193
|
version: applet.version,
|
|
165
194
|
frontAppletVersion: applet.frontAppletVersion,
|
|
195
|
+
})
|
|
196
|
+
.catch((error) => {
|
|
197
|
+
if (error instanceof Error) {
|
|
198
|
+
throw new Error(`Failed to create applet version "${applet.version}" with binary file "${applet.binaryFilePath}": ${error.message}\n` +
|
|
199
|
+
`Please check that the file exists, is readable, and that you have permissions to create applet versions.`);
|
|
200
|
+
}
|
|
201
|
+
throw error;
|
|
166
202
|
});
|
|
167
203
|
});
|
|
168
204
|
exports.createSingleFileApplet = createSingleFileApplet;
|
|
@@ -170,39 +206,71 @@ const createMultiFileFileApplet = (parameters) => __awaiter(void 0, void 0, void
|
|
|
170
206
|
const { restApi, applet, progressBar } = parameters;
|
|
171
207
|
const appletEntryFilePosixPath = path.posix.normalize(applet.entryFilePath.replace(/\\/g, '/'));
|
|
172
208
|
try {
|
|
173
|
-
yield restApi.applet.version
|
|
209
|
+
yield restApi.applet.version
|
|
210
|
+
.create(applet.uid, {
|
|
174
211
|
version: applet.version,
|
|
175
212
|
entryFile: appletEntryFilePosixPath,
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const fileSize = (yield fs.stat(fileAbsolutePath)).size;
|
|
182
|
-
if (fileSize === 0) {
|
|
183
|
-
throw new Error(`Empty files are temporarily disallowed ${fileAbsolutePath}`);
|
|
184
|
-
}
|
|
185
|
-
if (progressBar) {
|
|
186
|
-
progressBar.init({ size: fileSize, name: fileRelativePath });
|
|
213
|
+
})
|
|
214
|
+
.catch((error) => {
|
|
215
|
+
if (error instanceof Error) {
|
|
216
|
+
throw new Error(`Failed to create applet version "${applet.version}": ${error.message}\n` +
|
|
217
|
+
`Please check that you have permissions to create applet versions and that the version is valid.`);
|
|
187
218
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
219
|
+
throw error;
|
|
220
|
+
});
|
|
221
|
+
// Process files sequentially to avoid concurrent progress bar issues
|
|
222
|
+
for (const fileAbsolutePath of applet.files) {
|
|
223
|
+
try {
|
|
224
|
+
const fileRelativePath = (0, appletUploadFacadeHelper_1.getAppletFileRelativePath)(fileAbsolutePath, applet.directoryPath);
|
|
225
|
+
const fileHash = yield (0, fileSystem_1.getFileMD5Checksum)(fileAbsolutePath);
|
|
226
|
+
const fileType = yield (0, fileSystem_1.getFileType)(fileAbsolutePath);
|
|
227
|
+
const fileSize = (yield fs.stat(fileAbsolutePath)).size;
|
|
228
|
+
if (fileSize === 0) {
|
|
229
|
+
(0, log_1.log)('info', chalk_1.default.yellow(`Skipping empty file ${fileAbsolutePath}`));
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const filePosixPath = path.posix.normalize(fileRelativePath.replace(/\\/g, '/'));
|
|
191
233
|
if (progressBar) {
|
|
192
|
-
progressBar.
|
|
234
|
+
progressBar.init({ size: fileSize, name: filePosixPath });
|
|
193
235
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
236
|
+
const fileStream = fs.createReadStream(fileAbsolutePath);
|
|
237
|
+
fileStream.pause();
|
|
238
|
+
fileStream.on('data', (chunk) => {
|
|
239
|
+
if (progressBar) {
|
|
240
|
+
progressBar.update({ add: chunk.length, name: filePosixPath });
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
yield restApi.applet.version.file
|
|
244
|
+
.create(applet.uid, applet.version, {
|
|
245
|
+
name: path.basename(filePosixPath),
|
|
246
|
+
path: filePosixPath,
|
|
247
|
+
type: fileType,
|
|
248
|
+
hash: fileHash,
|
|
249
|
+
content: fileStream,
|
|
250
|
+
size: fileSize,
|
|
251
|
+
}, { build: false })
|
|
252
|
+
.catch((error) => {
|
|
253
|
+
if (error instanceof Error) {
|
|
254
|
+
throw new Error(`Failed to upload file "${fileAbsolutePath}": ${error.message}\n` +
|
|
255
|
+
`File details: Size=${fileSize} bytes, Type=${fileType}`);
|
|
256
|
+
}
|
|
257
|
+
throw error;
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
if (error instanceof Error) {
|
|
262
|
+
if (fileAbsolutePath) {
|
|
263
|
+
const enhancedError = new Error(`Failed to upload file "${fileAbsolutePath}": ${error.message}`);
|
|
264
|
+
// Preserve the original stack trace if available
|
|
265
|
+
if (error.stack) {
|
|
266
|
+
enhancedError.stack = error.stack;
|
|
267
|
+
}
|
|
268
|
+
throw enhancedError;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
206
274
|
}
|
|
207
275
|
finally {
|
|
208
276
|
if (progressBar) {
|
|
@@ -210,8 +278,12 @@ const createMultiFileFileApplet = (parameters) => __awaiter(void 0, void 0, void
|
|
|
210
278
|
}
|
|
211
279
|
}
|
|
212
280
|
// The extra update applet version which has to be after upload all files to trigger applet version build
|
|
213
|
-
yield restApi.applet.version.update(applet.uid, applet.version, {
|
|
214
|
-
|
|
281
|
+
yield restApi.applet.version.update(applet.uid, applet.version, { entryFile: appletEntryFilePosixPath }).catch((error) => {
|
|
282
|
+
if (error instanceof Error) {
|
|
283
|
+
throw new Error(`Failed to finalize applet version with entry file "${appletEntryFilePosixPath}": ${error.message}\n` +
|
|
284
|
+
`This usually happens when there were issues with one or more uploaded files or when building the applet.`);
|
|
285
|
+
}
|
|
286
|
+
throw error;
|
|
215
287
|
});
|
|
216
288
|
});
|
|
217
289
|
exports.createMultiFileFileApplet = createMultiFileFileApplet;
|
|
@@ -2,5 +2,5 @@ import RestApi from '@signageos/sdk/dist/RestApi/RestApi';
|
|
|
2
2
|
import IAppletVersionFile from '@signageos/sdk/dist/RestApi/Applet/Version/File/IAppletVersionFile';
|
|
3
3
|
export declare function getAppletFileRelativePath(fileAbsolutePath: string, directoryAbsolutePath: string): string;
|
|
4
4
|
export declare function getAppletFilesDictionary(restApi: RestApi, appletUid: string, appletVersion: string): Promise<{
|
|
5
|
-
[
|
|
5
|
+
[path: string]: IAppletVersionFile;
|
|
6
6
|
}>;
|
|
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.getAppletFileRelativePath = getAppletFileRelativePath;
|
|
46
46
|
exports.getAppletFilesDictionary = getAppletFilesDictionary;
|
|
47
47
|
const path = __importStar(require("path"));
|
|
48
|
+
const paginationHelper_1 = require("../../helper/paginationHelper");
|
|
48
49
|
function getAppletFileRelativePath(fileAbsolutePath, directoryAbsolutePath) {
|
|
49
50
|
const directoryAbsolutePathNormalized = path.normalize(directoryAbsolutePath);
|
|
50
51
|
const fileAbsolutePathNormalized = path.normalize(fileAbsolutePath);
|
|
@@ -66,10 +67,10 @@ function getAppletFileRelativePath(fileAbsolutePath, directoryAbsolutePath) {
|
|
|
66
67
|
function getAppletFilesDictionary(restApi, appletUid, appletVersion) {
|
|
67
68
|
return __awaiter(this, void 0, void 0, function* () {
|
|
68
69
|
const filesDictionary = {};
|
|
69
|
-
const
|
|
70
|
-
|
|
70
|
+
const files = yield (0, paginationHelper_1.getAllPages)(yield restApi.applet.version.file.list(appletUid, appletVersion));
|
|
71
|
+
for (const file of files) {
|
|
71
72
|
filesDictionary[file.path] = file;
|
|
72
|
-
}
|
|
73
|
+
}
|
|
73
74
|
return filesDictionary;
|
|
74
75
|
});
|
|
75
76
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AppletDoesNotExistError = void 0;
|
|
3
|
+
exports.AppletSelectionCancelledError = exports.AppletDoesNotExistError = void 0;
|
|
4
4
|
class AppletDoesNotExistError extends Error {
|
|
5
5
|
constructor(message) {
|
|
6
6
|
super(message);
|
|
@@ -8,3 +8,10 @@ class AppletDoesNotExistError extends Error {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
exports.AppletDoesNotExistError = AppletDoesNotExistError;
|
|
11
|
+
class AppletSelectionCancelledError extends Error {
|
|
12
|
+
constructor(message = 'Applet selection was cancelled') {
|
|
13
|
+
super(message);
|
|
14
|
+
Object.setPrototypeOf(this, AppletSelectionCancelledError.prototype);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.AppletSelectionCancelledError = AppletSelectionCancelledError;
|
|
@@ -56,6 +56,8 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
56
56
|
const parameters_1 = require("../parameters");
|
|
57
57
|
const packageConfig_1 = require("@signageos/sdk/dist/FileSystem/packageConfig");
|
|
58
58
|
const appletErrors_1 = require("./appletErrors");
|
|
59
|
+
const helper_1 = require("../helper");
|
|
60
|
+
const paginationHelper_1 = require("../helper/paginationHelper");
|
|
59
61
|
exports.APPLET_UID_OPTION = { name: 'applet-uid', type: String, description: 'Applet UID' };
|
|
60
62
|
function getApplet(directoryPath) {
|
|
61
63
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -96,7 +98,8 @@ function getAppletUid(restApi_1, options_1) {
|
|
|
96
98
|
const currentApplet = yield getApplet(currentDirectory);
|
|
97
99
|
let appletUid = options['applet-uid'] || currentApplet.uid;
|
|
98
100
|
if (!appletUid) {
|
|
99
|
-
|
|
101
|
+
// Fetch all pages to ensure we don't miss any applets
|
|
102
|
+
const applets = yield (0, paginationHelper_1.getAllPages)(yield restApi.applet.list());
|
|
100
103
|
const candidatesOfApplets = applets.filter((applet) => applet.name === currentApplet.name);
|
|
101
104
|
if (candidatesOfApplets.length === 0) {
|
|
102
105
|
appletUid = undefined;
|
|
@@ -114,7 +117,11 @@ function getAppletUid(restApi_1, options_1) {
|
|
|
114
117
|
title: `${applet.name} (${applet.uid})`,
|
|
115
118
|
value: applet.uid,
|
|
116
119
|
})),
|
|
120
|
+
suggest: helper_1.autocompleteSuggest,
|
|
117
121
|
});
|
|
122
|
+
if (!response.appletUid) {
|
|
123
|
+
throw new appletErrors_1.AppletSelectionCancelledError();
|
|
124
|
+
}
|
|
118
125
|
appletUid = response.appletUid;
|
|
119
126
|
}
|
|
120
127
|
}
|
|
@@ -132,7 +139,7 @@ function getAppletVersionFromApi(restApi_1, appletUid_1) {
|
|
|
132
139
|
return __awaiter(this, arguments, void 0, function* (restApi, appletUid, skipConfirmation = false) {
|
|
133
140
|
var _a;
|
|
134
141
|
let appletVersion;
|
|
135
|
-
const appletVersions = yield restApi.applet.version.list(appletUid);
|
|
142
|
+
const appletVersions = yield (0, paginationHelper_1.getAllPages)(yield restApi.applet.version.list(appletUid));
|
|
136
143
|
if (appletVersions.length === 1 && ((_a = appletVersions[0]) === null || _a === void 0 ? void 0 : _a.version)) {
|
|
137
144
|
appletVersion = appletVersions[0].version;
|
|
138
145
|
}
|
|
@@ -148,7 +155,11 @@ function getAppletVersionFromApi(restApi_1, appletUid_1) {
|
|
|
148
155
|
title: applet.version,
|
|
149
156
|
value: applet.version,
|
|
150
157
|
})),
|
|
158
|
+
suggest: helper_1.autocompleteSuggest,
|
|
151
159
|
});
|
|
160
|
+
if (!response.appletVersion) {
|
|
161
|
+
throw new Error('Applet version selection was cancelled');
|
|
162
|
+
}
|
|
152
163
|
appletVersion = response.appletVersion;
|
|
153
164
|
}
|
|
154
165
|
return appletVersion;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if the given directory appears to be a valid signageOS applet directory.
|
|
3
|
+
*
|
|
4
|
+
* A valid applet has @signageos/front-applet as a dependency.
|
|
5
|
+
* This is the definitive marker that distinguishes applets from other projects.
|
|
6
|
+
*
|
|
7
|
+
* @param directoryPath - The directory path to validate
|
|
8
|
+
* @returns true if the directory appears to be a valid applet, false otherwise
|
|
9
|
+
*/
|
|
10
|
+
export declare function isValidAppletDirectory(directoryPath: string): Promise<boolean>;
|
|
11
|
+
/**
|
|
12
|
+
* Validates that the directory is a valid applet directory and throws an error if not.
|
|
13
|
+
*
|
|
14
|
+
* @param directoryPath - The directory path to validate
|
|
15
|
+
* @throws {Error} If the directory is not a valid applet directory
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateAppletDirectory(directoryPath: string): Promise<void>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.isValidAppletDirectory = isValidAppletDirectory;
|
|
16
|
+
exports.validateAppletDirectory = validateAppletDirectory;
|
|
17
|
+
const packageConfig_1 = require("@signageos/sdk/dist/FileSystem/packageConfig");
|
|
18
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
19
|
+
/**
|
|
20
|
+
* Checks if the given directory appears to be a valid signageOS applet directory.
|
|
21
|
+
*
|
|
22
|
+
* A valid applet has @signageos/front-applet as a dependency.
|
|
23
|
+
* This is the definitive marker that distinguishes applets from other projects.
|
|
24
|
+
*
|
|
25
|
+
* @param directoryPath - The directory path to validate
|
|
26
|
+
* @returns true if the directory appears to be a valid applet, false otherwise
|
|
27
|
+
*/
|
|
28
|
+
function isValidAppletDirectory(directoryPath) {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
try {
|
|
32
|
+
const packageJSONObject = yield (0, packageConfig_1.loadPackage)(directoryPath);
|
|
33
|
+
if (!packageJSONObject) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// Check for @signageos/front-applet dependency
|
|
37
|
+
// If package has it, it's an applet
|
|
38
|
+
const hasFrontApplet = !!(((_a = packageJSONObject.dependencies) === null || _a === void 0 ? void 0 : _a['@signageos/front-applet']) || ((_b = packageJSONObject.devDependencies) === null || _b === void 0 ? void 0 : _b['@signageos/front-applet']));
|
|
39
|
+
return hasFrontApplet;
|
|
40
|
+
}
|
|
41
|
+
catch (_c) {
|
|
42
|
+
// If we can't load package.json or it's malformed, it's not a valid applet
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Validates that the directory is a valid applet directory and throws an error if not.
|
|
49
|
+
*
|
|
50
|
+
* @param directoryPath - The directory path to validate
|
|
51
|
+
* @throws {Error} If the directory is not a valid applet directory
|
|
52
|
+
*/
|
|
53
|
+
function validateAppletDirectory(directoryPath) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
const isValid = yield isValidAppletDirectory(directoryPath);
|
|
56
|
+
if (!isValid) {
|
|
57
|
+
throw new Error(chalk_1.default.red(`\nThe directory does not appear to be a valid signageOS applet:\n`) +
|
|
58
|
+
chalk_1.default.white(` ${directoryPath}\n\n`) +
|
|
59
|
+
chalk_1.default.yellow(`A valid applet must have ${chalk_1.default.green('@signageos/front-applet')} as a dependency.\n\n`));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -50,12 +50,14 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
50
50
|
const prompts_1 = __importDefault(require("prompts"));
|
|
51
51
|
const debug_1 = __importDefault(require("debug"));
|
|
52
52
|
const os = __importStar(require("os"));
|
|
53
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
53
54
|
const apiVersions_1 = require("@signageos/sdk/dist/RestApi/apiVersions");
|
|
54
55
|
const log_1 = require("@signageos/sdk/dist/Console/log");
|
|
55
56
|
const sosControlHelper_1 = require("@signageos/sdk/dist/SosHelper/sosControlHelper");
|
|
56
57
|
const helper_1 = require("../helper");
|
|
57
58
|
const runControlHelper_1 = require("../RunControl/runControlHelper");
|
|
58
59
|
const parameters_1 = require("../parameters");
|
|
60
|
+
const globalArgs_1 = require("../Command/globalArgs");
|
|
59
61
|
const commandDefinition_1 = require("../Command/commandDefinition");
|
|
60
62
|
const Debug = (0, debug_1.default)('@signageos/cli:Auth:login');
|
|
61
63
|
const OPTION_LIST = [{ name: 'username', type: String, description: `Username or e-mail used for authentication` }];
|
|
@@ -100,11 +102,35 @@ exports.login = (0, commandDefinition_1.createCommandDefinition)({
|
|
|
100
102
|
run(options) {
|
|
101
103
|
return __awaiter(this, void 0, void 0, function* () {
|
|
102
104
|
let identification = options.username;
|
|
105
|
+
const profile = (0, globalArgs_1.getGlobalProfile)();
|
|
106
|
+
const configFilePath = (0, sosControlHelper_1.getConfigFilePath)();
|
|
107
|
+
// Detect a new (non-existent) named profile and prompt for the API URL
|
|
108
|
+
let promptedApiUrl;
|
|
109
|
+
if (profile) {
|
|
110
|
+
let profileExists = false;
|
|
111
|
+
if (yield fs_extra_1.default.pathExists(configFilePath)) {
|
|
112
|
+
const content = (yield fs_extra_1.default.readFile(configFilePath)).toString();
|
|
113
|
+
profileExists = content.includes(`[profile ${profile}]`);
|
|
114
|
+
}
|
|
115
|
+
if (!profileExists) {
|
|
116
|
+
(0, log_1.log)('info', `Profile "${profile}" does not exist in ${configFilePath}. Please enter the server API URL to create it.`);
|
|
117
|
+
const { inputApiUrl } = yield (0, prompts_1.default)({
|
|
118
|
+
type: 'text',
|
|
119
|
+
name: 'inputApiUrl',
|
|
120
|
+
message: 'Server API URL',
|
|
121
|
+
initial: 'https://api.signageos.io',
|
|
122
|
+
validate: (v) => (v.startsWith('http') ? true : 'Must be a valid URL starting with http'),
|
|
123
|
+
});
|
|
124
|
+
if (!inputApiUrl) {
|
|
125
|
+
throw new Error('API URL is required to log in.');
|
|
126
|
+
}
|
|
127
|
+
promptedApiUrl = inputApiUrl.replace(/\/+$/, '');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
103
130
|
const config = yield (0, runControlHelper_1.loadConfig)();
|
|
104
|
-
const apiUrl = (0, helper_1.getApiUrl)(config);
|
|
131
|
+
const apiUrl = promptedApiUrl !== null && promptedApiUrl !== void 0 ? promptedApiUrl : (0, helper_1.getApiUrl)(config);
|
|
105
132
|
// Extract domain from API URL to show in prompts
|
|
106
|
-
const
|
|
107
|
-
const hostToDisplay = apiUrlObj.hostname;
|
|
133
|
+
const hostToDisplay = new URL(apiUrl).hostname;
|
|
108
134
|
if (!identification) {
|
|
109
135
|
const response = yield (0, prompts_1.default)({
|
|
110
136
|
type: 'text',
|
|
@@ -126,7 +152,7 @@ exports.login = (0, commandDefinition_1.createCommandDefinition)({
|
|
|
126
152
|
password,
|
|
127
153
|
apiUrl }, authQueryParams));
|
|
128
154
|
yield (0, runControlHelper_1.saveConfig)({
|
|
129
|
-
apiUrl: apiUrl !== parameters_1.parameters.apiUrl ? apiUrl : undefined,
|
|
155
|
+
apiUrl: profile || apiUrl !== parameters_1.parameters.apiUrl ? apiUrl : undefined,
|
|
130
156
|
identification: tokenId,
|
|
131
157
|
apiSecurityToken,
|
|
132
158
|
});
|
|
@@ -20,6 +20,7 @@ const command_line_usage_1 = __importDefault(require("command-line-usage"));
|
|
|
20
20
|
const command_line_args_1 = __importDefault(require("command-line-args"));
|
|
21
21
|
const packageVersion_1 = require("../Cli/packageVersion");
|
|
22
22
|
const log_1 = require("@signageos/sdk/dist/Console/log");
|
|
23
|
+
const globalArgs_1 = require("./globalArgs");
|
|
23
24
|
const Debug = (0, debug_1.default)('@signageos/cli:Command:processor');
|
|
24
25
|
// Preprocess argv to detect and handle multi-character single-dash options
|
|
25
26
|
function preprocessArgv(argv) {
|
|
@@ -40,6 +41,10 @@ function preprocessArgv(argv) {
|
|
|
40
41
|
}
|
|
41
42
|
function processCommand(currentCommand_1) {
|
|
42
43
|
return __awaiter(this, arguments, void 0, function* (currentCommand, parentOptionList = [], commandIndex = 0) {
|
|
44
|
+
// Validate mutually exclusive global options once at the root invocation
|
|
45
|
+
if (commandIndex === 0) {
|
|
46
|
+
(0, globalArgs_1.validateProfileAndApiUrl)();
|
|
47
|
+
}
|
|
43
48
|
const nestedOptionList = [...parentOptionList, ...currentCommand.optionList];
|
|
44
49
|
// Preprocess argv to handle multi-character single-dash options that should be treated as unknown
|
|
45
50
|
// rather than being split into individual characters
|
|
@@ -31,3 +31,24 @@ export declare function getGlobalApiUrl(): string | undefined;
|
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
33
|
export declare function getGlobalProfile(): string;
|
|
34
|
+
/**
|
|
35
|
+
* Validate that --profile and --api-url are not used together.
|
|
36
|
+
* These options are mutually exclusive: --profile selects a fully pre-configured
|
|
37
|
+
* connection from ~/.sosrc (which already includes an api-url), while --api-url
|
|
38
|
+
* overrides the endpoint directly (implying the default profile).
|
|
39
|
+
*
|
|
40
|
+
* @throws {Error} When both --profile and --api-url are present on the command line
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```bash
|
|
44
|
+
* # Valid: only --profile
|
|
45
|
+
* sos --profile staging login
|
|
46
|
+
*
|
|
47
|
+
* # Valid: only --api-url
|
|
48
|
+
* sos --api-url https://custom-api.com login
|
|
49
|
+
*
|
|
50
|
+
* # Invalid: both together
|
|
51
|
+
* sos --profile staging --api-url https://custom-api.com login
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function validateProfileAndApiUrl(): void;
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getGlobalApiUrl = getGlobalApiUrl;
|
|
7
7
|
exports.getGlobalProfile = getGlobalProfile;
|
|
8
|
+
exports.validateProfileAndApiUrl = validateProfileAndApiUrl;
|
|
8
9
|
const command_line_args_1 = __importDefault(require("command-line-args"));
|
|
9
10
|
const generalCommand_1 = require("../generalCommand");
|
|
10
11
|
/**
|
|
@@ -46,3 +47,32 @@ function getGlobalProfile() {
|
|
|
46
47
|
const options = (0, command_line_args_1.default)([generalCommand_1.PROFILE_OPTION], { partial: true });
|
|
47
48
|
return options[generalCommand_1.PROFILE_OPTION.name];
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate that --profile and --api-url are not used together.
|
|
52
|
+
* These options are mutually exclusive: --profile selects a fully pre-configured
|
|
53
|
+
* connection from ~/.sosrc (which already includes an api-url), while --api-url
|
|
54
|
+
* overrides the endpoint directly (implying the default profile).
|
|
55
|
+
*
|
|
56
|
+
* @throws {Error} When both --profile and --api-url are present on the command line
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```bash
|
|
60
|
+
* # Valid: only --profile
|
|
61
|
+
* sos --profile staging login
|
|
62
|
+
*
|
|
63
|
+
* # Valid: only --api-url
|
|
64
|
+
* sos --api-url https://custom-api.com login
|
|
65
|
+
*
|
|
66
|
+
* # Invalid: both together
|
|
67
|
+
* sos --profile staging --api-url https://custom-api.com login
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
function validateProfileAndApiUrl() {
|
|
71
|
+
const profile = getGlobalProfile();
|
|
72
|
+
const apiUrl = getGlobalApiUrl();
|
|
73
|
+
if (profile !== undefined && apiUrl !== undefined) {
|
|
74
|
+
throw new Error(`Options --profile and --api-url are mutually exclusive. ` +
|
|
75
|
+
`--profile selects a pre-configured connection from ~/.sosrc (which already includes an API URL), ` +
|
|
76
|
+
`while --api-url overrides the API endpoint directly. Use one or the other, not both.`);
|
|
77
|
+
}
|
|
78
|
+
}
|