@signageos/cli 2.8.0 → 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.
Files changed (41) hide show
  1. package/README.md +0 -6
  2. package/dist/Applet/Build/appletBuildCommand.js +2 -0
  3. package/dist/Applet/Generate/appletGenerateCommand.js +1 -1
  4. package/dist/Applet/Start/appletStartCommand.js +3 -1
  5. package/dist/Applet/Test/Upload/appletTestRunCommand.js +9 -5
  6. package/dist/Applet/Test/Upload/appletTestUploadCommand.js +5 -3
  7. package/dist/Applet/Upload/appletUploadCommand.d.ts +15 -1
  8. package/dist/Applet/Upload/appletUploadCommand.js +82 -13
  9. package/dist/Applet/Upload/appletUploadCommandHelper.js +3 -3
  10. package/dist/Applet/Upload/appletUploadFacade.js +124 -52
  11. package/dist/Applet/Upload/appletUploadFacadeHelper.d.ts +1 -1
  12. package/dist/Applet/Upload/appletUploadFacadeHelper.js +3 -3
  13. package/dist/Applet/appletErrors.d.ts +3 -0
  14. package/dist/Applet/appletErrors.js +8 -1
  15. package/dist/Applet/appletFacade.js +9 -0
  16. package/dist/Applet/appletValidation.d.ts +17 -0
  17. package/dist/Applet/appletValidation.js +62 -0
  18. package/dist/Auth/loginCommand.js +30 -4
  19. package/dist/Command/commandProcessor.js +5 -0
  20. package/dist/Command/globalArgs.d.ts +21 -0
  21. package/dist/Command/globalArgs.js +30 -0
  22. package/dist/CommandLine/progressBarFactory.js +51 -10
  23. package/dist/CustomScript/Upload/customScriptUploadCommand.js +2 -2
  24. package/dist/Device/Content/setContentCommand.js +2 -2
  25. package/dist/Device/deviceFacade.js +8 -0
  26. package/dist/Emulator/emulatorFacade.js +7 -2
  27. package/dist/Firmware/Upload/firmwareUploadCommand.js +7 -10
  28. package/dist/Firmware/Upload/firmwareUploadFacade.js +15 -6
  29. package/dist/Organization/organizationFacade.d.ts +1 -1
  30. package/dist/Organization/organizationFacade.js +50 -13
  31. package/dist/Plugin/Upload/pluginUploadCommand.js +2 -1
  32. package/dist/RunControl/runControlHelper.d.ts +7 -1
  33. package/dist/RunControl/runControlHelper.js +19 -1
  34. package/dist/Runner/Upload/runnerUploadCommand.js +2 -1
  35. package/dist/Timing/List/timingListCommand.js +1 -3
  36. package/dist/helper.d.ts +18 -0
  37. package/dist/helper.js +31 -3
  38. package/dist/parameters.d.ts +0 -1
  39. package/dist/parameters.js +3 -6
  40. package/docs/applet/upload/index.md +15 -1
  41. package/package.json +11 -7
@@ -1,3 +1,6 @@
1
1
  export declare class AppletDoesNotExistError extends Error {
2
2
  constructor(message: string);
3
3
  }
4
+ export declare class AppletSelectionCancelledError extends Error {
5
+ constructor(message?: string);
6
+ }
@@ -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 apiUrlObj = new URL(apiUrl);
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
- const progressBar = new cliProgress.Bar({
40
- format: '[{bar}] {name} {percentage}% | ETA: {eta}s | {value}/{total}',
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
- let current = 0;
43
- let currentName = 'preparing';
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
- currentName = name;
47
- progressBar.start(size, current, { name });
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
- current += add;
51
- currentName = name || currentName;
52
- progressBar.update(current + add, { name: currentName });
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
- progressBar.stop();
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 organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options);
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 organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options);
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
- return yield restApi.emulator.list({ organizationUid });
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: 'select',
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 {
@@ -173,15 +173,12 @@ exports.firmwareUpload = (0, commandDefinition_1.createCommandDefinition)({
173
173
  pathSet.add(path);
174
174
  });
175
175
  }
176
- try {
177
- yield (0, firmwareUploadFacade_1.uploadFirmwareVersion)({
178
- restApi,
179
- firmware: data,
180
- pathArr: Array.from(pathSet),
181
- progressBar: (0, progressBarFactory_1.createProgressBar)(),
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
- const totalSize = sizes.reduce((sum, size) => sum + size, 0);
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 !== null && fileSize !== void 0 ? fileSize : 0,
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]>): Promise<string>;
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>;
@@ -31,26 +31,59 @@ exports.NO_DEFAULT_ORGANIZATION_OPTION = {
31
31
  description: 'Prevent using the defaultOrganizationUid from ~/.sosrc',
32
32
  };
33
33
  exports.ORGANIZATION_OPTIONS = [exports.ORGANIZATION_UID_OPTION, exports.NO_DEFAULT_ORGANIZATION_OPTION];
34
- function getOrganizationUidOrDefaultOrSelect(options) {
35
- return __awaiter(this, void 0, void 0, function* () {
34
+ function getOrganizationUidOrDefaultOrSelect(options_1) {
35
+ return __awaiter(this, arguments, void 0, function* (options, skipPrompts = false) {
36
36
  const config = yield (0, runControlHelper_1.loadConfig)();
37
37
  let organizationUid = options['organization-uid'];
38
38
  if (!organizationUid && !options['no-default-organization']) {
39
39
  organizationUid = config.defaultOrganizationUid;
40
40
  }
41
41
  if (!organizationUid) {
42
- organizationUid = yield selectOrganizationUid(options);
43
- if (organizationUid && !options['no-default-organization']) {
44
- const response = yield (0, prompts_1.default)({
45
- type: 'confirm',
46
- name: 'setDefault',
47
- message: `Do you want to set the organization as a default for current profile?`,
48
- initial: false,
49
- });
50
- if (response.setDefault) {
51
- yield (0, runControlHelper_1.updateConfig)({
52
- defaultOrganizationUid: organizationUid,
42
+ // If skipPrompts is true (e.g., --yes flag), try to auto-select
43
+ if (skipPrompts) {
44
+ const organizations = yield getOrganizations();
45
+ if (organizations.length === 0) {
46
+ throw new Error('No organizations available. Please ensure you have access to at least one organization.');
47
+ }
48
+ if (organizations.length === 1) {
49
+ // Auto-select the only available organization
50
+ const org = organizations[0]; // Safe: we just checked length === 1
51
+ organizationUid = org.uid;
52
+ console.info(chalk_1.default.yellow(`Auto-selected organization: ${org.title} (${org.name}, ${org.uid})`));
53
+ // Set as default to avoid prompts in future
54
+ if (!options['no-default-organization']) {
55
+ yield (0, runControlHelper_1.updateConfig)({
56
+ defaultOrganizationUid: organizationUid,
57
+ });
58
+ console.info(chalk_1.default.green('Organization has been set as default for current profile.'));
59
+ }
60
+ }
61
+ else {
62
+ // Multiple organizations available - cannot auto-select safely
63
+ throw new Error(`Cannot auto-select organization: Multiple organizations available (${organizations.length} found).\n` +
64
+ `Please specify one of the following:\n` +
65
+ ` 1. Use --organization-uid <uid> flag\n` +
66
+ ` 2. Set default organization: sos organization set-default\n` +
67
+ ` 3. Remove --yes flag for interactive selection\n\n` +
68
+ `Available organizations:\n` +
69
+ organizations.map((org) => ` - ${org.title} (${org.name}, ${org.uid})`).join('\n'));
70
+ }
71
+ }
72
+ else {
73
+ // Interactive mode - prompt user to select
74
+ organizationUid = yield selectOrganizationUid(options);
75
+ if (organizationUid && !options['no-default-organization']) {
76
+ const response = yield (0, prompts_1.default)({
77
+ type: 'confirm',
78
+ name: 'setDefault',
79
+ message: `Do you want to set the organization as a default for current profile?`,
80
+ initial: false,
53
81
  });
82
+ if (response.setDefault) {
83
+ yield (0, runControlHelper_1.updateConfig)({
84
+ defaultOrganizationUid: organizationUid,
85
+ });
86
+ }
54
87
  }
55
88
  }
56
89
  }
@@ -70,7 +103,11 @@ function selectOrganizationUid(options) {
70
103
  title: `${org.title} (${org.name}, ${org.uid})`,
71
104
  value: org.uid,
72
105
  })),
106
+ suggest: helper_1.autocompleteSuggest,
73
107
  });
108
+ if (!response.organizationUid) {
109
+ throw new Error('Organization selection was cancelled');
110
+ }
74
111
  Debug('Organization selected', response.organizationUid);
75
112
  organizationUid = response.organizationUid;
76
113
  }
@@ -60,7 +60,8 @@ exports.pluginUpload = (0, commandDefinition_1.createCommandDefinition)({
60
60
  run(options) {
61
61
  return __awaiter(this, void 0, void 0, function* () {
62
62
  const currentDirectory = process.cwd();
63
- const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options);
63
+ const skipPrompts = options.yes;
64
+ const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options, skipPrompts);
64
65
  const organization = yield (0, organizationFacade_1.getOrganization)(organizationUid);
65
66
  const restApi = yield (0, helper_1.createOrganizationRestApi)(organization);
66
67
  const config = yield (0, customScriptFacade_1.getConfig)(currentDirectory);