@signageos/cli 2.7.1 → 2.9.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 +12 -6
- package/dist/Applet/Build/appletBuildCommand.js +2 -0
- package/dist/Applet/Generate/appletGenerateCommand.js +5 -1
- package/dist/Applet/Start/appletStartCommand.js +3 -1
- package/dist/Applet/Test/Upload/appletTestRunCommand.js +9 -5
- package/dist/Applet/Test/Upload/appletTestUploadCommand.js +5 -3
- 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 +3 -3
- package/dist/Applet/appletErrors.d.ts +3 -0
- package/dist/Applet/appletErrors.js +8 -1
- package/dist/Applet/appletFacade.js +9 -0
- 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 +8 -0
- package/dist/Emulator/emulatorFacade.js +7 -2
- package/dist/Emulator/emulatorFactory.js +18 -0
- package/dist/Firmware/Upload/firmwareUploadCommand.js +7 -10
- package/dist/Firmware/Upload/firmwareUploadFacade.js +15 -6
- 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.d.ts +19 -0
- package/dist/helper.js +45 -3
- package/dist/parameters.d.ts +0 -1
- package/dist/parameters.js +3 -6
- package/docs/applet/upload/index.md +15 -1
- package/docs/index.md +13 -0
- package/package.json +11 -7
|
@@ -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
|
}>;
|
|
@@ -66,10 +66,10 @@ function getAppletFileRelativePath(fileAbsolutePath, directoryAbsolutePath) {
|
|
|
66
66
|
function getAppletFilesDictionary(restApi, appletUid, appletVersion) {
|
|
67
67
|
return __awaiter(this, void 0, void 0, function* () {
|
|
68
68
|
const filesDictionary = {};
|
|
69
|
-
const
|
|
70
|
-
|
|
69
|
+
const files = yield restApi.applet.version.file.list(appletUid, appletVersion);
|
|
70
|
+
for (const file of files) {
|
|
71
71
|
filesDictionary[file.path] = file;
|
|
72
|
-
}
|
|
72
|
+
}
|
|
73
73
|
return filesDictionary;
|
|
74
74
|
});
|
|
75
75
|
}
|
|
@@ -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,7 @@ 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");
|
|
59
60
|
exports.APPLET_UID_OPTION = { name: 'applet-uid', type: String, description: 'Applet UID' };
|
|
60
61
|
function getApplet(directoryPath) {
|
|
61
62
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -114,7 +115,11 @@ function getAppletUid(restApi_1, options_1) {
|
|
|
114
115
|
title: `${applet.name} (${applet.uid})`,
|
|
115
116
|
value: applet.uid,
|
|
116
117
|
})),
|
|
118
|
+
suggest: helper_1.autocompleteSuggest,
|
|
117
119
|
});
|
|
120
|
+
if (!response.appletUid) {
|
|
121
|
+
throw new appletErrors_1.AppletSelectionCancelledError();
|
|
122
|
+
}
|
|
118
123
|
appletUid = response.appletUid;
|
|
119
124
|
}
|
|
120
125
|
}
|
|
@@ -148,7 +153,11 @@ function getAppletVersionFromApi(restApi_1, appletUid_1) {
|
|
|
148
153
|
title: applet.version,
|
|
149
154
|
value: applet.version,
|
|
150
155
|
})),
|
|
156
|
+
suggest: helper_1.autocompleteSuggest,
|
|
151
157
|
});
|
|
158
|
+
if (!response.appletVersion) {
|
|
159
|
+
throw new Error('Applet version selection was cancelled');
|
|
160
|
+
}
|
|
152
161
|
appletVersion = response.appletVersion;
|
|
153
162
|
}
|
|
154
163
|
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
|
+
}
|
|
@@ -36,23 +36,64 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.createProgressBar = createProgressBar;
|
|
37
37
|
const cliProgress = __importStar(require("cli-progress"));
|
|
38
38
|
function createProgressBar() {
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
// Create a MultiBar instance to manage multiple progress bars
|
|
40
|
+
const multiBar = new cliProgress.MultiBar({
|
|
41
|
+
format: '[{bar}] {name} {percentage}% | ETA: {eta}s | {valueKB}/{totalKB} kB',
|
|
42
|
+
forceRedraw: true, // Force redraw to ensure the display updates correctly
|
|
43
|
+
linewrap: false, // Disable line wrapping to keep the output clean
|
|
44
|
+
autopadding: true, // Automatically adjust padding for better alignment
|
|
45
|
+
etaBuffer: 5, // Reduced buffer for faster ETA calculation on small files
|
|
46
|
+
etaAsynchronousUpdate: false, // Disable asynchronous ETA updates to avoid NULL/Infinite states
|
|
41
47
|
}, cliProgress.Presets.rect);
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
// Keep track of all bars to clean up properly
|
|
49
|
+
const bars = new Map();
|
|
50
|
+
const barValues = new Map();
|
|
44
51
|
return {
|
|
45
52
|
init({ size, name }) {
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
// If a bar already exists with this name, stop and remove it
|
|
54
|
+
if (bars.has(name)) {
|
|
55
|
+
const existingBar = bars.get(name);
|
|
56
|
+
if (existingBar) {
|
|
57
|
+
existingBar.stop();
|
|
58
|
+
bars.delete(name);
|
|
59
|
+
barValues.delete(name);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Create a new bar for this file
|
|
63
|
+
const totalKB = Math.round((size / 1024) * 100) / 100; // Round to 2 decimal places
|
|
64
|
+
const bar = multiBar.create(size, 0, {
|
|
65
|
+
name,
|
|
66
|
+
totalKB: totalKB,
|
|
67
|
+
valueKB: 0,
|
|
68
|
+
});
|
|
69
|
+
bars.set(name, bar);
|
|
70
|
+
barValues.set(name, { current: 0, total: size });
|
|
48
71
|
},
|
|
49
72
|
update({ add, name }) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
if (name && bars.has(name)) {
|
|
74
|
+
const bar = bars.get(name);
|
|
75
|
+
const values = barValues.get(name);
|
|
76
|
+
if (bar && values) {
|
|
77
|
+
// Update tracked values
|
|
78
|
+
values.current += add;
|
|
79
|
+
// Calculate kB values
|
|
80
|
+
const valueKB = Math.round((values.current / 1024) * 100) / 100;
|
|
81
|
+
const totalKB = Math.round((values.total / 1024) * 100) / 100;
|
|
82
|
+
// Update bar with new values
|
|
83
|
+
bar.update(values.current, {
|
|
84
|
+
name,
|
|
85
|
+
totalKB: totalKB,
|
|
86
|
+
valueKB: valueKB,
|
|
87
|
+
});
|
|
88
|
+
// Force ETA update
|
|
89
|
+
bar.updateETA();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
53
92
|
},
|
|
54
93
|
end() {
|
|
55
|
-
|
|
94
|
+
multiBar.stop();
|
|
95
|
+
bars.clear();
|
|
96
|
+
barValues.clear();
|
|
56
97
|
},
|
|
57
98
|
};
|
|
58
99
|
}
|
|
@@ -68,11 +68,11 @@ exports.customScriptUpload = (0, commandDefinition_1.createCommandDefinition)({
|
|
|
68
68
|
run(options) {
|
|
69
69
|
return __awaiter(this, void 0, void 0, function* () {
|
|
70
70
|
const currentDirectory = process.cwd();
|
|
71
|
-
const
|
|
71
|
+
const skipConfirmation = options.yes;
|
|
72
|
+
const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options, skipConfirmation);
|
|
72
73
|
const organization = yield (0, organizationFacade_1.getOrganization)(organizationUid);
|
|
73
74
|
const restApi = yield (0, helper_1.createOrganizationRestApi)(organization);
|
|
74
75
|
const config = yield (0, customScriptFacade_1.getConfig)(currentDirectory);
|
|
75
|
-
const skipConfirmation = options.yes;
|
|
76
76
|
const customScriptVersion = yield (0, customScriptFacade_1.ensureCustomScriptVersion)(restApi, config, skipConfirmation);
|
|
77
77
|
for (const platform of Object.keys(config.platforms)) {
|
|
78
78
|
const platformConfig = config.platforms[platform];
|
|
@@ -66,10 +66,10 @@ exports.setContent = (0, commandDefinition_1.createCommandDefinition)({
|
|
|
66
66
|
commands: [],
|
|
67
67
|
run(options) {
|
|
68
68
|
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
-
const
|
|
69
|
+
const skipConfirmation = !!options.yes;
|
|
70
|
+
const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options, skipConfirmation);
|
|
70
71
|
const organization = yield (0, organizationFacade_1.getOrganization)(organizationUid);
|
|
71
72
|
const restApi = yield (0, helper_1.createOrganizationRestApi)(organization);
|
|
72
|
-
const skipConfirmation = !!options.yes;
|
|
73
73
|
const appletUid = yield (0, appletFacade_1.getAppletUid)(restApi, options, skipConfirmation);
|
|
74
74
|
const appletVersion = yield (0, appletFacade_1.getAppletVersionFromApi)(restApi, appletUid, skipConfirmation);
|
|
75
75
|
const deviceUid = yield (0, deviceFacade_1.getDeviceUid)(restApi, options, skipConfirmation);
|
|
@@ -55,7 +55,11 @@ function getDeviceUid(restApi_1, options_1) {
|
|
|
55
55
|
value: dev.uid,
|
|
56
56
|
});
|
|
57
57
|
}),
|
|
58
|
+
suggest: helper_1.autocompleteSuggest,
|
|
58
59
|
});
|
|
60
|
+
if (!response.deviceUid) {
|
|
61
|
+
throw new Error('Device selection was cancelled');
|
|
62
|
+
}
|
|
59
63
|
Debug('Device selected', response.deviceUid);
|
|
60
64
|
deviceUid = response.deviceUid;
|
|
61
65
|
}
|
|
@@ -78,7 +82,11 @@ function getActionType(options) {
|
|
|
78
82
|
title: item[1].name,
|
|
79
83
|
value: item[0],
|
|
80
84
|
})),
|
|
85
|
+
suggest: helper_1.autocompleteSuggest,
|
|
81
86
|
});
|
|
87
|
+
if (!response.type) {
|
|
88
|
+
throw new Error('Power action selection was cancelled');
|
|
89
|
+
}
|
|
82
90
|
action = response.type;
|
|
83
91
|
}
|
|
84
92
|
if (!action) {
|
|
@@ -37,7 +37,8 @@ const createRestApi = (config) => {
|
|
|
37
37
|
function getListOfEmulators(restApi, organizationUid) {
|
|
38
38
|
return __awaiter(this, void 0, void 0, function* () {
|
|
39
39
|
try {
|
|
40
|
-
|
|
40
|
+
const emulators = yield restApi.emulator.list({ organizationUid });
|
|
41
|
+
return emulators;
|
|
41
42
|
}
|
|
42
43
|
catch (e) {
|
|
43
44
|
if (e instanceof AuthenticationError_1.default) {
|
|
@@ -84,14 +85,18 @@ function loadEmulatorOrCreateNewAndReturnUid(organizationUid) {
|
|
|
84
85
|
}
|
|
85
86
|
else if (listOfEmulatorsResponse.length > 1) {
|
|
86
87
|
const selectedEmulator = yield (0, prompts_1.default)({
|
|
87
|
-
type: '
|
|
88
|
+
type: 'autocomplete',
|
|
88
89
|
name: 'duid',
|
|
89
90
|
message: 'Select emulator to use',
|
|
90
91
|
choices: listOfEmulatorsResponse.map((emu) => ({
|
|
91
92
|
title: `${emu.name} (${emu.duid})`,
|
|
92
93
|
value: emu.duid,
|
|
93
94
|
})),
|
|
95
|
+
suggest: helper_1.autocompleteSuggest,
|
|
94
96
|
});
|
|
97
|
+
if (!selectedEmulator.duid) {
|
|
98
|
+
throw new Error('Emulator selection was cancelled');
|
|
99
|
+
}
|
|
95
100
|
emulatorUid = selectedEmulator.duid;
|
|
96
101
|
}
|
|
97
102
|
else {
|
|
@@ -57,8 +57,10 @@ const fsExtra = __importStar(require("fs-extra"));
|
|
|
57
57
|
const chalk_1 = __importDefault(require("chalk"));
|
|
58
58
|
const log_1 = require("@signageos/sdk/dist/Console/log");
|
|
59
59
|
const fileSystem_1 = require("../Lib/fileSystem");
|
|
60
|
+
const helper_1 = require("../helper");
|
|
60
61
|
const DUMMY_CHECKSUM = '0000000000ffffffffff';
|
|
61
62
|
const APPLET_DIRECTORY_PATH = '/applet';
|
|
63
|
+
const SOS_CONFIG_LOCAL_FILENAME = 'sos.config.local.json';
|
|
62
64
|
function createEmulator(params, organizationUid, dev) {
|
|
63
65
|
return __awaiter(this, void 0, void 0, function* () {
|
|
64
66
|
const { appletUid, appletVersion, emulatorServerPort, appletPath, entryFileRelativePath } = params;
|
|
@@ -84,6 +86,7 @@ function createEmulator(params, organizationUid, dev) {
|
|
|
84
86
|
res.redirect(`${req.originalUrl}${req.originalUrl.includes('?') ? '&' : '?'}duid=${params.emulatorUid}`);
|
|
85
87
|
}
|
|
86
88
|
else {
|
|
89
|
+
const localConfig = loadSosLocalConfig(appletPath);
|
|
87
90
|
const page = fsExtra.readFileSync(path.join(frontDisplayDistPath, 'index.html')).toString();
|
|
88
91
|
const script = `
|
|
89
92
|
<script>
|
|
@@ -91,6 +94,7 @@ function createEmulator(params, organizationUid, dev) {
|
|
|
91
94
|
window.__SOS_BUNDLED_APPLET.binaryFile = location.origin + ${JSON.stringify(envVars.binaryFilePath)};
|
|
92
95
|
window.__SOS_BUNDLED_APPLET.uid = ${JSON.stringify(envVars.uid)};
|
|
93
96
|
window.__SOS_BUNDLED_APPLET.version = ${JSON.stringify(envVars.version)};
|
|
97
|
+
window.__SOS_BUNDLED_APPLET.config = ${JSON.stringify(localConfig)};
|
|
94
98
|
window.__SOS_BUNDLED_APPLET.checksum = ${JSON.stringify(envVars.checksum)};
|
|
95
99
|
window.__SOS_BUNDLED_APPLET.frontAppletVersion = ${JSON.stringify(envVars.frontAppletVersion)};
|
|
96
100
|
window.__SOS_BUNDLED_APPLET.frontAppletBinaryFile = ${JSON.stringify(envVars.frontAppletBinaryFile)};
|
|
@@ -139,3 +143,17 @@ function createEmulator(params, organizationUid, dev) {
|
|
|
139
143
|
};
|
|
140
144
|
});
|
|
141
145
|
}
|
|
146
|
+
function loadSosLocalConfig(appletPath) {
|
|
147
|
+
const sosConfigLocalPath = path.join(appletPath, SOS_CONFIG_LOCAL_FILENAME);
|
|
148
|
+
if (fsExtra.existsSync(sosConfigLocalPath)) {
|
|
149
|
+
try {
|
|
150
|
+
const configContent = fsExtra.readFileSync(sosConfigLocalPath, 'utf8');
|
|
151
|
+
(0, log_1.log)('info', `Loaded local config from ${SOS_CONFIG_LOCAL_FILENAME}`);
|
|
152
|
+
return JSON.parse(configContent);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
(0, log_1.log)('warning', `Failed to load ${SOS_CONFIG_LOCAL_FILENAME}: ${(0, helper_1.getErrorMessageFromUnknownError)(error)}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {};
|
|
159
|
+
}
|
|
@@ -173,15 +173,12 @@ exports.firmwareUpload = (0, commandDefinition_1.createCommandDefinition)({
|
|
|
173
173
|
pathSet.add(path);
|
|
174
174
|
});
|
|
175
175
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
catch (error) {
|
|
176
|
+
yield (0, firmwareUploadFacade_1.uploadFirmwareVersion)({
|
|
177
|
+
restApi,
|
|
178
|
+
firmware: data,
|
|
179
|
+
pathArr: Array.from(pathSet),
|
|
180
|
+
progressBar: (0, progressBarFactory_1.createProgressBar)(),
|
|
181
|
+
}).catch((error) => __awaiter(this, void 0, void 0, function* () {
|
|
185
182
|
if (error instanceof RequestError_1.default && error.errorName === 'INVALID_TYPE_TO_FIRMWARE_VERSION_UPLOAD') {
|
|
186
183
|
const promptOverride = () => (0, prompts_1.default)({
|
|
187
184
|
type: 'confirm',
|
|
@@ -204,7 +201,7 @@ exports.firmwareUpload = (0, commandDefinition_1.createCommandDefinition)({
|
|
|
204
201
|
else {
|
|
205
202
|
throw error;
|
|
206
203
|
}
|
|
207
|
-
}
|
|
204
|
+
}));
|
|
208
205
|
});
|
|
209
206
|
},
|
|
210
207
|
});
|
|
@@ -41,11 +41,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
41
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
44
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
48
|
exports.uploadFirmwareVersion = uploadFirmwareVersion;
|
|
46
49
|
const path = __importStar(require("path"));
|
|
47
50
|
const fs = __importStar(require("fs-extra"));
|
|
48
51
|
const fileSystem_1 = require("../../Lib/fileSystem");
|
|
52
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
49
53
|
function uploadFirmwareVersion(parameters) {
|
|
50
54
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51
55
|
const { restApi, firmware, pathArr, progressBar } = parameters;
|
|
@@ -53,17 +57,22 @@ function uploadFirmwareVersion(parameters) {
|
|
|
53
57
|
const stat = yield fs.stat(filePath);
|
|
54
58
|
return stat.size;
|
|
55
59
|
})));
|
|
56
|
-
|
|
57
|
-
if (progressBar) {
|
|
58
|
-
progressBar.init({ size: totalSize, name: pathArr.join(',') });
|
|
59
|
-
}
|
|
60
|
+
// Process each file sequentially for cleaner output
|
|
60
61
|
for (const index in pathArr) {
|
|
61
62
|
const filePath = pathArr[index];
|
|
62
63
|
if (!filePath) {
|
|
63
64
|
continue;
|
|
64
65
|
}
|
|
65
|
-
const fileSize = sizes[parseInt(index)];
|
|
66
|
+
const fileSize = sizes[Number.parseInt(index)];
|
|
67
|
+
if (fileSize === undefined) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
66
70
|
const fileName = path.basename(filePath);
|
|
71
|
+
// Log with newline for cleaner output
|
|
72
|
+
console.info(chalk_1.default.yellow(`Uploading ${filePath}\n`));
|
|
73
|
+
if (progressBar) {
|
|
74
|
+
progressBar.init({ size: fileSize, name: fileName });
|
|
75
|
+
}
|
|
67
76
|
const md5Hash = yield (0, fileSystem_1.getFileMD5Checksum)(filePath);
|
|
68
77
|
const stream = fs.createReadStream(filePath);
|
|
69
78
|
stream.pause();
|
|
@@ -75,7 +84,7 @@ function uploadFirmwareVersion(parameters) {
|
|
|
75
84
|
firmware.files.push({
|
|
76
85
|
hash: md5Hash,
|
|
77
86
|
content: stream,
|
|
78
|
-
size: fileSize
|
|
87
|
+
size: fileSize,
|
|
79
88
|
});
|
|
80
89
|
}
|
|
81
90
|
try {
|
|
@@ -26,7 +26,7 @@ export declare const ORGANIZATION_OPTIONS: ({
|
|
|
26
26
|
readonly type: BooleanConstructor;
|
|
27
27
|
readonly description: "Prevent using the defaultOrganizationUid from ~/.sosrc";
|
|
28
28
|
})[];
|
|
29
|
-
export declare function getOrganizationUidOrDefaultOrSelect(options: CommandLineOptions<[typeof ORGANIZATION_UID_OPTION, typeof NO_DEFAULT_ORGANIZATION_OPTION]
|
|
29
|
+
export declare function getOrganizationUidOrDefaultOrSelect(options: CommandLineOptions<[typeof ORGANIZATION_UID_OPTION, typeof NO_DEFAULT_ORGANIZATION_OPTION]>, skipPrompts?: boolean): Promise<string>;
|
|
30
30
|
export declare function selectOrganizationUid(options: CommandLineOptions<[typeof ORGANIZATION_UID_OPTION]>): Promise<string>;
|
|
31
31
|
export declare function getOrganizations(): Promise<IOrganization[]>;
|
|
32
32
|
export declare function getOrganization(organizationUid: string): Promise<IOrganization>;
|