@microtronics/studio-cli 0.44.0 → 0.45.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 +7 -0
- package/README.md +51 -0
- package/dist/studio-cli.d.ts +165 -0
- package/out/api.js +7 -1
- package/out/main.js +75 -3
- package/out/myDatanetClient.js +286 -0
- package/out/run.js +254 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.45.0 (2026-02-04)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* add development deployment workflow with USB and myDatanet integration ([0503666](https://bitbucket.org/microtronics-core/vscode-studio/commits/05036666d0a80f419b76ed4140037e11281d0621))
|
|
8
|
+
* add VSCode extension support to MyDatanetClient ([67866fc](https://bitbucket.org/microtronics-core/vscode-studio/commits/67866fc2ab069a108df4594124fcb38b13ca1b68))
|
|
9
|
+
|
|
3
10
|
## 0.44.0 (2026-01-19)
|
|
4
11
|
|
|
5
12
|
## 0.43.0 (2026-01-19)
|
package/README.md
CHANGED
|
@@ -117,6 +117,57 @@ $ studio-cli pack --phase alpha
|
|
|
117
117
|
$ studio-cli pack --env wynni
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
### run
|
|
121
|
+
Build, upload to USB device, and create development site on myDatanet. This command is designed for development and integration testing purposes.
|
|
122
|
+
|
|
123
|
+
```console
|
|
124
|
+
$ studio-cli run -s <serial> --server <url> [options]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Options:**
|
|
128
|
+
- `-s, --serial <serial>` - Device serial number (16 characters hex) **[required]**
|
|
129
|
+
- `--server <url>` - myDatanet instance URL (https:// prefix optional). Defaults to `MYDATANET_HOST` environment variable
|
|
130
|
+
- `--customer <customerId>` - myDatanet customer UID or ID. Defaults to `MYDATANET_CUSTOMER` environment variable
|
|
131
|
+
- `--mdn-token <token>` - myDatanet API token. Defaults to `MYDATANET_TOKEN` environment variable
|
|
132
|
+
- `-p, --part <APM part>` - APM part that should be built
|
|
133
|
+
- Available parts: `dde`, `dlo`, `pov`, `blo`, `dfiles`
|
|
134
|
+
- `-e, --env <environment>` - CLI target environment
|
|
135
|
+
- Available environments: `lucky`, `wynni`, `production` (default)
|
|
136
|
+
- `--token <API-token>` - Studio API Access Token
|
|
137
|
+
- `--debug` - Enable debug logging
|
|
138
|
+
|
|
139
|
+
**Exit Codes:**
|
|
140
|
+
- `0` - Success
|
|
141
|
+
- `1` - Build failed
|
|
142
|
+
- `2` - USB device not found
|
|
143
|
+
- `3` - USB upload failed
|
|
144
|
+
- `4` - myDatanet authentication failed
|
|
145
|
+
- `5` - Development tag upload failed
|
|
146
|
+
- `6` - Site creation failed
|
|
147
|
+
|
|
148
|
+
**Output:**
|
|
149
|
+
On success, the command outputs:
|
|
150
|
+
- Site UID of the newly created/reused development site
|
|
151
|
+
- APM ID of the development tag
|
|
152
|
+
|
|
153
|
+
**Examples:**
|
|
154
|
+
```console
|
|
155
|
+
# Basic usage with environment variables
|
|
156
|
+
$ export MYDATANET_TOKEN=your_token
|
|
157
|
+
$ export MYDATANET_HOST=myserver.mydatanet.net
|
|
158
|
+
$ export MYDATANET_CUSTOMER=your_customer_id
|
|
159
|
+
$ studio-cli run -s 1234567890ABCDEF
|
|
160
|
+
|
|
161
|
+
# Explicit options
|
|
162
|
+
$ studio-cli run -s 1234567890ABCDEF --server myserver.mydatanet.net --customer CUST123 --mdn-token your_token
|
|
163
|
+
|
|
164
|
+
# With build part selection
|
|
165
|
+
$ studio-cli run -s 1234567890ABCDEF --server myserver.mydatanet.net --part dlo --debug
|
|
166
|
+
|
|
167
|
+
# With manifest overrides
|
|
168
|
+
$ studio-cli run -s 1234567890ABCDEF --server myserver.mydatanet.net --dlo.mainFile="./dlo/test.dlo"
|
|
169
|
+
```
|
|
170
|
+
|
|
120
171
|
### usb
|
|
121
172
|
USB device management commands.
|
|
122
173
|
|
package/dist/studio-cli.d.ts
CHANGED
|
@@ -135,6 +135,11 @@ export declare function build(cwd: URI, fs: LocalFS, logger: Log.Logger, env: Gl
|
|
|
135
135
|
[APM.Part.dfiles]: Diagnostics.File[];
|
|
136
136
|
}>;
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Build development installation info from manifest and options
|
|
140
|
+
*/
|
|
141
|
+
export declare function buildDevelopmentInstallInfo(cwd: URI, fs: LocalFS, serialNumber: string, userName: string, customerUid: string | null): Promise<DevelopmentInstallInfo>;
|
|
142
|
+
|
|
138
143
|
declare interface components {
|
|
139
144
|
schemas: {
|
|
140
145
|
/** @default 25 */
|
|
@@ -1708,6 +1713,23 @@ export declare namespace Dependencies {
|
|
|
1708
1713
|
export function getApmPartDependencies(cwd: URI, fs: LocalFS, apmPart: APM.Part.dlo | APM.Part.dfiles): Promise<APMPartLibrary[]>;
|
|
1709
1714
|
}
|
|
1710
1715
|
|
|
1716
|
+
/**
|
|
1717
|
+
* Development installation information
|
|
1718
|
+
*/
|
|
1719
|
+
export declare interface DevelopmentInstallInfo {
|
|
1720
|
+
apmId: string;
|
|
1721
|
+
applicationId: string | null;
|
|
1722
|
+
name: string;
|
|
1723
|
+
version: number;
|
|
1724
|
+
serialNumber?: string;
|
|
1725
|
+
siteUid: string | null;
|
|
1726
|
+
siteName?: string;
|
|
1727
|
+
customerUid: string | null;
|
|
1728
|
+
customerName?: string;
|
|
1729
|
+
/** Whether a registration code is needed (used by VSCode extension) */
|
|
1730
|
+
registrationCodeNeeded?: boolean;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1711
1733
|
export declare const DeviceProfiles: any;
|
|
1712
1734
|
|
|
1713
1735
|
export declare namespace DFILES {
|
|
@@ -1947,6 +1969,11 @@ declare enum FileType {
|
|
|
1947
1969
|
SymbolicLink = 64
|
|
1948
1970
|
}
|
|
1949
1971
|
|
|
1972
|
+
/**
|
|
1973
|
+
* Generate a development APM ID based on a reference string
|
|
1974
|
+
*/
|
|
1975
|
+
export declare function generateDevApmId(developmentReference: string): string;
|
|
1976
|
+
|
|
1950
1977
|
export declare namespace Globals {
|
|
1951
1978
|
/**
|
|
1952
1979
|
* Supported ENV for the studio environment
|
|
@@ -2035,6 +2062,16 @@ export declare namespace Helper {
|
|
|
2035
2062
|
export function defer(token: string, timeout: number, callback: any): void;
|
|
2036
2063
|
}
|
|
2037
2064
|
|
|
2065
|
+
/**
|
|
2066
|
+
* HTTP methods supported by the myDatanet API
|
|
2067
|
+
*/
|
|
2068
|
+
export declare enum HttpMethod {
|
|
2069
|
+
Get = "GET",
|
|
2070
|
+
Post = "POST",
|
|
2071
|
+
Put = "PUT",
|
|
2072
|
+
Delete = "DELETE"
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2038
2075
|
/**
|
|
2039
2076
|
* Installs the specified dependencies or all dependencies if none are provided.
|
|
2040
2077
|
*
|
|
@@ -2311,6 +2348,134 @@ export declare namespace Manifest {
|
|
|
2311
2348
|
export function isApmPartEnabled(cwd: URI, fs: LocalFS, apmPart: APM.Part, manifest?: Manifest.Manifest): Promise<boolean>;
|
|
2312
2349
|
}
|
|
2313
2350
|
|
|
2351
|
+
/**
|
|
2352
|
+
* MyDatanet API client for CLI and extension operations
|
|
2353
|
+
*/
|
|
2354
|
+
export declare class MyDatanetClient {
|
|
2355
|
+
private domain;
|
|
2356
|
+
private auth;
|
|
2357
|
+
private logger?;
|
|
2358
|
+
private userInfo;
|
|
2359
|
+
/**
|
|
2360
|
+
* Create a new MyDatanetClient
|
|
2361
|
+
* @param serverUrl - Server URL (https:// prefix is optional)
|
|
2362
|
+
* @param apiTokenOrLogger - API token string OR Logger instance for backward compatibility
|
|
2363
|
+
* @param loggerOrUndefined - Logger instance (only when apiTokenOrLogger is a string)
|
|
2364
|
+
*/
|
|
2365
|
+
constructor(serverUrl: string, apiTokenOrLogger: string | Log.Logger, loggerOrUndefined?: Log.Logger);
|
|
2366
|
+
/**
|
|
2367
|
+
* Create a new MyDatanetClient with options
|
|
2368
|
+
* @param options - Client configuration options
|
|
2369
|
+
*/
|
|
2370
|
+
constructor(options: MyDatanetClientOptions);
|
|
2371
|
+
/**
|
|
2372
|
+
* Set or update the authentication header
|
|
2373
|
+
*/
|
|
2374
|
+
setAuth(authHeader: string): void;
|
|
2375
|
+
/**
|
|
2376
|
+
* Get the current domain
|
|
2377
|
+
*/
|
|
2378
|
+
getDomain(): string;
|
|
2379
|
+
/**
|
|
2380
|
+
* Normalize server URL to ensure https:// prefix
|
|
2381
|
+
*/
|
|
2382
|
+
private normalizeServerUrl;
|
|
2383
|
+
/**
|
|
2384
|
+
* Remove protocol from domain for display purposes
|
|
2385
|
+
*/
|
|
2386
|
+
private trimDomain;
|
|
2387
|
+
/**
|
|
2388
|
+
* Make an authenticated JSON request to the myDatanet API
|
|
2389
|
+
*/
|
|
2390
|
+
fetchJSON<T>(method: HttpMethod, path: string, body?: any): Promise<T>;
|
|
2391
|
+
/**
|
|
2392
|
+
* Authenticate and get user information
|
|
2393
|
+
*/
|
|
2394
|
+
authenticate(): Promise<MyDatanetUserInfo>;
|
|
2395
|
+
/**
|
|
2396
|
+
* Get the current user info (must call authenticate first)
|
|
2397
|
+
*/
|
|
2398
|
+
getUserInfo(): MyDatanetUserInfo | null;
|
|
2399
|
+
/**
|
|
2400
|
+
* Upload a development APM tag to the app-center
|
|
2401
|
+
*/
|
|
2402
|
+
uploadDevTag(apmId: string, apmBuffer: FormData): Promise<string>;
|
|
2403
|
+
/**
|
|
2404
|
+
* Get customer information by UID or name
|
|
2405
|
+
*/
|
|
2406
|
+
getCustomer(customerIdOrUid: string): Promise<MyDatanetCustomer>;
|
|
2407
|
+
/**
|
|
2408
|
+
* Get all customers accessible by the current user
|
|
2409
|
+
*/
|
|
2410
|
+
getCustomers(): Promise<MyDatanetCustomer[]>;
|
|
2411
|
+
/**
|
|
2412
|
+
* Get device site information
|
|
2413
|
+
*/
|
|
2414
|
+
getDeviceSite(serialNumber: string): Promise<{
|
|
2415
|
+
app: {
|
|
2416
|
+
phase: string;
|
|
2417
|
+
apmid: string;
|
|
2418
|
+
appl_id: string;
|
|
2419
|
+
};
|
|
2420
|
+
_uid: string;
|
|
2421
|
+
name: string;
|
|
2422
|
+
customer_uid: string;
|
|
2423
|
+
} | null>;
|
|
2424
|
+
/**
|
|
2425
|
+
* Get device information
|
|
2426
|
+
*/
|
|
2427
|
+
getDevice(serialNumber: string): Promise<{
|
|
2428
|
+
customer_uid: string;
|
|
2429
|
+
} | null>;
|
|
2430
|
+
/**
|
|
2431
|
+
* Get or create a site by name within a customer
|
|
2432
|
+
*/
|
|
2433
|
+
getSite(customerUid: string, siteName: string): Promise<{
|
|
2434
|
+
_uid: string;
|
|
2435
|
+
name: string;
|
|
2436
|
+
} | null>;
|
|
2437
|
+
/**
|
|
2438
|
+
* Create a development site
|
|
2439
|
+
*/
|
|
2440
|
+
createDevelopmentSite(customerUid: string, installInfo: DevelopmentInstallInfo, registrationCode?: string): Promise<string>;
|
|
2441
|
+
/**
|
|
2442
|
+
* Update site name
|
|
2443
|
+
*/
|
|
2444
|
+
updateSiteName(siteUid: string, name: string): Promise<void>;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
/**
|
|
2448
|
+
* Options for creating a MyDatanetClient
|
|
2449
|
+
*/
|
|
2450
|
+
export declare interface MyDatanetClientOptions {
|
|
2451
|
+
/** Server URL (https:// prefix is optional) */
|
|
2452
|
+
serverUrl: string;
|
|
2453
|
+
/** API token for Bearer authentication */
|
|
2454
|
+
apiToken?: string;
|
|
2455
|
+
/** Pre-formatted authentication header (e.g., "Basic 1-xxx" or "Bearer xxx") */
|
|
2456
|
+
authHeader?: string;
|
|
2457
|
+
/** Logger instance (optional) */
|
|
2458
|
+
logger?: Log.Logger;
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2461
|
+
/**
|
|
2462
|
+
* Customer information from myDatanet
|
|
2463
|
+
*/
|
|
2464
|
+
export declare interface MyDatanetCustomer {
|
|
2465
|
+
_uid: string;
|
|
2466
|
+
name: string;
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
/**
|
|
2470
|
+
* User information returned from myDatanet /api/1/me endpoint
|
|
2471
|
+
*/
|
|
2472
|
+
export declare interface MyDatanetUserInfo {
|
|
2473
|
+
name: string;
|
|
2474
|
+
email: string;
|
|
2475
|
+
group?: number;
|
|
2476
|
+
customers?: MyDatanetCustomer[];
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2314
2479
|
/**
|
|
2315
2480
|
* Packs the given directory into a deployable package using the specified configuration.
|
|
2316
2481
|
*
|
package/out/api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Log = exports.ENV = exports.DFILES = exports.Library = exports.APM = exports.Package = exports.DLO = exports.Globals = exports.DeviceProfiles = exports.DefaultFiles = exports.Diagnostics = exports.Helper = exports.DDE = exports.DDE_Core = exports.Registry = exports.Manifest = exports.Dependencies = void 0;
|
|
3
|
+
exports.buildDevelopmentInstallInfo = exports.generateDevApmId = exports.HttpMethod = exports.MyDatanetClient = exports.Log = exports.ENV = exports.DFILES = exports.Library = exports.APM = exports.Package = exports.DLO = exports.Globals = exports.DeviceProfiles = exports.DefaultFiles = exports.Diagnostics = exports.Helper = exports.DDE = exports.DDE_Core = exports.Registry = exports.Manifest = exports.Dependencies = void 0;
|
|
4
4
|
exports.install = install;
|
|
5
5
|
exports.build = build;
|
|
6
6
|
exports.publish = publish;
|
|
@@ -134,4 +134,10 @@ async function publish(cwd, fs, logger, env, apiToken, optsAllowedBackends, opts
|
|
|
134
134
|
async function pack(cwd, fs, logger, env, apiToken, optsPhase) {
|
|
135
135
|
return package_1.Package.pack(cwd, fs, logger, env, apiToken, optsPhase);
|
|
136
136
|
}
|
|
137
|
+
// Re-export myDatanet client types and helpers for external use
|
|
138
|
+
var myDatanetClient_1 = require("./myDatanetClient");
|
|
139
|
+
Object.defineProperty(exports, "MyDatanetClient", { enumerable: true, get: function () { return myDatanetClient_1.MyDatanetClient; } });
|
|
140
|
+
Object.defineProperty(exports, "HttpMethod", { enumerable: true, get: function () { return myDatanetClient_1.HttpMethod; } });
|
|
141
|
+
Object.defineProperty(exports, "generateDevApmId", { enumerable: true, get: function () { return myDatanetClient_1.generateDevApmId; } });
|
|
142
|
+
Object.defineProperty(exports, "buildDevelopmentInstallInfo", { enumerable: true, get: function () { return myDatanetClient_1.buildDevelopmentInstallInfo; } });
|
|
137
143
|
//# sourceMappingURL=api.js.map
|
package/out/main.js
CHANGED
|
@@ -14,6 +14,7 @@ const api_1 = require("./api");
|
|
|
14
14
|
const log_1 = require("./log");
|
|
15
15
|
const argumentParser_1 = require("./argumentParser");
|
|
16
16
|
const usb_1 = require("./usb");
|
|
17
|
+
const run_1 = require("./run");
|
|
17
18
|
const program = new commander_1.Command();
|
|
18
19
|
const notifier = (0, update_notifier_1.default)({ pkg: package_json_1.default });
|
|
19
20
|
const logger = new log_1.Log.Logger();
|
|
@@ -98,6 +99,30 @@ const releasePhaseArguments = Object.values(apm_1.APM.TagPhase);
|
|
|
98
99
|
const optionReleasePhase = new commander_1.Option('-ph --phase <release phase>', 'specify a release phase')
|
|
99
100
|
.choices(releasePhaseArguments)
|
|
100
101
|
.default('release');
|
|
102
|
+
/**
|
|
103
|
+
* Option for specifying the APM part to build
|
|
104
|
+
*/
|
|
105
|
+
const optionApmPart = new commander_1.Option('-p --part <APM part>', 'APM part that should be built').choices(Object.values(apm_1.APM.Part));
|
|
106
|
+
/**
|
|
107
|
+
* Option for device serial number (16 characters hex)
|
|
108
|
+
*/
|
|
109
|
+
const optionDeviceSerial = new commander_1.Option('-s, --serial <serial>', 'Device serial number (16 characters hex)');
|
|
110
|
+
/**
|
|
111
|
+
* Option for enabling debug logging
|
|
112
|
+
*/
|
|
113
|
+
const optionDebug = new commander_1.Option('--debug', 'Enable debug logging').default(false);
|
|
114
|
+
/**
|
|
115
|
+
* Option for myDatanet instance URL
|
|
116
|
+
*/
|
|
117
|
+
const optionMdnServer = new commander_1.Option('--server <url>', 'myDatanet instance URL (https:// prefix optional)').default(process.env['MYDATANET_HOST']);
|
|
118
|
+
/**
|
|
119
|
+
* Option for myDatanet customer UID or ID
|
|
120
|
+
*/
|
|
121
|
+
const optionMdnCustomer = new commander_1.Option('--customer <customerId>', 'myDatanet customer UID or ID').default(process.env['MYDATANET_CUSTOMER']);
|
|
122
|
+
/**
|
|
123
|
+
* Option for myDatanet API token
|
|
124
|
+
*/
|
|
125
|
+
const optionMdnToken = new commander_1.Option('--mdn-token <token>', 'myDatanet API token').default(process.env['MYDATANET_TOKEN']);
|
|
101
126
|
program
|
|
102
127
|
.command('install')
|
|
103
128
|
.aliases(['i'])
|
|
@@ -113,7 +138,7 @@ program
|
|
|
113
138
|
});
|
|
114
139
|
program
|
|
115
140
|
.command('build')
|
|
116
|
-
.addOption(
|
|
141
|
+
.addOption(optionApmPart)
|
|
117
142
|
.allowExcessArguments(true)
|
|
118
143
|
.addOption(optionStudioEnv)
|
|
119
144
|
.addOption(optionStudioToken)
|
|
@@ -146,6 +171,53 @@ program
|
|
|
146
171
|
const { env, token } = getDefaultOptions(opts);
|
|
147
172
|
main(command, (0, api_1.pack)(cwd, cliFS_1.cliFS, logger, env, token, opts.phase));
|
|
148
173
|
});
|
|
174
|
+
// Run command - Build, upload to device, and create development site
|
|
175
|
+
program
|
|
176
|
+
.command('run')
|
|
177
|
+
.description('Build, upload to USB device, and create development site on myDatanet')
|
|
178
|
+
.addOption(optionDeviceSerial.makeOptionMandatory(true))
|
|
179
|
+
.addOption(optionMdnServer)
|
|
180
|
+
.addOption(optionMdnCustomer)
|
|
181
|
+
.addOption(optionMdnToken)
|
|
182
|
+
.addOption(optionApmPart)
|
|
183
|
+
.addOption(optionStudioEnv)
|
|
184
|
+
.addOption(optionStudioToken)
|
|
185
|
+
.addOption(optionDebug)
|
|
186
|
+
.allowUnknownOption(true)
|
|
187
|
+
.action(async (opts, command) => {
|
|
188
|
+
const { env, token } = getDefaultOptions(opts);
|
|
189
|
+
const mdnToken = opts.mdnToken;
|
|
190
|
+
const server = opts.server;
|
|
191
|
+
// Validate required options
|
|
192
|
+
if (!mdnToken) {
|
|
193
|
+
logger.error('MYDATANET_TOKEN is required. Set via --mdn-token or MYDATANET_TOKEN environment variable');
|
|
194
|
+
notifier.notify();
|
|
195
|
+
process.exit(run_1.RunExitCode.AuthenticationFailed);
|
|
196
|
+
}
|
|
197
|
+
if (!server) {
|
|
198
|
+
logger.error('Server URL is required. Set via --server or MYDATANET_HOST environment variable');
|
|
199
|
+
notifier.notify();
|
|
200
|
+
process.exit(run_1.RunExitCode.AuthenticationFailed);
|
|
201
|
+
}
|
|
202
|
+
const manifestOptions = (0, argumentParser_1.parseUnknownLongOptions)(command);
|
|
203
|
+
api_1.Manifest.setOverwrites(manifestOptions);
|
|
204
|
+
const result = await (0, run_1.run)(cwd, cliFS_1.cliFS, logger, env, token, {
|
|
205
|
+
serial: opts.serial,
|
|
206
|
+
server: server,
|
|
207
|
+
customer: opts.customer,
|
|
208
|
+
mdnToken: mdnToken,
|
|
209
|
+
debug: opts.debug,
|
|
210
|
+
part: opts.part ? [opts.part] : undefined
|
|
211
|
+
});
|
|
212
|
+
notifier.notify();
|
|
213
|
+
if (result.success) {
|
|
214
|
+
process.exit(run_1.RunExitCode.Success);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
logger.error(result.message);
|
|
218
|
+
process.exit(result.errorCode || 1);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
149
221
|
// USB commands
|
|
150
222
|
const usbCommand = program.command('usb').description('USB device management commands');
|
|
151
223
|
usbCommand
|
|
@@ -158,9 +230,9 @@ usbCommand
|
|
|
158
230
|
usbCommand
|
|
159
231
|
.command('upload')
|
|
160
232
|
.description('Upload an AMX binary to a USB device')
|
|
161
|
-
.
|
|
233
|
+
.addOption(optionDeviceSerial.makeOptionMandatory(true))
|
|
162
234
|
.requiredOption('-f, --file <path>', 'Path to the AMX file (relative to current directory)')
|
|
163
|
-
.
|
|
235
|
+
.addOption(optionDebug)
|
|
164
236
|
.action(async (opts) => {
|
|
165
237
|
const fileUri = vscode_uri_1.Utils.joinPath(cwd, opts.file);
|
|
166
238
|
const result = await (0, usb_1.uploadToDevice)(opts.serial, fileUri, cliFS_1.cliFS, logger, opts.debug);
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MyDatanetClient = exports.RunExitCode = exports.HttpMethod = void 0;
|
|
4
|
+
exports.generateDevApmId = generateDevApmId;
|
|
5
|
+
exports.buildDevelopmentInstallInfo = buildDevelopmentInstallInfo;
|
|
6
|
+
const manifest_1 = require("./manifest");
|
|
7
|
+
const apm_1 = require("./package/apm");
|
|
8
|
+
const uuid_1 = require("uuid");
|
|
9
|
+
// Used to generate reproducible apmids
|
|
10
|
+
const UUID_NAMESPACE = 'b4baec47-0ba4-4e37-b237-8f9cde42b8dd';
|
|
11
|
+
/**
|
|
12
|
+
* HTTP methods supported by the myDatanet API
|
|
13
|
+
*/
|
|
14
|
+
var HttpMethod;
|
|
15
|
+
(function (HttpMethod) {
|
|
16
|
+
HttpMethod["Get"] = "GET";
|
|
17
|
+
HttpMethod["Post"] = "POST";
|
|
18
|
+
HttpMethod["Put"] = "PUT";
|
|
19
|
+
HttpMethod["Delete"] = "DELETE";
|
|
20
|
+
})(HttpMethod || (exports.HttpMethod = HttpMethod = {}));
|
|
21
|
+
/**
|
|
22
|
+
* Exit codes for the run command
|
|
23
|
+
*/
|
|
24
|
+
var RunExitCode;
|
|
25
|
+
(function (RunExitCode) {
|
|
26
|
+
RunExitCode[RunExitCode["Success"] = 0] = "Success";
|
|
27
|
+
RunExitCode[RunExitCode["BuildFailed"] = 1] = "BuildFailed";
|
|
28
|
+
RunExitCode[RunExitCode["DeviceNotFound"] = 2] = "DeviceNotFound";
|
|
29
|
+
RunExitCode[RunExitCode["UsbUploadFailed"] = 3] = "UsbUploadFailed";
|
|
30
|
+
RunExitCode[RunExitCode["AuthenticationFailed"] = 4] = "AuthenticationFailed";
|
|
31
|
+
RunExitCode[RunExitCode["DevTagUploadFailed"] = 5] = "DevTagUploadFailed";
|
|
32
|
+
RunExitCode[RunExitCode["SiteCreationFailed"] = 6] = "SiteCreationFailed";
|
|
33
|
+
})(RunExitCode || (exports.RunExitCode = RunExitCode = {}));
|
|
34
|
+
/**
|
|
35
|
+
* MyDatanet API client for CLI and extension operations
|
|
36
|
+
*/
|
|
37
|
+
class MyDatanetClient {
|
|
38
|
+
domain;
|
|
39
|
+
auth;
|
|
40
|
+
logger;
|
|
41
|
+
userInfo = null;
|
|
42
|
+
constructor(serverUrlOrOptions, apiTokenOrLogger, loggerOrUndefined) {
|
|
43
|
+
if (typeof serverUrlOrOptions === 'object') {
|
|
44
|
+
// New options-based constructor
|
|
45
|
+
const { serverUrl, apiToken, authHeader, logger } = serverUrlOrOptions;
|
|
46
|
+
this.domain = this.normalizeServerUrl(serverUrl);
|
|
47
|
+
this.auth = authHeader || (apiToken ? `Bearer ${apiToken}` : '');
|
|
48
|
+
this.logger = logger;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Legacy constructor for backward compatibility
|
|
52
|
+
this.domain = this.normalizeServerUrl(serverUrlOrOptions);
|
|
53
|
+
if (typeof apiTokenOrLogger === 'string') {
|
|
54
|
+
this.auth = `Bearer ${apiTokenOrLogger}`;
|
|
55
|
+
this.logger = loggerOrUndefined;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
this.auth = '';
|
|
59
|
+
this.logger = apiTokenOrLogger;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Set or update the authentication header
|
|
65
|
+
*/
|
|
66
|
+
setAuth(authHeader) {
|
|
67
|
+
this.auth = authHeader;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the current domain
|
|
71
|
+
*/
|
|
72
|
+
getDomain() {
|
|
73
|
+
return this.domain;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Normalize server URL to ensure https:// prefix
|
|
77
|
+
*/
|
|
78
|
+
normalizeServerUrl(url) {
|
|
79
|
+
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
80
|
+
return `https://${url}`;
|
|
81
|
+
}
|
|
82
|
+
return url;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Remove protocol from domain for display purposes
|
|
86
|
+
*/
|
|
87
|
+
trimDomain(domain) {
|
|
88
|
+
return domain.replace(/(^\w+:|^)\/\//, '');
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Make an authenticated JSON request to the myDatanet API
|
|
92
|
+
*/
|
|
93
|
+
async fetchJSON(method, path, body) {
|
|
94
|
+
const url = `${this.domain}${path}`;
|
|
95
|
+
const headers = {
|
|
96
|
+
Authorization: this.auth
|
|
97
|
+
};
|
|
98
|
+
const options = {
|
|
99
|
+
method,
|
|
100
|
+
headers
|
|
101
|
+
};
|
|
102
|
+
if (body) {
|
|
103
|
+
if (body instanceof FormData) {
|
|
104
|
+
options.body = body;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
headers['Content-Type'] = 'application/json';
|
|
108
|
+
options.body = JSON.stringify(body);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const response = await fetch(url, options);
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
114
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
115
|
+
}
|
|
116
|
+
return response.json();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Authenticate and get user information
|
|
120
|
+
*/
|
|
121
|
+
async authenticate() {
|
|
122
|
+
this.logger?.info(`Authenticating with ${this.trimDomain(this.domain)}...`);
|
|
123
|
+
try {
|
|
124
|
+
const { user, customers } = await this.fetchJSON(HttpMethod.Get, '/api/1/me');
|
|
125
|
+
this.userInfo = {
|
|
126
|
+
name: user.name,
|
|
127
|
+
email: user.email,
|
|
128
|
+
group: user.group,
|
|
129
|
+
customers: customers
|
|
130
|
+
};
|
|
131
|
+
this.logger?.info(`Authenticated as ${this.userInfo.name}`);
|
|
132
|
+
return this.userInfo;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
throw new Error(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the current user info (must call authenticate first)
|
|
140
|
+
*/
|
|
141
|
+
getUserInfo() {
|
|
142
|
+
return this.userInfo;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Upload a development APM tag to the app-center
|
|
146
|
+
*/
|
|
147
|
+
async uploadDevTag(apmId, apmBuffer) {
|
|
148
|
+
this.logger?.info(`Uploading development tag ${apmId}...`);
|
|
149
|
+
const { applid } = await this.fetchJSON(HttpMethod.Post, `/api/1/app-center/__dev/${apmId}`, apmBuffer);
|
|
150
|
+
this.logger?.info(`Development tag uploaded, application ID: ${applid}`);
|
|
151
|
+
return applid;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get customer information by UID or name
|
|
155
|
+
*/
|
|
156
|
+
async getCustomer(customerIdOrUid) {
|
|
157
|
+
const { _uid, name } = await this.fetchJSON(HttpMethod.Get, `/api/1/customers/${customerIdOrUid}`);
|
|
158
|
+
return { _uid, name };
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get all customers accessible by the current user
|
|
162
|
+
*/
|
|
163
|
+
async getCustomers() {
|
|
164
|
+
return this.fetchJSON(HttpMethod.Get, '/api/1/customers');
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get device site information
|
|
168
|
+
*/
|
|
169
|
+
async getDeviceSite(serialNumber) {
|
|
170
|
+
try {
|
|
171
|
+
return await this.fetchJSON(HttpMethod.Get, `/api/1/devices/${serialNumber}/site`);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get device information
|
|
179
|
+
*/
|
|
180
|
+
async getDevice(serialNumber) {
|
|
181
|
+
try {
|
|
182
|
+
return await this.fetchJSON(HttpMethod.Get, `/api/1/devices/${serialNumber}`);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get or create a site by name within a customer
|
|
190
|
+
*/
|
|
191
|
+
async getSite(customerUid, siteName) {
|
|
192
|
+
try {
|
|
193
|
+
return await this.fetchJSON(HttpMethod.Get, `/api/1/customers/${customerUid}/sites/${siteName}`);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Create a development site
|
|
201
|
+
*/
|
|
202
|
+
async createDevelopmentSite(customerUid, installInfo, registrationCode) {
|
|
203
|
+
this.logger?.info(`Creating development site "${installInfo.name}"...`);
|
|
204
|
+
// Check if device is already attached to a site
|
|
205
|
+
if (installInfo.serialNumber) {
|
|
206
|
+
const existingSite = await this.getSite(customerUid, installInfo.name.toLowerCase());
|
|
207
|
+
if (existingSite) {
|
|
208
|
+
// Try to attach device to existing site
|
|
209
|
+
try {
|
|
210
|
+
await this.fetchJSON(HttpMethod.Post, `/api/1/customers/${customerUid}/sites/${existingSite._uid}/cn-attach-device`, {
|
|
211
|
+
device_id: installInfo.serialNumber,
|
|
212
|
+
allow_overwrite: true,
|
|
213
|
+
detach_from_old_site: true,
|
|
214
|
+
acquire: true,
|
|
215
|
+
create: true
|
|
216
|
+
});
|
|
217
|
+
this.logger?.info(`Device attached to existing site ${existingSite._uid}`);
|
|
218
|
+
return existingSite._uid;
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
// 304 means device is already attached - that's OK
|
|
222
|
+
if (error.message && !error.message.includes('304')) {
|
|
223
|
+
this.logger?.warn(`Could not attach device to existing site: ${error.message}`);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
return existingSite._uid;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Create new site
|
|
232
|
+
const { _uid } = await this.fetchJSON(HttpMethod.Post, `/api/1/customers/${customerUid}/sites`, {
|
|
233
|
+
name: installInfo.name,
|
|
234
|
+
create: true,
|
|
235
|
+
force: true,
|
|
236
|
+
note: 'This is a Studio development site',
|
|
237
|
+
applid: installInfo.applicationId,
|
|
238
|
+
device_id: installInfo.serialNumber,
|
|
239
|
+
reg_code: registrationCode,
|
|
240
|
+
servicetariff: '0000000000000000',
|
|
241
|
+
servicetariff_indiv_cost: 0
|
|
242
|
+
});
|
|
243
|
+
this.logger?.info(`Development site created: ${_uid}`);
|
|
244
|
+
return _uid;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Update site name
|
|
248
|
+
*/
|
|
249
|
+
async updateSiteName(siteUid, name) {
|
|
250
|
+
try {
|
|
251
|
+
await this.fetchJSON(HttpMethod.Put, `/api/1/sites/${siteUid}`, { name });
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Ignore update failures
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
exports.MyDatanetClient = MyDatanetClient;
|
|
259
|
+
/**
|
|
260
|
+
* Generate a development APM ID based on a reference string
|
|
261
|
+
*/
|
|
262
|
+
function generateDevApmId(developmentReference) {
|
|
263
|
+
return (0, uuid_1.v5)(developmentReference, UUID_NAMESPACE);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Build development installation info from manifest and options
|
|
267
|
+
*/
|
|
268
|
+
async function buildDevelopmentInstallInfo(cwd, fs, serialNumber, userName, customerUid) {
|
|
269
|
+
const manifest = await manifest_1.Manifest.read(cwd, fs);
|
|
270
|
+
const currentUserId = userName.replaceAll(' ', '_').replace(/[^a-zA-Z0-9-]+/g, '');
|
|
271
|
+
const appName = manifest.name.replaceAll(' ', '_').replace(/[^a-zA-Z0-9-]+/g, '');
|
|
272
|
+
const hasDlo = await manifest_1.Manifest.isApmPartEnabled(cwd, fs, apm_1.APM.Part.dlo, manifest);
|
|
273
|
+
const useDeviceSerial = hasDlo && serialNumber;
|
|
274
|
+
const siteName = useDeviceSerial ? serialNumber.toUpperCase() : `${appName}_${currentUserId}`;
|
|
275
|
+
const developmentReference = useDeviceSerial ? serialNumber : siteName;
|
|
276
|
+
return {
|
|
277
|
+
apmId: generateDevApmId(developmentReference),
|
|
278
|
+
applicationId: null,
|
|
279
|
+
name: `Development_${siteName}`,
|
|
280
|
+
version: 1,
|
|
281
|
+
serialNumber: hasDlo ? serialNumber : undefined,
|
|
282
|
+
siteUid: null,
|
|
283
|
+
customerUid: customerUid
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=myDatanetClient.js.map
|
package/out/run.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RunExitCode = void 0;
|
|
4
|
+
exports.run = run;
|
|
5
|
+
const vscode_uri_1 = require("vscode-uri");
|
|
6
|
+
const apm_1 = require("./package/apm");
|
|
7
|
+
const defaultFiles_1 = require("./defaultFiles");
|
|
8
|
+
const package_1 = require("./package/package");
|
|
9
|
+
const manifest_1 = require("./manifest");
|
|
10
|
+
const myDatanetClient_1 = require("./myDatanetClient");
|
|
11
|
+
Object.defineProperty(exports, "RunExitCode", { enumerable: true, get: function () { return myDatanetClient_1.RunExitCode; } });
|
|
12
|
+
const usb_1 = require("./usb");
|
|
13
|
+
/**
|
|
14
|
+
* Helper to extract error message from unknown error
|
|
15
|
+
*/
|
|
16
|
+
function getErrorMessage(error) {
|
|
17
|
+
return error instanceof Error ? error.message : String(error);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Helper to create a failure result
|
|
21
|
+
*/
|
|
22
|
+
function failureResult(errorCode, message) {
|
|
23
|
+
return { success: false, errorCode, message };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Step 1: Build the project
|
|
27
|
+
*/
|
|
28
|
+
async function buildProject(ctx) {
|
|
29
|
+
const { cwd, fs, logger, env, apiToken, options } = ctx;
|
|
30
|
+
logger.info('[1/5] Building project...');
|
|
31
|
+
try {
|
|
32
|
+
const partsToBuild = options.part ? options.part : true;
|
|
33
|
+
await manifest_1.Manifest.validateBasicManifest(cwd, fs, null);
|
|
34
|
+
await package_1.Package.buildAll(cwd, fs, logger, env, apiToken, partsToBuild, true);
|
|
35
|
+
logger.done('Build completed successfully');
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return failureResult(myDatanetClient_1.RunExitCode.BuildFailed, `Build failed: ${getErrorMessage(error)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Step 2: Find and validate USB device
|
|
44
|
+
*/
|
|
45
|
+
async function findDevice(ctx) {
|
|
46
|
+
const { logger, options } = ctx;
|
|
47
|
+
const { serial } = options;
|
|
48
|
+
logger.info(`[2/5] Searching for USB device with serial: ${serial}...`);
|
|
49
|
+
const device = await (0, usb_1.findDeviceBySerialNumber)(serial);
|
|
50
|
+
if (!device) {
|
|
51
|
+
return failureResult(myDatanetClient_1.RunExitCode.DeviceNotFound, `USB device not found with serial number: ${serial}`);
|
|
52
|
+
}
|
|
53
|
+
logger.done(`Device found: ${device.productName}`);
|
|
54
|
+
return device;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Step 3: Upload binary to USB device
|
|
58
|
+
*/
|
|
59
|
+
async function uploadToUsbDevice(ctx) {
|
|
60
|
+
const { cwd, fs, logger, options } = ctx;
|
|
61
|
+
const { serial, debug } = options;
|
|
62
|
+
logger.info('[3/5] Uploading binary to USB device...');
|
|
63
|
+
const amxPath = vscode_uri_1.Utils.joinPath(cwd, defaultFiles_1.DefaultFiles.filePaths.dist.mainAmxPath);
|
|
64
|
+
const uploadResult = await (0, usb_1.uploadToDevice)(serial, amxPath, fs, logger, debug);
|
|
65
|
+
if (!uploadResult.success) {
|
|
66
|
+
return failureResult(myDatanetClient_1.RunExitCode.UsbUploadFailed, `USB upload failed: ${uploadResult.message}`);
|
|
67
|
+
}
|
|
68
|
+
logger.done('USB upload completed');
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Step 4a: Authenticate with myDatanet
|
|
73
|
+
*/
|
|
74
|
+
async function authenticateWithMyDatanet(mdnClient, logger) {
|
|
75
|
+
logger.info('[4/5] Connecting to myDatanet server...');
|
|
76
|
+
try {
|
|
77
|
+
return await mdnClient.authenticate();
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
return failureResult(myDatanetClient_1.RunExitCode.AuthenticationFailed, `myDatanet authentication failed: ${getErrorMessage(error)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Step 4b: Resolve customer UID
|
|
85
|
+
*/
|
|
86
|
+
async function resolveCustomer(mdnClient, customer, logger) {
|
|
87
|
+
if (customer) {
|
|
88
|
+
try {
|
|
89
|
+
const customerInfo = await mdnClient.getCustomer(customer);
|
|
90
|
+
logger.info(`Using customer: ${customerInfo.name} (${customerInfo._uid})`);
|
|
91
|
+
return customerInfo._uid;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return failureResult(myDatanetClient_1.RunExitCode.SiteCreationFailed, `Customer "${customer}" not found`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Try to get customer from available customers
|
|
98
|
+
const customers = await mdnClient.getCustomers();
|
|
99
|
+
if (customers.length === 0) {
|
|
100
|
+
return failureResult(myDatanetClient_1.RunExitCode.SiteCreationFailed, 'No customers available. Please specify --customer or set MYDATANET_CUSTOMER');
|
|
101
|
+
}
|
|
102
|
+
if (customers.length === 1) {
|
|
103
|
+
logger.info(`Using customer: ${customers[0].name} (${customers[0]._uid})`);
|
|
104
|
+
return customers[0]._uid;
|
|
105
|
+
}
|
|
106
|
+
const customerNames = customers.map(c => c.name).join(', ');
|
|
107
|
+
return failureResult(myDatanetClient_1.RunExitCode.SiteCreationFailed, `Multiple customers available. Please specify --customer or set MYDATANET_CUSTOMER. Available: ${customerNames}`);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Step 4c: Update install info with existing site data if available
|
|
111
|
+
*/
|
|
112
|
+
async function updateInstallInfoFromExistingSite(mdnClient, serial, installInfo, logger) {
|
|
113
|
+
const existingSite = await mdnClient.getDeviceSite(serial);
|
|
114
|
+
if (!existingSite) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (existingSite.app.phase === 'dev') {
|
|
118
|
+
installInfo.apmId = existingSite.app.apmid;
|
|
119
|
+
installInfo.applicationId = existingSite.app.appl_id;
|
|
120
|
+
}
|
|
121
|
+
installInfo.siteUid = existingSite._uid;
|
|
122
|
+
installInfo.siteName = existingSite.name;
|
|
123
|
+
installInfo.customerUid = existingSite.customer_uid;
|
|
124
|
+
logger.info(`Found existing site: ${existingSite.name}`);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Step 4d: Create and upload development APM tag
|
|
128
|
+
*/
|
|
129
|
+
async function uploadDevTag(ctx, mdnClient, installInfo) {
|
|
130
|
+
const { cwd, fs, logger } = ctx;
|
|
131
|
+
logger.info('Creating development APM tag...');
|
|
132
|
+
try {
|
|
133
|
+
const apmBuffer = await apm_1.APM.createDevTag(cwd, fs);
|
|
134
|
+
const applicationId = await mdnClient.uploadDevTag(installInfo.apmId, apmBuffer);
|
|
135
|
+
installInfo.applicationId = applicationId;
|
|
136
|
+
logger.done('Development tag uploaded');
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
return failureResult(myDatanetClient_1.RunExitCode.DevTagUploadFailed, `Development tag upload failed: ${getErrorMessage(error)}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Step 5: Create or update development site
|
|
145
|
+
*/
|
|
146
|
+
async function createOrUpdateSite(mdnClient, customerUid, installInfo, logger) {
|
|
147
|
+
logger.info('[5/5] Creating development site...');
|
|
148
|
+
try {
|
|
149
|
+
if (installInfo.siteUid) {
|
|
150
|
+
// Update site name if different
|
|
151
|
+
if (installInfo.siteName !== installInfo.name) {
|
|
152
|
+
await mdnClient.updateSiteName(installInfo.siteUid, installInfo.name);
|
|
153
|
+
}
|
|
154
|
+
logger.info(`Reusing existing site: ${installInfo.siteUid}`);
|
|
155
|
+
return installInfo.siteUid;
|
|
156
|
+
}
|
|
157
|
+
return await mdnClient.createDevelopmentSite(customerUid, installInfo);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
return failureResult(myDatanetClient_1.RunExitCode.SiteCreationFailed, `Site creation failed: ${getErrorMessage(error)}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Log the deployment summary
|
|
165
|
+
*/
|
|
166
|
+
function logDeploymentSummary(logger, siteUid, apmId) {
|
|
167
|
+
logger.done('Development deployment completed successfully!');
|
|
168
|
+
logger.info('');
|
|
169
|
+
logger.info('=== Deployment Summary ===');
|
|
170
|
+
logger.info(`Site UID: ${siteUid}`);
|
|
171
|
+
logger.info(`APM ID: ${apmId}`);
|
|
172
|
+
logger.info('==========================');
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Check if result is a failure
|
|
176
|
+
*/
|
|
177
|
+
function isFailure(result) {
|
|
178
|
+
return typeof result === 'object' && result !== null && 'errorCode' in result;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Builds, uploads to USB device, and creates a development site on myDatanet.
|
|
182
|
+
*
|
|
183
|
+
* This function orchestrates the complete development deployment workflow:
|
|
184
|
+
* 1. Builds the project
|
|
185
|
+
* 2. Uploads the binary to the USB device
|
|
186
|
+
* 3. Authenticates with myDatanet
|
|
187
|
+
* 4. Creates and uploads a development APM tag
|
|
188
|
+
* 5. Creates or reuses a development site
|
|
189
|
+
*
|
|
190
|
+
* @param {URI} cwd - The current working directory of the project.
|
|
191
|
+
* @param {LocalFS} fs - The file system interface for file operations.
|
|
192
|
+
* @param {Log.Logger} logger - Logger instance for logging progress.
|
|
193
|
+
* @param {Globals.ENV} env - The environment configuration.
|
|
194
|
+
* @param {string | null} apiToken - The Studio API token for registry operations.
|
|
195
|
+
* @param {RunOptions} options - Options for the run command.
|
|
196
|
+
* @return {Promise<RunResult>} A promise that resolves to the result of the run command.
|
|
197
|
+
*/
|
|
198
|
+
async function run(cwd, fs, logger, env, apiToken, options) {
|
|
199
|
+
const ctx = { cwd, fs, logger, env, apiToken, options };
|
|
200
|
+
const { serial, server, customer, mdnToken } = options;
|
|
201
|
+
logger.info('Starting development deployment...');
|
|
202
|
+
// Step 1: Build the project
|
|
203
|
+
const buildResult = await buildProject(ctx);
|
|
204
|
+
if (buildResult) {
|
|
205
|
+
return buildResult;
|
|
206
|
+
}
|
|
207
|
+
// Step 2: Find and validate USB device
|
|
208
|
+
const deviceResult = await findDevice(ctx);
|
|
209
|
+
if (isFailure(deviceResult)) {
|
|
210
|
+
return deviceResult;
|
|
211
|
+
}
|
|
212
|
+
// Step 3: Upload binary to USB device
|
|
213
|
+
const uploadResult = await uploadToUsbDevice(ctx);
|
|
214
|
+
if (uploadResult) {
|
|
215
|
+
return uploadResult;
|
|
216
|
+
}
|
|
217
|
+
// Step 4: myDatanet operations
|
|
218
|
+
const mdnClient = new myDatanetClient_1.MyDatanetClient(server, mdnToken, logger);
|
|
219
|
+
// 4a: Authenticate
|
|
220
|
+
const authResult = await authenticateWithMyDatanet(mdnClient, logger);
|
|
221
|
+
if (isFailure(authResult)) {
|
|
222
|
+
return authResult;
|
|
223
|
+
}
|
|
224
|
+
const userInfo = authResult;
|
|
225
|
+
// 4b: Resolve customer
|
|
226
|
+
const customerResult = await resolveCustomer(mdnClient, customer, logger);
|
|
227
|
+
if (isFailure(customerResult)) {
|
|
228
|
+
return customerResult;
|
|
229
|
+
}
|
|
230
|
+
const customerUid = customerResult;
|
|
231
|
+
// Build development install info
|
|
232
|
+
const installInfo = await (0, myDatanetClient_1.buildDevelopmentInstallInfo)(cwd, fs, serial, userInfo.name, customerUid);
|
|
233
|
+
// 4c: Check for existing site
|
|
234
|
+
await updateInstallInfoFromExistingSite(mdnClient, serial, installInfo, logger);
|
|
235
|
+
// 4d: Upload dev tag
|
|
236
|
+
const devTagResult = await uploadDevTag(ctx, mdnClient, installInfo);
|
|
237
|
+
if (devTagResult) {
|
|
238
|
+
return devTagResult;
|
|
239
|
+
}
|
|
240
|
+
// Step 5: Create or update development site
|
|
241
|
+
const siteResult = await createOrUpdateSite(mdnClient, customerUid, installInfo, logger);
|
|
242
|
+
if (isFailure(siteResult)) {
|
|
243
|
+
return siteResult;
|
|
244
|
+
}
|
|
245
|
+
const siteUid = siteResult;
|
|
246
|
+
logDeploymentSummary(logger, siteUid, installInfo.apmId);
|
|
247
|
+
return {
|
|
248
|
+
success: true,
|
|
249
|
+
siteUid,
|
|
250
|
+
apmId: installInfo.apmId,
|
|
251
|
+
message: 'Development deployment completed successfully'
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=run.js.map
|