@microtronics/studio-cli 0.42.0 → 0.44.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/CHANGELOG.md CHANGED
@@ -1,14 +1,29 @@
1
1
  # Changelog
2
2
 
3
- ## 0.42.0 (2026-01-13)
4
-
5
- ### Features
6
-
7
- * add isIotApp helper function to check IoT app project type ([98ce8a1](https://bitbucket.org/microtronics-core/vscode-studio/commits/98ce8a1358bdd4d2ba1635cd4ef1a3f6e8ec0004))
8
- * add POST endpoint for addon timeseries data insertion ([ace6880](https://bitbucket.org/microtronics-core/vscode-studio/commits/ace6880f801faefc303a8020c7acf87b06db5709))
9
-
10
- ### Bug Fixes
11
-
3
+ ## 0.44.0 (2026-01-19)
4
+
5
+ ## 0.43.0 (2026-01-19)
6
+
7
+ ### Features
8
+
9
+ * add debug option to enable mdnCom logging ([34dfd29](https://bitbucket.org/microtronics-core/vscode-studio/commits/34dfd29cf2f5e6776e31463a85f0484a6907b13b))
10
+ * add support for uploading any amx file to an usb device ([e89f108](https://bitbucket.org/microtronics-core/vscode-studio/commits/e89f1087e22e6c0cc88c8b68bbbcc4f1d813dc4f))
11
+ * add USB device detection and listing functionality ([906e3e1](https://bitbucket.org/microtronics-core/vscode-studio/commits/906e3e12bca647f5ceba8b4196f00d507be163d9))
12
+ * add USB device listing command ([fb16e1a](https://bitbucket.org/microtronics-core/vscode-studio/commits/fb16e1ab0c9a52e38467b664e4b2b6adb135a249)), closes [#close](https://bitbucket.org/microtronics-core/vscode-studio/issues/close)
13
+
14
+ ### Bug Fixes
15
+
16
+ * ensure proper exit code on successful USB upload ([cc1ae63](https://bitbucket.org/microtronics-core/vscode-studio/commits/cc1ae632669e785017c2afaa689c3750ba22d7fb))
17
+
18
+ ## 0.42.0 (2026-01-13)
19
+
20
+ ### Features
21
+
22
+ * add isIotApp helper function to check IoT app project type ([98ce8a1](https://bitbucket.org/microtronics-core/vscode-studio/commits/98ce8a1358bdd4d2ba1635cd4ef1a3f6e8ec0004))
23
+ * add POST endpoint for addon timeseries data insertion ([ace6880](https://bitbucket.org/microtronics-core/vscode-studio/commits/ace6880f801faefc303a8020c7acf87b06db5709))
24
+
25
+ ### Bug Fixes
26
+
12
27
  * restrict write access rejection to IoT apps only ([13aa98f](https://bitbucket.org/microtronics-core/vscode-studio/commits/13aa98f261632b576681a894ecb375b7ab5a4b99))
13
28
 
14
29
  ## 0.41.0 (2026-01-12)
package/README.md CHANGED
@@ -21,9 +21,130 @@ For a reference on all the available `studio-cli` commands, run `studio-cli --he
21
21
 
22
22
  ## Requirements
23
23
 
24
- [Node.js](https://nodejs.org/en/) at least `20.x.x`.
24
+ [Node.js](https://nodejs.org/en/) version `22` or higher.
25
25
 
26
26
 
27
+ ## Commands
28
+
29
+ ### install (alias: i)
30
+ Install all dependencies in your project.
31
+
32
+ ```console
33
+ $ studio-cli install [dependency...]
34
+ ```
35
+
36
+ **Options:**
37
+ - `-e, --env <environment>` - CLI target environment (defaults to STUDIO_ENV environment variable)
38
+ - `--token <API-token>` - API Access Token (defaults to STUDIO_TOKEN environment variable)
39
+
40
+ **Examples:**
41
+ ```console
42
+ $ studio-cli install
43
+ $ studio-cli i
44
+ $ studio-cli install dependency-name
45
+ ```
46
+
47
+ ### build
48
+ Build the project.
49
+
50
+ ```console
51
+ $ studio-cli build [options]
52
+ ```
53
+
54
+ **Options:**
55
+ - `-p, --part <APM part>` - APM part that should be built
56
+ - Available parts: `dde`, `dlo`, `pov`, `blo`, `dfiles`
57
+ - `-e, --env <environment>` - CLI target environment
58
+ - Available environments: `lucky`, `wynni`, `production` (default)
59
+ - `--token <API-token>` - API Access Token
60
+
61
+ **Manifest Overrides:**
62
+ You can overwrite manifest options using the `--option=value` syntax.
63
+
64
+ **Examples:**
65
+ ```console
66
+ $ studio-cli build
67
+ $ studio-cli build --part dlo
68
+ $ studio-cli build --env wynni
69
+ $ studio-cli build --dlo.mainFile="./dlo/test.dlo"
70
+ ```
71
+
72
+ ### publish
73
+ Publish the project to the registry.
74
+
75
+ ```console
76
+ $ studio-cli publish [options]
77
+ ```
78
+
79
+ **Options:**
80
+ - `-ph, --phase <release phase>` - Specify a release phase (default: `release`)
81
+ - Available phases: `alpha`, `beta`, `rc`, `release`, `stage`, `passive`, `withdrawn`
82
+ - `--allowedBackends <null|*|server.xz;server.yz>` - Specify the allowedBackends deployment for this version
83
+ - `null` - No restrictions (default)
84
+ - `*` - All backends allowed
85
+ - `server.xz;server.yz` - Specific backend servers (semicolon-separated)
86
+ - `-e, --env <environment>` - CLI target environment
87
+ - Available environments: `lucky`, `wynni`, `production` (default)
88
+ - `--token <API-token>` - API Access Token
89
+ - `-i, --packagePath <path>` - Publish the provided library or app package. Use "*" for autodetection
90
+
91
+ **Examples:**
92
+ ```console
93
+ $ studio-cli publish
94
+ $ studio-cli publish --phase beta
95
+ $ studio-cli publish --allowedBackends "*"
96
+ $ studio-cli publish --packagePath ./my-package.tar.gz --env wynni
97
+ ```
98
+
99
+ ### package (alias: pack)
100
+ Package the project.
101
+
102
+ ```console
103
+ $ studio-cli package [options]
104
+ ```
105
+
106
+ **Options:**
107
+ - `-ph, --phase <release phase>` - Specify a release phase (default: `release`)
108
+ - Available phases: `alpha`, `beta`, `rc`, `release`, `stage`, `passive`, `withdrawn`
109
+ - `-e, --env <environment>` - CLI target environment
110
+ - Available environments: `lucky`, `wynni`, `production` (default)
111
+ - `--token <API-token>` - API Access Token
112
+
113
+ **Examples:**
114
+ ```console
115
+ $ studio-cli package
116
+ $ studio-cli pack --phase alpha
117
+ $ studio-cli pack --env wynni
118
+ ```
119
+
120
+ ### usb
121
+ USB device management commands.
122
+
123
+ #### usb list
124
+ List available USB devices.
125
+
126
+ ```console
127
+ $ studio-cli usb list
128
+ ```
129
+
130
+ #### usb upload
131
+ Upload an AMX binary to a USB device.
132
+
133
+ ```console
134
+ $ studio-cli usb upload -s <serial> -f <path> [--debug]
135
+ ```
136
+
137
+ **Options:**
138
+ - `-s, --serial <serial>` - Device serial number (16 characters hex) **[required]**
139
+ - `-f, --file <path>` - Path to the AMX file (relative to current directory) **[required]**
140
+ - `--debug` - Enable debug logging
141
+
142
+ **Example:**
143
+ ```console
144
+ $ studio-cli usb upload -s 1234567890ABCDEF -f ./app.amx
145
+ $ studio-cli usb upload -s 1234567890ABCDEF -f ./app.amx --debug
146
+ ```
147
+
27
148
  ## Development
28
149
 
29
150
  Execute commands from the root dir
package/out/main.js CHANGED
@@ -13,6 +13,7 @@ const update_notifier_1 = __importDefault(require("update-notifier"));
13
13
  const api_1 = require("./api");
14
14
  const log_1 = require("./log");
15
15
  const argumentParser_1 = require("./argumentParser");
16
+ const usb_1 = require("./usb");
16
17
  const program = new commander_1.Command();
17
18
  const notifier = (0, update_notifier_1.default)({ pkg: package_json_1.default });
18
19
  const logger = new log_1.Log.Logger();
@@ -145,6 +146,33 @@ program
145
146
  const { env, token } = getDefaultOptions(opts);
146
147
  main(command, (0, api_1.pack)(cwd, cliFS_1.cliFS, logger, env, token, opts.phase));
147
148
  });
149
+ // USB commands
150
+ const usbCommand = program.command('usb').description('USB device management commands');
151
+ usbCommand
152
+ .command('list')
153
+ .description('List available USB devices')
154
+ .action(async () => {
155
+ await (0, usb_1.printUsbDevices)(logger);
156
+ notifier.notify();
157
+ });
158
+ usbCommand
159
+ .command('upload')
160
+ .description('Upload an AMX binary to a USB device')
161
+ .requiredOption('-s, --serial <serial>', 'Device serial number (16 characters hex)')
162
+ .requiredOption('-f, --file <path>', 'Path to the AMX file (relative to current directory)')
163
+ .requiredOption('--debug', 'Enable debug logging', false)
164
+ .action(async (opts) => {
165
+ const fileUri = vscode_uri_1.Utils.joinPath(cwd, opts.file);
166
+ const result = await (0, usb_1.uploadToDevice)(opts.serial, fileUri, cliFS_1.cliFS, logger, opts.debug);
167
+ notifier.notify();
168
+ if (!result.success) {
169
+ logger.error(result.message);
170
+ process.exit(1);
171
+ }
172
+ else {
173
+ process.exit(0);
174
+ }
175
+ });
148
176
  module.exports = function (argv) {
149
177
  program.parse(argv);
150
178
  };
package/out/usb.js ADDED
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listUsbDevices = listUsbDevices;
4
+ exports.printUsbDevices = printUsbDevices;
5
+ exports.findDeviceBySerialNumber = findDeviceBySerialNumber;
6
+ exports.uploadToDevice = uploadToDevice;
7
+ const usb_1 = require("usb");
8
+ const uto_master_1 = require("uto-master");
9
+ const utoStack_1 = require("uto-master/dist/src/utilities/utoStack");
10
+ // Microtronics vendor ID
11
+ const MICROTRONICS_VENDOR_ID = 0x2544;
12
+ /**
13
+ * Custom WebUSB instance configured to allow Microtronics devices
14
+ */
15
+ const customWebUSB = new usb_1.WebUSB({
16
+ // Bypass checking for authorized devices
17
+ allowedDevices: [
18
+ {
19
+ vendorId: MICROTRONICS_VENDOR_ID
20
+ }
21
+ ]
22
+ });
23
+ /**
24
+ * Lists all available USB devices filtered by Microtronics vendor ID
25
+ * @returns Promise resolving to an array of USB device information
26
+ */
27
+ async function listUsbDevices() {
28
+ const devices = await customWebUSB.getDevices();
29
+ return devices.map((device) => ({
30
+ productName: device.productName,
31
+ serialNumber: device.serialNumber || device.productName?.split(' ').pop(),
32
+ vendorId: device.vendorId,
33
+ productId: device.productId
34
+ }));
35
+ }
36
+ /**
37
+ * Prints USB device product names using the provided logger
38
+ * @param logger - The logger instance to use for output
39
+ */
40
+ async function printUsbDevices(logger) {
41
+ const devices = await listUsbDevices();
42
+ if (devices.length === 0) {
43
+ logger.info('No USB devices found.');
44
+ return;
45
+ }
46
+ logger.info('Available USB devices:');
47
+ devices.forEach((device, index) => {
48
+ const name = device.productName ||
49
+ `Unknown Device (VID: 0x${device.vendorId.toString(16)}, PID: 0x${device.productId.toString(16)})`;
50
+ const serial = device.serialNumber ? ` [${device.serialNumber}]` : '';
51
+ logger.log(` ${index + 1}. ${name}${serial}`);
52
+ });
53
+ }
54
+ /**
55
+ * Find a USB device by its serial number
56
+ * @param serialNumber - The 16-character hex serial number of the device
57
+ * @returns The USB device or null if not found
58
+ */
59
+ async function findDeviceBySerialNumber(serialNumber) {
60
+ const devices = await customWebUSB.getDevices();
61
+ for (const device of devices) {
62
+ // Check direct serial number or serial from product name
63
+ const deviceSerial = device.serialNumber || device.productName?.split(' ').pop();
64
+ if (deviceSerial?.toLowerCase() === serialNumber.toLowerCase()) {
65
+ return device;
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * Upload an AMX binary file to a USB device
72
+ * @param serialNumber - The serial number of the target device
73
+ * @param fileUri - The URI to the AMX file
74
+ * @param fs - The filesystem interface for file operations
75
+ * @param logger - The logger instance for output
76
+ * @param debugLog - Enable debug logging
77
+ * @returns Promise resolving to an UploadResult
78
+ */
79
+ async function uploadToDevice(serialNumber, fileUri, fs, logger, debugLog = false) {
80
+ // Check if file exists
81
+ const fileStat = await fs.stat(fileUri);
82
+ if (!fileStat) {
83
+ return {
84
+ success: false,
85
+ message: `File not found: ${fileUri.fsPath}`
86
+ };
87
+ }
88
+ // Find the device by serial number
89
+ logger.info(`Searching for device with serial number: ${serialNumber}`);
90
+ const device = await findDeviceBySerialNumber(serialNumber);
91
+ if (!device) {
92
+ return {
93
+ success: false,
94
+ message: `Device not found with serial number: ${serialNumber}`
95
+ };
96
+ }
97
+ logger.info(`Device found: ${device.productName}`);
98
+ // Read the file contents
99
+ const fileData = (await fs.readFile(fileUri));
100
+ // Initialize UTO Master
101
+ const utoMaster = new uto_master_1.UtoMaster();
102
+ let deviceConnected = false;
103
+ try {
104
+ await utoMaster.init();
105
+ // Set up UTO transmit callback
106
+ utoMaster.utoTx(async (data) => {
107
+ if (device.opened) {
108
+ await device.transferOut(uto_master_1.USB_CONFIGURATION.utoEndpoint, data);
109
+ }
110
+ });
111
+ if (debugLog) {
112
+ utoMaster.on('mdnCom', (message) => {
113
+ logger.log('mdnCom', message);
114
+ });
115
+ utoMaster.on('mdnComError', (message) => {
116
+ logger.log('mdnComError', message);
117
+ });
118
+ }
119
+ // Set up progress reporting
120
+ let lastProgress = -1;
121
+ utoMaster.on('installing', (data) => {
122
+ if (!data) {
123
+ return;
124
+ }
125
+ // /*const progress = Math.round(data.progress);
126
+ if (data !== lastProgress) {
127
+ lastProgress = data;
128
+ logger.info(`Upload progress: ${data}%`);
129
+ }
130
+ });
131
+ // Open device and claim interfaces
132
+ await device.open();
133
+ await device.selectConfiguration(uto_master_1.USB_CONFIGURATION.configurationNumber);
134
+ await device.claimInterface(uto_master_1.USB_CONFIGURATION.utoInterface);
135
+ deviceConnected = true;
136
+ logger.info('Device connected, starting upload...');
137
+ // Start UTO polling for receiving data
138
+ startUtoPolling(device, utoMaster, () => deviceConnected);
139
+ await utoMaster.requestDeviceInfo();
140
+ // Upload the file
141
+ await utoMaster.sendFile(utoStack_1.UtoFileType.pawn, new Uint8Array(fileData.buffer, fileData.byteOffset, fileData.length));
142
+ logger.done('Upload completed successfully!');
143
+ return {
144
+ success: true,
145
+ message: 'File uploaded successfully'
146
+ };
147
+ }
148
+ catch (error) {
149
+ const errorMessage = error instanceof Error ? error.message : String(error);
150
+ return {
151
+ success: false,
152
+ message: `Upload failed: ${errorMessage}`
153
+ };
154
+ }
155
+ finally {
156
+ // Clean up - close device
157
+ deviceConnected = false;
158
+ if (device.opened) {
159
+ try {
160
+ await device.close().catch();
161
+ }
162
+ catch {
163
+ // Ignore close errors
164
+ }
165
+ }
166
+ }
167
+ }
168
+ /**
169
+ * Start polling for UTO data from the device
170
+ * @param device - The USB device
171
+ * @param utoMaster - The UTO Master instance
172
+ * @param isConnected - Function that returns connection status
173
+ */
174
+ async function startUtoPolling(device, utoMaster, isConnected) {
175
+ while (isConnected()) {
176
+ try {
177
+ const result = await device.transferIn(uto_master_1.USB_CONFIGURATION.utoEndpoint, uto_master_1.USB_CONFIGURATION.utoSize);
178
+ if (result.status === 'ok' && result.data) {
179
+ const uint8Data = new Uint8Array(result.data.buffer);
180
+ await utoMaster.utoRx(uint8Data);
181
+ }
182
+ }
183
+ catch {
184
+ // Connection lost or polling stopped
185
+ break;
186
+ }
187
+ }
188
+ }
189
+ //# sourceMappingURL=usb.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microtronics/studio-cli",
3
- "version": "0.42.0",
3
+ "version": "0.44.0",
4
4
  "description": "Microtronics Studio CLI Tool",
5
5
  "main": "./out/api.js",
6
6
  "typings": "./dist/studio-cli.d.ts",
@@ -45,6 +45,8 @@
45
45
  "semver": "^7.7.2",
46
46
  "tinytar-fix": "^0.1.1",
47
47
  "update-notifier": "^7.3.1",
48
+ "usb": "^2.14.0",
49
+ "uto-master": "bitbucket:microtronics-core/wasm-uto-master",
48
50
  "vscode-uri": "^3.0.8",
49
51
  "yaml": "^2.6.1"
50
52
  },