@microtronics/studio-cli 0.42.0 → 0.43.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,27 @@
1
1
  # Changelog
2
2
 
3
- ## 0.42.0 (2026-01-13)
3
+ ## 0.43.0 (2026-01-19)
4
4
 
5
5
  ### Features
6
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))
7
+ * add debug option to enable mdnCom logging ([34dfd29](https://bitbucket.org/microtronics-core/vscode-studio/commits/34dfd29cf2f5e6776e31463a85f0484a6907b13b))
8
+ * add support for uploading any amx file to an usb device ([e89f108](https://bitbucket.org/microtronics-core/vscode-studio/commits/e89f1087e22e6c0cc88c8b68bbbcc4f1d813dc4f))
9
+ * add USB device detection and listing functionality ([906e3e1](https://bitbucket.org/microtronics-core/vscode-studio/commits/906e3e12bca647f5ceba8b4196f00d507be163d9))
10
+ * 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)
9
11
 
10
12
  ### Bug Fixes
11
13
 
14
+ * ensure proper exit code on successful USB upload ([cc1ae63](https://bitbucket.org/microtronics-core/vscode-studio/commits/cc1ae632669e785017c2afaa689c3750ba22d7fb))
15
+
16
+ ## 0.42.0 (2026-01-13)
17
+
18
+ ### Features
19
+
20
+ * add isIotApp helper function to check IoT app project type ([98ce8a1](https://bitbucket.org/microtronics-core/vscode-studio/commits/98ce8a1358bdd4d2ba1635cd4ef1a3f6e8ec0004))
21
+ * add POST endpoint for addon timeseries data insertion ([ace6880](https://bitbucket.org/microtronics-core/vscode-studio/commits/ace6880f801faefc303a8020c7acf87b06db5709))
22
+
23
+ ### Bug Fixes
24
+
12
25
  * restrict write access rejection to IoT apps only ([13aa98f](https://bitbucket.org/microtronics-core/vscode-studio/commits/13aa98f261632b576681a894ecb375b7ab5a4b99))
13
26
 
14
27
  ## 0.41.0 (2026-01-12)
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.43.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
  },