@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.
Files changed (43) hide show
  1. package/README.md +12 -6
  2. package/dist/Applet/Build/appletBuildCommand.js +2 -0
  3. package/dist/Applet/Generate/appletGenerateCommand.js +5 -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/Emulator/emulatorFactory.js +18 -0
  28. package/dist/Firmware/Upload/firmwareUploadCommand.js +7 -10
  29. package/dist/Firmware/Upload/firmwareUploadFacade.js +15 -6
  30. package/dist/Organization/organizationFacade.d.ts +1 -1
  31. package/dist/Organization/organizationFacade.js +50 -13
  32. package/dist/Plugin/Upload/pluginUploadCommand.js +2 -1
  33. package/dist/RunControl/runControlHelper.d.ts +7 -1
  34. package/dist/RunControl/runControlHelper.js +19 -1
  35. package/dist/Runner/Upload/runnerUploadCommand.js +2 -1
  36. package/dist/Timing/List/timingListCommand.js +1 -3
  37. package/dist/helper.d.ts +19 -0
  38. package/dist/helper.js +45 -3
  39. package/dist/parameters.d.ts +0 -1
  40. package/dist/parameters.js +3 -6
  41. package/docs/applet/upload/index.md +15 -1
  42. package/docs/index.md +13 -0
  43. package/package.json +11 -7
package/README.md CHANGED
@@ -45,12 +45,6 @@ sos autocomplete uninstall
45
45
  This will remove the completion script and the source line from your shell configuration file.
46
46
 
47
47
 
48
- ## Test
49
- ```bash
50
- cp .env.amy .env
51
- npm test
52
- ```
53
-
54
48
  ## API reference
55
49
  ### General
56
50
  | Argument | Description | Default value |
@@ -175,6 +169,18 @@ sos applet start
175
169
  | --detach *(optional)* | Run the applet HTTP server in the background (detached process) | false |
176
170
  | --forward-server-url *(optional)* | Custom forward server URL for `sos device connect` command. | https://forward.signageos.io |
177
171
 
172
+ ##### Local Configuration
173
+ You can create a `sos.config.local.json` file in your applet directory to provide configuration values during local development. This file is automatically loaded when running `sos applet start` and passed to your applet as configuration.
174
+
175
+ ```json
176
+ {
177
+ "myConfigKey": "myConfigValue",
178
+ "anotherSetting": true
179
+ }
180
+ ```
181
+
182
+ This is useful for testing your applet with different configuration values without having to deploy it to a device.
183
+
178
184
  #### Applet Tests Upload
179
185
  ```bash
180
186
  sos applet test upload
@@ -21,6 +21,7 @@ const dist_1 = require("@signageos/sdk/dist");
21
21
  const appletFacade_1 = require("../appletFacade");
22
22
  const log_1 = require("@signageos/sdk/dist/Console/log");
23
23
  const debug_1 = __importDefault(require("debug"));
24
+ const appletValidation_1 = require("../appletValidation");
24
25
  const Debug = (0, debug_1.default)('@signageos/cli:Applet:Build:appletBuildCommand');
25
26
  exports.OPTION_LIST = [organizationFacade_1.NO_DEFAULT_ORGANIZATION_OPTION, organizationFacade_1.ORGANIZATION_UID_OPTION, appletFacade_1.APPLET_UID_OPTION];
26
27
  /**
@@ -56,6 +57,7 @@ exports.appletBuild = (0, commandDefinition_1.createCommandDefinition)({
56
57
  run(options) {
57
58
  return __awaiter(this, void 0, void 0, function* () {
58
59
  const currentDirectory = process.cwd();
60
+ yield (0, appletValidation_1.validateAppletDirectory)(currentDirectory);
59
61
  const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options);
60
62
  const organization = yield (0, organizationFacade_1.getOrganization)(organizationUid);
61
63
  const restApi = yield (0, helper_1.createOrganizationRestApi)(organization);
@@ -413,11 +413,15 @@ exports.appletGenerate = (0, commandDefinition_1.createCommandDefinition)({
413
413
  path: path.join(appletRootDirectory, '.sosignore'),
414
414
  content: 'node_modules/\n',
415
415
  });
416
+ generateFiles.push({
417
+ path: path.join(appletRootDirectory, 'sos.config.local.json'),
418
+ content: '{}\n',
419
+ });
416
420
  // Initialise git repository
417
421
  if (git === GitOptions.Yes && gitFound) {
418
422
  generateFiles.push({
419
423
  path: path.join(appletRootDirectory, '.gitignore'),
420
- content: 'node_modules/\n./dist',
424
+ content: 'node_modules/\n/dist/\nsos.config.local.json',
421
425
  });
422
426
  yield (0, git_1.initGitRepository)(appletRootDirectory, true).catch((error) => {
423
427
  console.error(`Git repository initialization failed: ${error}`);
@@ -24,6 +24,7 @@ const wait_1 = __importDefault(require("../../Timer/wait"));
24
24
  const appletServerHelper_1 = require("../appletServerHelper");
25
25
  const log_1 = require("@signageos/sdk/dist/Console/log");
26
26
  const parameters_1 = require("../../parameters");
27
+ const appletValidation_1 = require("../appletValidation");
27
28
  const DEFAULT_PORT = 8090;
28
29
  const PORT_OPTION = {
29
30
  name: 'port',
@@ -96,8 +97,9 @@ exports.appletStart = (0, commandDefinition_1.createCommandDefinition)({
96
97
  var _a, _b;
97
98
  const currentDirectory = process.cwd();
98
99
  const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options);
99
- const entryFileAbsolutePath = yield (0, appletUploadCommandHelper_1.getAppletEntryFileAbsolutePath)(currentDirectory, options);
100
100
  const appletPath = yield (0, appletUploadCommandHelper_1.getAppletDirectoryAbsolutePath)(currentDirectory, options);
101
+ yield (0, appletValidation_1.validateAppletDirectory)(appletPath);
102
+ const entryFileAbsolutePath = yield (0, appletUploadCommandHelper_1.getAppletEntryFileAbsolutePath)(currentDirectory, options);
101
103
  const entryFileRelativePath = (0, appletUploadCommandHelper_1.getAppletEntryFileRelativePath)(entryFileAbsolutePath, appletPath);
102
104
  const emulatorUid = yield (0, emulatorFacade_1.loadEmulatorOrCreateNewAndReturnUid)(organizationUid);
103
105
  const dev = (0, dist_1.createDevelopment)({
@@ -24,6 +24,7 @@ const appletTestRunFacade_1 = require("./appletTestRunFacade");
24
24
  const wait_1 = __importDefault(require("../../../Timer/wait"));
25
25
  const commandDefinition_1 = require("../../../Command/commandDefinition");
26
26
  const log_1 = require("@signageos/sdk/dist/Console/log");
27
+ const appletValidation_1 = require("../../appletValidation");
27
28
  const OPTION_LIST = [
28
29
  organizationFacade_1.NO_DEFAULT_ORGANIZATION_OPTION,
29
30
  deviceFacade_1.DEVICE_UID_OPTION,
@@ -75,7 +76,8 @@ exports.appletTestRun = (0, commandDefinition_1.createCommandDefinition)({
75
76
  const skipConfirmation = !!options.yes;
76
77
  let tests = options.test;
77
78
  const currentDirectory = process.cwd();
78
- const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options);
79
+ yield (0, appletValidation_1.validateAppletDirectory)(currentDirectory);
80
+ const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options, skipConfirmation);
79
81
  const organization = yield (0, organizationFacade_1.getOrganization)(organizationUid);
80
82
  const restApi = yield (0, helper_1.createOrganizationRestApi)(organization);
81
83
  const deviceUid = yield (0, deviceFacade_1.getDeviceUid)(restApi, options);
@@ -88,8 +90,10 @@ exports.appletTestRun = (0, commandDefinition_1.createCommandDefinition)({
88
90
  if (!tests || tests.length === 0) {
89
91
  tests = testSuites.map((testSuite) => testSuite.identifier);
90
92
  }
91
- (0, appletTestRunFacade_1.validateTestIdentifiers)(tests, testSuites);
92
- printRunTests(tests);
93
+ // At this point tests should be defined, but TypeScript doesn't know that
94
+ const testsToRun = tests || [];
95
+ (0, appletTestRunFacade_1.validateTestIdentifiers)(testsToRun, testSuites);
96
+ printRunTests(testsToRun);
93
97
  if (!skipConfirmation) {
94
98
  const response = yield (0, prompts_1.default)({
95
99
  type: 'confirm',
@@ -104,10 +108,10 @@ exports.appletTestRun = (0, commandDefinition_1.createCommandDefinition)({
104
108
  const progressBar = (0, progressBarFactory_1.createProgressBar)();
105
109
  try {
106
110
  progressBar.init({
107
- size: tests.length,
111
+ size: testsToRun.length,
108
112
  name: RUNNING_TEST_TITLE,
109
113
  });
110
- yield restApi.device.appletTest.run(device.uid, applet.uid, appletVersion.version, tests);
114
+ yield restApi.device.appletTest.run(device.uid, applet.uid, appletVersion.version, testsToRun);
111
115
  let deviceAppletTest;
112
116
  do {
113
117
  yield (0, wait_1.default)(1e3);
@@ -23,6 +23,7 @@ const progressBarFactory_1 = require("../../../CommandLine/progressBarFactory");
23
23
  const packageConfig_1 = require("@signageos/sdk/dist/FileSystem/packageConfig");
24
24
  const commandDefinition_1 = require("../../../Command/commandDefinition");
25
25
  const log_1 = require("@signageos/sdk/dist/Console/log");
26
+ const appletValidation_1 = require("../../appletValidation");
26
27
  const OPTION_LIST = [
27
28
  organizationFacade_1.NO_DEFAULT_ORGANIZATION_OPTION,
28
29
  organizationFacade_1.ORGANIZATION_UID_OPTION,
@@ -79,7 +80,8 @@ exports.appletTestUpload = (0, commandDefinition_1.createCommandDefinition)({
79
80
  const isVerbose = !!options.verbose;
80
81
  const skipConfirmation = !!options.yes;
81
82
  const currentDirectory = process.cwd();
82
- const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options);
83
+ yield (0, appletValidation_1.validateAppletDirectory)(currentDirectory);
84
+ const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options, skipConfirmation);
83
85
  const organization = yield (0, organizationFacade_1.getOrganization)(organizationUid);
84
86
  const restApi = yield (0, helper_1.createOrganizationRestApi)(organization);
85
87
  const version = yield (0, appletFacade_1.getAppletVersion)(currentDirectory);
@@ -151,8 +153,8 @@ exports.appletTestUpload = (0, commandDefinition_1.createCommandDefinition)({
151
153
  },
152
154
  });
153
155
  function displaySuccessMessage(appletName, appletVersion) {
154
- (0, log_1.log)('info', `Applet ${chalk_1.default.green(appletName)} version ${chalk_1.default.green(appletVersion)} tests has been uploaded.`);
155
- (0, log_1.log)('info', `To run the tests, use command ${chalk_1.default.blue(`sos applet test run`)}`);
156
+ (0, log_1.log)('info', `\nApplet ${chalk_1.default.green(appletName)} version ${chalk_1.default.green(appletVersion)} tests has been uploaded.`);
157
+ (0, log_1.log)('info', `To run the tests, use command ${chalk_1.default.green(`sos applet test run`)}`);
156
158
  }
157
159
  function printMatchedFiles(testFiles) {
158
160
  if (testFiles.length > 0) {
@@ -56,14 +56,28 @@ export declare const OPTION_LIST: readonly [{
56
56
  * # Upload with organization override
57
57
  * sos applet upload --organization-uid abc123def456
58
58
  *
59
- * # Skip confirmation prompts
59
+ * # Skip confirmation prompts (auto-selects if only 1 org available)
60
60
  * sos applet upload --yes
61
61
  *
62
+ * # CI/CD: Non-interactive upload with specific organization
63
+ * sos applet upload --yes --organization-uid abc123def456
64
+ *
62
65
  * # Verbose output with detailed file information
63
66
  * sos applet upload --verbose
64
67
  *
65
68
  * # Update package.json with new applet UID
66
69
  * sos applet upload --update-package-config
70
+ *
71
+ * # Complete CI/CD example
72
+ * sos applet upload --yes --organization-uid abc123def456 --update-package-config
73
+ * ```
74
+ *
75
+ * **Note for CI/CD and --yes flag:**
76
+ * When using `--yes` with multiple organizations:
77
+ * - If you have only 1 organization: it will be auto-selected
78
+ * - If you have multiple organizations: you MUST specify `--organization-uid`
79
+ * - Alternative: Set a default organization with `sos organization set-default`
80
+ *
67
81
  * ```
68
82
  *
69
83
  * @throws {Error} When package.json is missing or invalid
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
36
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
37
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -15,8 +48,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
48
  exports.appletUpload = exports.OPTION_LIST = void 0;
16
49
  const chalk_1 = __importDefault(require("chalk"));
17
50
  const prompts_1 = __importDefault(require("prompts"));
51
+ const path = __importStar(require("path"));
52
+ const fs = __importStar(require("fs-extra"));
18
53
  const helper_1 = require("../../helper");
19
- const parameters_1 = require("../../parameters");
20
54
  const organizationFacade_1 = require("../../Organization/organizationFacade");
21
55
  const appletFacade_1 = require("../appletFacade");
22
56
  const appletUploadFacade_1 = require("./appletUploadFacade");
@@ -30,6 +64,7 @@ const log_1 = require("@signageos/sdk/dist/Console/log");
30
64
  const sdk_1 = require("@signageos/sdk");
31
65
  const GatewayError_1 = __importDefault(require("@signageos/sdk/dist/RestApi/Error/GatewayError"));
32
66
  const NotFoundError_1 = __importDefault(require("@signageos/sdk/dist/RestApi/Error/NotFoundError"));
67
+ const appletValidation_1 = require("../appletValidation");
33
68
  exports.OPTION_LIST = [
34
69
  appletUploadCommandHelper_1.APPLET_PATH_OPTION,
35
70
  appletUploadCommandHelper_1.ENTRY_FILE_PATH_OPTION,
@@ -77,14 +112,28 @@ exports.OPTION_LIST = [
77
112
  * # Upload with organization override
78
113
  * sos applet upload --organization-uid abc123def456
79
114
  *
80
- * # Skip confirmation prompts
115
+ * # Skip confirmation prompts (auto-selects if only 1 org available)
81
116
  * sos applet upload --yes
82
117
  *
118
+ * # CI/CD: Non-interactive upload with specific organization
119
+ * sos applet upload --yes --organization-uid abc123def456
120
+ *
83
121
  * # Verbose output with detailed file information
84
122
  * sos applet upload --verbose
85
123
  *
86
124
  * # Update package.json with new applet UID
87
125
  * sos applet upload --update-package-config
126
+ *
127
+ * # Complete CI/CD example
128
+ * sos applet upload --yes --organization-uid abc123def456 --update-package-config
129
+ * ```
130
+ *
131
+ * **Note for CI/CD and --yes flag:**
132
+ * When using `--yes` with multiple organizations:
133
+ * - If you have only 1 organization: it will be auto-selected
134
+ * - If you have multiple organizations: you MUST specify `--organization-uid`
135
+ * - Alternative: Set a default organization with `sos organization set-default`
136
+ *
88
137
  * ```
89
138
  *
90
139
  * @throws {Error} When package.json is missing or invalid
@@ -103,25 +152,44 @@ exports.appletUpload = (0, commandDefinition_1.createCommandDefinition)({
103
152
  run(options) {
104
153
  return __awaiter(this, void 0, void 0, function* () {
105
154
  const currentDirectory = process.cwd();
106
- const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options);
155
+ const skipPrompts = options.yes;
156
+ const organizationUid = yield (0, organizationFacade_1.getOrganizationUidOrDefaultOrSelect)(options, skipPrompts);
107
157
  const organization = yield (0, organizationFacade_1.getOrganization)(organizationUid);
108
158
  const restApi = yield (0, helper_1.createOrganizationRestApi)(organization);
109
- const { name: appletName, version: appletVersion, frontAppletVersion } = yield (0, appletFacade_1.getApplet)(currentDirectory);
110
159
  const appletPathOption = options['applet-path'];
111
160
  const appletEntryOption = options['entry-file-path'];
112
161
  const updatePackageConfig = options['update-package-config'];
113
162
  let appletBinaryFilePath = undefined;
114
163
  let appletDirectoryPath = undefined;
115
164
  let appletEntryFilePath = undefined;
116
- const isSingleFileApplet = !!(appletPathOption && !appletEntryOption);
165
+ // Determine if this is a single file applet by checking if applet-path points to a file
166
+ let isSingleFileApplet = false;
167
+ if (appletPathOption) {
168
+ const appletPath = path.isAbsolute(appletPathOption) ? appletPathOption : path.join(currentDirectory, appletPathOption);
169
+ if (yield fs.pathExists(appletPath)) {
170
+ const stats = yield fs.stat(appletPath);
171
+ isSingleFileApplet = stats.isFile();
172
+ }
173
+ else {
174
+ // If path doesn't exist, fall back to the original logic
175
+ isSingleFileApplet = !appletEntryOption;
176
+ }
177
+ }
117
178
  if (isSingleFileApplet) {
118
179
  displaySingleFileAppletDeprecationNote();
119
180
  appletBinaryFilePath = yield (0, appletUploadCommandHelper_1.getAppletBinaryFileAbsolutePath)(currentDirectory, options);
120
181
  }
121
182
  else {
122
183
  appletDirectoryPath = yield (0, appletUploadCommandHelper_1.getAppletDirectoryAbsolutePath)(currentDirectory, options);
123
- appletEntryFilePath = yield (0, appletUploadCommandHelper_1.getAppletEntryFileAbsolutePath)(currentDirectory, options);
184
+ // For multi-file applets, use the applet directory as the base for resolving the entry file
185
+ appletEntryFilePath = yield (0, appletUploadCommandHelper_1.getAppletEntryFileAbsolutePath)(appletDirectoryPath, options);
124
186
  }
187
+ // Validate that the applet directory contains a valid applet
188
+ // For multi-file applets, validate the resolved applet directory
189
+ // For single-file applets (deprecated), validate the current directory
190
+ const directoryToValidate = appletDirectoryPath !== null && appletDirectoryPath !== void 0 ? appletDirectoryPath : currentDirectory;
191
+ yield (0, appletValidation_1.validateAppletDirectory)(directoryToValidate);
192
+ const { name: appletName, version: appletVersion, frontAppletVersion } = yield (0, appletFacade_1.getApplet)(directoryToValidate);
125
193
  let overrideAppletVersionConfirmed = false;
126
194
  let createNewAppletVersionConfirmed = false;
127
195
  let appletUid;
@@ -138,7 +206,10 @@ exports.appletUpload = (0, commandDefinition_1.createCommandDefinition)({
138
206
  }
139
207
  finally {
140
208
  if (updatePackageConfig) {
141
- yield (0, packageConfig_1.saveToPackage)(currentDirectory, { sos: { appletUid } });
209
+ // For multi-file applets, save to the applet directory's package.json
210
+ // For single-file applets, save to the current directory's package.json
211
+ const packageConfigDirectory = isSingleFileApplet ? currentDirectory : appletDirectoryPath || currentDirectory;
212
+ yield (0, packageConfig_1.saveToPackage)(packageConfigDirectory, { sos: { appletUid } });
142
213
  }
143
214
  }
144
215
  const applet = yield restApi.applet.get(appletUid);
@@ -233,7 +304,7 @@ exports.appletUpload = (0, commandDefinition_1.createCommandDefinition)({
233
304
  progressBar: progressBar,
234
305
  });
235
306
  }
236
- displaySuccessMessage(applet.uid, applet.name, appletVersion, parameters_1.parameters.boxHost);
307
+ displaySuccessMessage(applet.uid, applet.name, appletVersion);
237
308
  }
238
309
  else if (createNewAppletVersionConfirmed) {
239
310
  if (isSingleFileApplet) {
@@ -262,7 +333,7 @@ exports.appletUpload = (0, commandDefinition_1.createCommandDefinition)({
262
333
  progressBar,
263
334
  });
264
335
  }
265
- displaySuccessMessage(applet.uid, applet.name, appletVersion, parameters_1.parameters.boxHost);
336
+ displaySuccessMessage(applet.uid, applet.name, appletVersion);
266
337
  }
267
338
  }
268
339
  catch (error) {
@@ -274,10 +345,8 @@ exports.appletUpload = (0, commandDefinition_1.createCommandDefinition)({
274
345
  });
275
346
  },
276
347
  });
277
- function displaySuccessMessage(appletUid, appletName, appletVersion, boxHost) {
278
- (0, log_1.log)('info', `Applet ${chalk_1.default.green(appletName)} version ${chalk_1.default.green(appletVersion)} has been uploaded.`);
279
- const appletBoxUri = `https://${boxHost}/applet/${appletUid}/${appletVersion}/build`;
280
- (0, log_1.log)('info', `To build specific applications (Tizen, WebOS, SSSP, BrightSign, RPi, Android etc.) go to ${chalk_1.default.blue(appletBoxUri)}`);
348
+ function displaySuccessMessage(appletUid, appletName, appletVersion) {
349
+ (0, log_1.log)('info', `\nApplet ${chalk_1.default.green(appletName)} version ${chalk_1.default.green(appletVersion)} (${chalk_1.default.blue(appletUid)}) uploaded successfully.`);
281
350
  }
282
351
  function displaySingleFileAppletDeprecationNote() {
283
352
  (0, log_1.log)('warning', `${chalk_1.default.red(`Applets with only applet-path file are ${chalk_1.default.bold(`deprecated`)}.`)} Please find more information at our website.`);
@@ -82,7 +82,7 @@ function getAppletDirectoryAbsolutePath(currentDirectory, options) {
82
82
  if (appletDirectoryPath.length > 1 && (appletDirectoryPath.endsWith('/') || appletDirectoryPath.endsWith('\\'))) {
83
83
  appletDirectoryPath = appletDirectoryPath.slice(0, -1);
84
84
  }
85
- (0, log_1.log)('info', `\nUse applet project directory path: ${appletDirectoryPath}`);
85
+ (0, log_1.log)('info', chalk_1.default.white(`Use applet project directory path: ${appletDirectoryPath}`) + '\n');
86
86
  const appletDirectoryPathExists = yield fs.pathExists(appletDirectoryPath);
87
87
  if (!appletDirectoryPathExists) {
88
88
  throw new Error(`Applet project directory not found: ${appletDirectoryPath}`);
@@ -105,7 +105,7 @@ function getAppletBinaryFileAbsolutePath(currentDirectory, options) {
105
105
  }
106
106
  // Normalize the path for cross-platform compatibility
107
107
  appletBinaryFilePath = path.normalize(appletBinaryFilePath);
108
- (0, log_1.log)('info', `\nUse applet binary file: ${appletBinaryFilePath}`);
108
+ (0, log_1.log)('info', chalk_1.default.white(`Use applet binary file: ${appletBinaryFilePath}` + '\n'));
109
109
  const appletBinaryFilePathExists = yield fs.pathExists(appletBinaryFilePath);
110
110
  if (!appletBinaryFilePathExists) {
111
111
  throw new Error(`Applet binary file not found: ${appletBinaryFilePath}`);
@@ -132,7 +132,7 @@ function getAppletEntryFileAbsolutePath(currentDirectory, options) {
132
132
  }
133
133
  // Normalize the path for cross-platform compatibility
134
134
  appletEntryFilePath = path.normalize(appletEntryFilePath);
135
- (0, log_1.log)('info', `\nUse applet entry file: ${appletEntryFilePath}`);
135
+ (0, log_1.log)('info', chalk_1.default.white(`Use applet entry file: ${appletEntryFilePath}`) + '\n');
136
136
  const appletEntryFilePathExists = yield fs.pathExists(appletEntryFilePath);
137
137
  if (!appletEntryFilePathExists) {
138
138
  throw new Error(`Applet entry file not found: ${appletEntryFilePath}\nDid you forget to build your applet by ${chalk_1.default.green('sos applet build')}?`);
@@ -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.update(applet.uid, 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: fileRelativePath });
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
- try {
107
- // update file is just alias to create file (both are idempotent)
108
- yield restApi.applet.version.file.update(applet.uid, applet.version, fileRelativePosixPath, {
109
- content: fileStream,
110
- hash: fileHash,
111
- size: fileSize,
112
- type: fileType,
113
- }, { build: false });
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
- try {
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.update(applet.uid, 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.create(applet.uid, {
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.create(applet.uid, {
209
+ yield restApi.applet.version
210
+ .create(applet.uid, {
174
211
  version: applet.version,
175
212
  entryFile: appletEntryFilePosixPath,
176
- });
177
- yield Promise.all(applet.files.map((fileAbsolutePath) => __awaiter(void 0, void 0, void 0, function* () {
178
- const fileRelativePath = (0, appletUploadFacadeHelper_1.getAppletFileRelativePath)(fileAbsolutePath, applet.directoryPath);
179
- const fileHash = yield (0, fileSystem_1.getFileMD5Checksum)(fileAbsolutePath);
180
- const fileType = yield (0, fileSystem_1.getFileType)(fileAbsolutePath);
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
- const fileStream = fs.createReadStream(fileAbsolutePath);
189
- fileStream.pause();
190
- fileStream.on('data', (chunk) => {
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.update({ add: chunk.length });
234
+ progressBar.init({ size: fileSize, name: filePosixPath });
193
235
  }
194
- });
195
- const filePosixPath = path.posix.normalize(fileRelativePath.replace(/\\/g, '/'));
196
- (0, log_1.log)('info', chalk_1.default.yellow(` Uploading ${fileAbsolutePath}`));
197
- return restApi.applet.version.file.create(applet.uid, applet.version, {
198
- name: path.basename(filePosixPath),
199
- path: filePosixPath,
200
- type: fileType,
201
- hash: fileHash,
202
- content: fileStream,
203
- size: fileSize,
204
- }, { build: false });
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
- entryFile: appletEntryFilePosixPath,
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;