@oniroproject/oniro-app 0.15.0 → 0.16.1
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/README.md +14 -3
- package/dist/oniro-app.js +106 -24
- package/dist/oniro-app.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -61,9 +61,20 @@ Install commands are idempotent (they skip when already present); pass `--force`
|
|
|
61
61
|
| Command | Description |
|
|
62
62
|
| --- | --- |
|
|
63
63
|
| `sdk install <version> [--force] [--tmp-dir <path>]` | Download and install an OpenHarmony SDK (e.g. `6.1`). `--tmp-dir` overrides where download/extract temporaries go (see [Temporary space](#temporary-space)). |
|
|
64
|
+
| `sdk install --from-archive <path> [--force] [--tmp-dir <path>]` | Install an SDK from a local `.tar.gz` instead, such as the [full SDK](#full-sdk). Only the current OS's components are unpacked, and the API level is read from them; an SDK already installed at that level is replaced only with `--force`. |
|
|
64
65
|
| `sdk list [--json]` | List known SDK versions; an `*` marks installed ones. |
|
|
65
66
|
| `sdk remove <api>` | Remove an installed SDK by API level (e.g. `18`, `20`). |
|
|
66
67
|
|
|
68
|
+
##### Full SDK
|
|
69
|
+
|
|
70
|
+
`sdk install <version>` downloads the public SDK, which leaves out system APIs (`@systemapi`). The full SDK is not on the release mirror: download an `ohos-sdk-full` archive from the [OpenHarmony daily builds](https://dcp.openharmony.cn/workbench/cicd/dailybuild/dailylist) and install it from the file:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
oniro-app sdk install --from-archive ~/Downloads/version-Master_Version-OpenHarmony_7.0.0.39-…-ohos-sdk-full_7.0-Release.tar.gz
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
It lands in `<ONIRO_SDK_ROOT_DIR>/<os>/<api>` like a downloaded release, so projects with that `compileSdkVersion` build against it. Pass `--force` to replace a public SDK of the same API level, or set `ONIRO_SDK_ROOT_DIR` to a separate root to keep both.
|
|
77
|
+
|
|
67
78
|
#### `cmdtools` — manage the OpenHarmony command-line tools (provides `hvigorw`, `ohpm`, `hdc`, `codelinter`)
|
|
68
79
|
|
|
69
80
|
| Command | Description |
|
|
@@ -478,7 +489,7 @@ oniro-app auth login # once per machine
|
|
|
478
489
|
oniro-app create --name MyApp --bundle com.example.myapp \
|
|
479
490
|
--location ~/projects --sdk 24 --template HarmonyOSApp
|
|
480
491
|
cd ~/projects/MyApp
|
|
481
|
-
oniro-app sign --harmonyos # registers
|
|
492
|
+
oniro-app sign --harmonyos # registers each connected device that reports a UDID
|
|
482
493
|
oniro-app build
|
|
483
494
|
oniro-app app install && oniro-app app launch
|
|
484
495
|
```
|
|
@@ -506,7 +517,7 @@ Mirrors DevEco Studio's automatic signing:
|
|
|
506
517
|
|
|
507
518
|
1. generates a local p12 keystore and a CSR (via the SDK's `hap-sign-tool.jar`, so a JDK is needed — DevEco Studio's bundled JBR is used when present);
|
|
508
519
|
2. has AppGallery Connect issue a debug certificate for that CSR;
|
|
509
|
-
3. registers every connected device's UDID with the team — no device is needed when the team already has some registered (from another machine, or by DevEco Studio);
|
|
520
|
+
3. registers every connected device's UDID with the team — no device is needed when the team already has some registered (from another machine, or by DevEco Studio). A device that does not report a UDID (`hdc shell bm get -u`; some OpenHarmony builds do not) cannot be registered, so the profile does not cover it;
|
|
510
521
|
4. issues a debug provisioning profile naming that certificate, all of the team's registered devices, and any ACL-gated permissions the project requests;
|
|
511
522
|
5. writes an encrypted `signingConfigs` entry into `build-profile.json5`, leaving other entries untouched.
|
|
512
523
|
|
|
@@ -578,7 +589,7 @@ oniro-app sign --apl system_core --acls ohos.permission.ACCESSIBILITY_EXTENSION_
|
|
|
578
589
|
--app-privilege-capabilities AllowAppUsePrivilegeExtension
|
|
579
590
|
```
|
|
580
591
|
|
|
581
|
-
Apps like these usually also call system APIs (`@systemapi`), which the public SDK that `sdk install` downloads leaves out: the build fails with errors such as `'config' is not exported from Kit '@kit.AccessibilityKit'`. Build them against the full OpenHarmony SDK
|
|
592
|
+
Apps like these usually also call system APIs (`@systemapi`), which the public SDK that `sdk install` downloads leaves out: the build fails with errors such as `'config' is not exported from Kit '@kit.AccessibilityKit'`. Build them against the full OpenHarmony SDK, installed with `sdk install --from-archive` (see [Full SDK](#full-sdk)).
|
|
582
593
|
|
|
583
594
|
The `signingConfigs` block written to `build-profile.json5` mirrors the names used by the project's `products[*].signingConfig` (e.g. `applications/standard/systemui` uses `release`, `launcher` uses `default`). If the project has no products section, `default` is used, and a product that names no `signingConfig` is pointed at it.
|
|
584
595
|
|
package/dist/oniro-app.js
CHANGED
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
// src/bin/oniro-app.ts
|
|
4
4
|
import { Command as Command4 } from "commander";
|
|
5
|
-
import { OniroError as
|
|
5
|
+
import { OniroError as OniroError3 } from "@oniroproject/core";
|
|
6
6
|
|
|
7
7
|
// src/commands/sdk.ts
|
|
8
8
|
import * as fs from "fs";
|
|
9
9
|
import * as path from "path";
|
|
10
10
|
import {
|
|
11
11
|
ALL_SDKS,
|
|
12
|
+
OHOS_DAILY_BUILDS_URL,
|
|
12
13
|
downloadAndInstallSdk,
|
|
13
14
|
getOsFolder,
|
|
14
15
|
getSdkRootDir,
|
|
15
16
|
getSupportedSdksForUi,
|
|
17
|
+
installSdkFromArchive,
|
|
16
18
|
removeSdk
|
|
17
19
|
} from "@oniroproject/core";
|
|
18
20
|
|
|
@@ -104,13 +106,42 @@ function getRuntime() {
|
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
// src/commands/sdk.ts
|
|
109
|
+
var FULL_SDK_TIP = `Tip: the full SDK (with system APIs) is not on the release mirror. Download an ohos-sdk-full archive from the OpenHarmony daily builds, ${OHOS_DAILY_BUILDS_URL}, and install it with \`oniro-app sdk install --from-archive <archive>\`.`;
|
|
107
110
|
function registerSdkCommand(program) {
|
|
108
111
|
const sdk = program.command("sdk").description("Manage OpenHarmony SDK installs.");
|
|
109
|
-
sdk.command("install
|
|
112
|
+
sdk.command("install [version]").description(
|
|
113
|
+
"Download and install an OpenHarmony SDK (skips if already installed), or install one from a local archive with --from-archive."
|
|
114
|
+
).option(
|
|
115
|
+
"--from-archive <path>",
|
|
116
|
+
"Install from a local SDK .tar.gz instead of downloading, e.g. the full SDK. The API level is read from the archive."
|
|
117
|
+
).option("--force", "Reinstall even if the SDK is already present.").option(
|
|
110
118
|
"--tmp-dir <path>",
|
|
111
119
|
"Directory for download/extract temporaries (default: ONIRO_TMP_DIR, else .oniro-tmp next to the install target). Use this when that filesystem is short on space."
|
|
112
|
-
).
|
|
120
|
+
).addHelpText("after", `
|
|
121
|
+
${FULL_SDK_TIP}`).action(async (version, opts) => {
|
|
113
122
|
const { config, logger, progress } = getRuntime();
|
|
123
|
+
if (opts.fromArchive) {
|
|
124
|
+
if (version) throw new Error("Pass either a version or --from-archive, not both.");
|
|
125
|
+
const archivePath = path.resolve(opts.fromArchive);
|
|
126
|
+
logger.info(`Installing OpenHarmony SDK from ${archivePath}...`);
|
|
127
|
+
const installed = await installSdkFromArchive({
|
|
128
|
+
config,
|
|
129
|
+
archivePath,
|
|
130
|
+
force: opts.force,
|
|
131
|
+
progress,
|
|
132
|
+
logger,
|
|
133
|
+
tmpDir: opts.tmpDir
|
|
134
|
+
});
|
|
135
|
+
const label = installed.version ? `SDK ${installed.version} (api ${installed.api})` : `SDK api ${installed.api}`;
|
|
136
|
+
logger.info(`${label} installed at ${installed.dir}.`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (!version) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Name an SDK version (${ALL_SDKS.map((s) => s.version).join(", ")}) or pass --from-archive <path>.
|
|
142
|
+
` + FULL_SDK_TIP
|
|
143
|
+
);
|
|
144
|
+
}
|
|
114
145
|
const release = ALL_SDKS.find((s) => s.version === version);
|
|
115
146
|
if (!release) {
|
|
116
147
|
throw new Error(
|
|
@@ -369,7 +400,7 @@ function registerSignCommand(program) {
|
|
|
369
400
|
).argParser(parseAclsList)
|
|
370
401
|
).option(
|
|
371
402
|
"--harmonyos",
|
|
372
|
-
"Sign a HarmonyOS project through AppGallery Connect. Needs `oniro-app auth login`, and a
|
|
403
|
+
"Sign a HarmonyOS project through AppGallery Connect. Needs `oniro-app auth login`, and a device registered to the team: a connected device is registered if it reports a UDID (`hdc shell bm get -u`)."
|
|
373
404
|
).option("--team-id <id>", "With --harmonyos: the AGC team to sign under. Default: the account's own (`oniro-app auth team list`).").option("--product <name>", "With --harmonyos: the product to sign. Default `default`.").option("--force", "With --harmonyos: regenerate the certificate and profile even when they are still valid.").option("--bootstrap", "No-op if signing material is already present; otherwise generate it.").option("--store-password <pwd>", "Keystore store password (default 123456 \u2014 the SDK keystore password).").option("--key-password <pwd>", "Keystore key password (default 123456 \u2014 the SDK keystore password).").action(
|
|
374
405
|
async (projectDir, opts) => {
|
|
375
406
|
const { config, logger } = getRuntime();
|
|
@@ -488,7 +519,17 @@ function registerAuthCommand(program) {
|
|
|
488
519
|
|
|
489
520
|
// src/commands/app.ts
|
|
490
521
|
import * as path4 from "path";
|
|
491
|
-
import {
|
|
522
|
+
import {
|
|
523
|
+
installApp,
|
|
524
|
+
launchApp,
|
|
525
|
+
uninstallApp,
|
|
526
|
+
forceStop,
|
|
527
|
+
applyChanges,
|
|
528
|
+
existingHapOrUnsigned,
|
|
529
|
+
getBundleName,
|
|
530
|
+
readHapBundleName,
|
|
531
|
+
OniroError as OniroError2
|
|
532
|
+
} from "@oniroproject/core";
|
|
492
533
|
function registerAppCommand(program) {
|
|
493
534
|
const app = program.command("app").description("Install, launch, and manage built apps on a connected device/emulator.");
|
|
494
535
|
app.command("install [project-dir-or-hap]").description(
|
|
@@ -516,16 +557,29 @@ function registerAppCommand(program) {
|
|
|
516
557
|
}
|
|
517
558
|
logger.info("App launched.");
|
|
518
559
|
});
|
|
519
|
-
app.command("apply [project-dir]").description(
|
|
520
|
-
"Install a HAP and verify the running process took the change. Handles sign-info mismatch, asset-cache invalidation, and persistent-bundle restart."
|
|
521
|
-
).option(
|
|
560
|
+
app.command("apply [project-dir-or-hap]").description(
|
|
561
|
+
"Install a HAP (a project dir, default the current one, or a .hap file) and verify the running process took the change. Refuses a HAP of another bundle. Handles sign-info mismatch, asset-cache invalidation, and persistent-bundle restart."
|
|
562
|
+
).option(
|
|
563
|
+
"--bundle <bundle>",
|
|
564
|
+
"Bundle name to apply changes to. Defaults to the bundleName in the .hap file, else in the project AppScope/app.json5."
|
|
565
|
+
).option("--module <module>", "Module to resolve the HAP from (multi-module projects).").option("--hap <path>", "Explicit .hap path (relative to project-dir or absolute).").option("--installed-hap <path>", "Local path to the currently-installed HAP, to enable the asset-cache reboot.").option("--system", "Treat as a persistent/system bundle (refuses uninstall on sign-info mismatch).").option("--allow-uninstall", "Permit uninstalling a system bundle on sign-info mismatch (dangerous).").option("--device <serial>", "Target device serial.").option("--json", "Emit the result as JSON.").action(async (target, opts) => {
|
|
522
566
|
const { config, logger } = getRuntime();
|
|
523
|
-
const
|
|
524
|
-
|
|
567
|
+
const isHap = target !== void 0 && /\.hap$/i.test(target);
|
|
568
|
+
if (isHap && opts.hap) throw new OniroError2("Pass the HAP either as the argument or with --hap, not both.");
|
|
569
|
+
const dir = isHap ? process.cwd() : path4.resolve(target ?? process.cwd());
|
|
570
|
+
const hapPath = isHap ? path4.resolve(target) : opts.hap;
|
|
571
|
+
let bundle = opts.bundle;
|
|
572
|
+
if (!bundle && isHap) {
|
|
573
|
+
const hap = existingHapOrUnsigned(hapPath);
|
|
574
|
+
if (!hap) throw new OniroError2(`HAP not found at ${hapPath}.`);
|
|
575
|
+
bundle = await readHapBundleName(hap.path).catch(() => null) ?? void 0;
|
|
576
|
+
if (!bundle) throw new OniroError2(`Could not read the bundle name from ${hap.path}. Pass --bundle.`);
|
|
577
|
+
}
|
|
578
|
+
bundle ??= getBundleName(dir);
|
|
525
579
|
const result = await applyChanges({
|
|
526
580
|
config,
|
|
527
581
|
bundle,
|
|
528
|
-
hapPath
|
|
582
|
+
hapPath,
|
|
529
583
|
projectDir: dir,
|
|
530
584
|
module: opts.module,
|
|
531
585
|
installedHapPath: opts.installedHap,
|
|
@@ -685,16 +739,31 @@ function targetIndexFrom(opts) {
|
|
|
685
739
|
return n;
|
|
686
740
|
}
|
|
687
741
|
|
|
742
|
+
// src/lib/logSince.ts
|
|
743
|
+
function parseSince(value) {
|
|
744
|
+
if (value === void 0) return void 0;
|
|
745
|
+
if (value === "all") return Infinity;
|
|
746
|
+
const seconds = Number(value);
|
|
747
|
+
if (value.trim() === "" || !Number.isFinite(seconds) || seconds < 0) {
|
|
748
|
+
throw new Error(`--since takes a number of seconds or "all", not "${value}".`);
|
|
749
|
+
}
|
|
750
|
+
return seconds * 1e3;
|
|
751
|
+
}
|
|
752
|
+
|
|
688
753
|
// src/commands/wait.ts
|
|
689
754
|
function registerWaitCommand(program) {
|
|
690
755
|
const wait = program.command("wait").description(
|
|
691
756
|
"Wait for a device condition: a log line (--log), boot (--boot), a bundle to start (--bundle), or an on-screen element (--target / --target-id / --target-type)."
|
|
692
|
-
).option("--log <pattern>", "Resolve when a hilog line (tag: message)
|
|
757
|
+
).option("--log <pattern>", "Resolve when a hilog line (tag: message) logged after the command starts matches this regex.").option(
|
|
758
|
+
"--since <seconds>",
|
|
759
|
+
'With --log: also match lines logged up to N seconds before the command started, measured on the device clock; "all" matches everything still in the device log buffer.'
|
|
760
|
+
).option("--boot", "Resolve when the device is reachable (optionally also --pid-of).").option("--bundle <bundle>", "With --log: filter to this bundle. Alone: wait until this bundle is running.").option("--pid-of <name>", "With --boot: also require this process to have a pid.").option("--domain <domain>", "With --log: hilog domain filter (e.g. 0xD003900).");
|
|
693
761
|
addTargetOptions(wait, "Resolve when", { index: false });
|
|
694
762
|
wait.option("--gone", "With --target*: resolve when no uncovered element matches any more.").option("--interval <ms>", "With --target*: poll interval, ms (each poll is a layout dump).", "500").option("--timeout <ms>", "Max wait, ms.", "30000").option("--device <serial>", "Target device serial.").option("--json", "With --log or --target*: emit the matched entry/element as JSON.").action(async (opts) => {
|
|
695
763
|
const { config, logger } = getRuntime();
|
|
696
764
|
const timeoutMs = Number(opts.timeout);
|
|
697
765
|
const selector = selectorFrom(opts);
|
|
766
|
+
if (opts.since !== void 0 && !opts.log) throw new Error("--since applies only to --log.");
|
|
698
767
|
if (opts.log) {
|
|
699
768
|
const entry = await waitForLog({
|
|
700
769
|
config,
|
|
@@ -703,11 +772,12 @@ function registerWaitCommand(program) {
|
|
|
703
772
|
bundle: opts.bundle,
|
|
704
773
|
domain: opts.domain,
|
|
705
774
|
deviceSerial: opts.device,
|
|
775
|
+
lookbackMs: parseSince(opts.since),
|
|
706
776
|
logger
|
|
707
777
|
});
|
|
708
778
|
if (opts.json) process.stdout.write(`${JSON.stringify(entry, null, 2)}
|
|
709
779
|
`);
|
|
710
|
-
else logger.info(`matched: ${entry.tag}: ${entry.message}`);
|
|
780
|
+
else logger.info(`matched: ${entry.time} ${entry.tag}: ${entry.message}`);
|
|
711
781
|
return;
|
|
712
782
|
}
|
|
713
783
|
if (opts.boot) {
|
|
@@ -746,7 +816,10 @@ function registerWaitCommand(program) {
|
|
|
746
816
|
// src/commands/watch.ts
|
|
747
817
|
import { watchLog } from "@oniroproject/core";
|
|
748
818
|
function registerWatchCommand(program) {
|
|
749
|
-
program.command("watch").description("Collect hilog lines matching a pattern
|
|
819
|
+
program.command("watch").description("Collect the hilog lines matching a pattern that are logged during a fixed duration.").option("--log <pattern>", "Regex tested against each line (tag: message).").option("--for <ms>", "Duration to watch, ms.", "10000").option(
|
|
820
|
+
"--since <seconds>",
|
|
821
|
+
'Also collect lines logged up to N seconds before the command started, measured on the device clock; "all" adds everything still in the device log buffer.'
|
|
822
|
+
).option("--bundle <bundle>", "Filter to this bundle.").option("--domain <domain>", "hilog domain filter (e.g. 0xD003900).").option("--no-dedup", "Do not collapse consecutive duplicate lines.").option("--device <serial>", "Target device serial.").option("--json", "Emit the entries as JSON.").action(async (opts) => {
|
|
750
823
|
const { config, logger } = getRuntime();
|
|
751
824
|
if (!opts.log) throw new Error("watch requires --log <pattern>.");
|
|
752
825
|
const entries = await watchLog({
|
|
@@ -757,6 +830,7 @@ function registerWatchCommand(program) {
|
|
|
757
830
|
domain: opts.domain,
|
|
758
831
|
dedup: opts.dedup,
|
|
759
832
|
deviceSerial: opts.device,
|
|
833
|
+
lookbackMs: parseSince(opts.since),
|
|
760
834
|
logger
|
|
761
835
|
});
|
|
762
836
|
if (opts.json) {
|
|
@@ -1194,6 +1268,7 @@ import {
|
|
|
1194
1268
|
detectCmdToolsFlavour,
|
|
1195
1269
|
detectRuntimeOs as detectRuntimeOs2,
|
|
1196
1270
|
getCmdToolsPath,
|
|
1271
|
+
missingProjectRootFiles,
|
|
1197
1272
|
runCodelinter
|
|
1198
1273
|
} from "@oniroproject/core";
|
|
1199
1274
|
function lintTotals(result) {
|
|
@@ -1208,8 +1283,10 @@ function lintTotals(result) {
|
|
|
1208
1283
|
}
|
|
1209
1284
|
function lintExitCode(result) {
|
|
1210
1285
|
if (lintTotals(result).errors > 0) return 1;
|
|
1211
|
-
|
|
1212
|
-
|
|
1286
|
+
return lintFailed(result) ? result.code : 0;
|
|
1287
|
+
}
|
|
1288
|
+
function lintFailed(result) {
|
|
1289
|
+
return result.summary === null && result.findings.length === 0 && result.code !== 0;
|
|
1213
1290
|
}
|
|
1214
1291
|
function registerLintCommand(program) {
|
|
1215
1292
|
program.command("lint [project-dir]").description(
|
|
@@ -1219,10 +1296,11 @@ function registerLintCommand(program) {
|
|
|
1219
1296
|
const dir = path8.resolve(projectDir ?? process.cwd());
|
|
1220
1297
|
const result = await runCodelinter({ config, projectDir: dir, files: opts.files, logger });
|
|
1221
1298
|
const totals = lintTotals(result);
|
|
1299
|
+
const failed = lintFailed(result);
|
|
1222
1300
|
if (opts.json) {
|
|
1223
1301
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
1224
1302
|
`);
|
|
1225
|
-
} else {
|
|
1303
|
+
} else if (!failed) {
|
|
1226
1304
|
for (const f of result.findings) {
|
|
1227
1305
|
const at = `${f.file}:${f.line}${f.column ? `:${f.column}` : ""}`;
|
|
1228
1306
|
process.stdout.write(`${at} ${f.severity} ${f.message}${f.code ? ` [${f.code}]` : ""}
|
|
@@ -1233,9 +1311,13 @@ function registerLintCommand(program) {
|
|
|
1233
1311
|
`
|
|
1234
1312
|
);
|
|
1235
1313
|
}
|
|
1236
|
-
if (
|
|
1237
|
-
|
|
1238
|
-
${
|
|
1314
|
+
if (failed) {
|
|
1315
|
+
const missing = missingProjectRootFiles(dir);
|
|
1316
|
+
const why = missing.length ? ` It needs a DevEco project root, and ${dir} has no ${missing.join(", ")}.` : "";
|
|
1317
|
+
logger.error(
|
|
1318
|
+
`codelinter exited ${result.code} without a report, so nothing was linted.${why}
|
|
1319
|
+
` + result.raw.trim().split("\n").slice(-15).join("\n")
|
|
1320
|
+
);
|
|
1239
1321
|
}
|
|
1240
1322
|
if (result.incomplete) {
|
|
1241
1323
|
for (const n of result.notices) logger.warn(`codelinter: ${n}`);
|
|
@@ -1921,19 +2003,19 @@ function registerHdcCommand(program) {
|
|
|
1921
2003
|
`);
|
|
1922
2004
|
return;
|
|
1923
2005
|
}
|
|
1924
|
-
await new Promise((
|
|
2006
|
+
await new Promise((resolve10, reject) => {
|
|
1925
2007
|
const child = spawnCompat(hdc, hdcPassthroughArgv(args, opts.device), { stdio: "inherit" });
|
|
1926
2008
|
child.on("error", (err) => reject(new Error(`Failed to run ${hdc}: ${err.message}`)));
|
|
1927
2009
|
child.on("close", (code) => {
|
|
1928
2010
|
process.exitCode = code ?? 1;
|
|
1929
|
-
|
|
2011
|
+
resolve10();
|
|
1930
2012
|
});
|
|
1931
2013
|
});
|
|
1932
2014
|
});
|
|
1933
2015
|
}
|
|
1934
2016
|
|
|
1935
2017
|
// src/bin/oniro-app.ts
|
|
1936
|
-
var CLI_VERSION = "0.
|
|
2018
|
+
var CLI_VERSION = "0.16.1";
|
|
1937
2019
|
function buildProgram() {
|
|
1938
2020
|
const program = new Command4().name("oniro-app").description(
|
|
1939
2021
|
"Cross-platform CLI for Oniro/OpenHarmony app development.\n\nDesigned for non-interactive use (CI, scripts, agents): every command\ntakes explicit flags, results go to stdout, progress/logs go to stderr,\nand a non-zero exit code indicates failure."
|
|
@@ -1965,7 +2047,7 @@ async function main() {
|
|
|
1965
2047
|
try {
|
|
1966
2048
|
await program.parseAsync(process.argv);
|
|
1967
2049
|
} catch (err) {
|
|
1968
|
-
const message = err instanceof
|
|
2050
|
+
const message = err instanceof OniroError3 ? err.message : err instanceof Error ? err.message : String(err);
|
|
1969
2051
|
process.stderr.write(`[error] ${message}
|
|
1970
2052
|
`);
|
|
1971
2053
|
if (process.env.ONIRO_DEBUG === "1" && err instanceof Error && err.stack) {
|