@oniroproject/oniro-app 0.18.0 → 0.19.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/README.md CHANGED
@@ -387,6 +387,35 @@ The SDK is `--sdk <dir>`, else `<sdk home>/<--api>`, else the one the project's
387
387
  | `--no-other-sdks` | `show` only: on a miss, do not look in the other installed SDKs. |
388
388
  | `--json` | `search`: `sdk` (`dir`, `api`, `version`, `source`), `query`, `total`, `hits` (the fields above as `name`, `qualifiedName`, `kind`, `kit`, `import`, `global`, `module`, `since`, `sinceText`, `deprecated`, `deprecatedSince`, `useInstead`, `syscap`, `systemApi`, `file`, `path`, `line`, `signature`, `doc`, `overloads`, `score`). `show`: `sdk`, `symbol`, `found`, `entries`, and on a miss `alsoIn` (`[{ api, since }]`) and `suggestions`. |
389
389
 
390
+ #### `docs sync` / `docs search` / `docs show` — read the OpenHarmony documentation
391
+
392
+ The application-development documentation of the OpenHarmony release matching the SDK (`en/application-dev` of [gitcode.com/openharmony/docs](https://gitcode.com/openharmony/docs)): usage guides (ArkUI components, navigation, state management, data storage, abilities), kit API prose, the business error codes each kit throws and platform behaviour (lifecycle, permissions, threads). `api search` says whether a symbol exists and with what signature; this says how it is used.
393
+
394
+ - `docs sync` clones the release's tree once: a partial, sparse clone without figures, about 100 MB (a plain clone of the tree is 2 GB), under `<sdkRootDir>/docs/<branch>` (`ONIRO_DOCS_DIR`). The branch comes from the SDK's version (`6.1.0.31` → `OpenHarmony-6.1-Release`, `5.1.0.107` → `OpenHarmony-5.1.0-Release`) or `--ref`. A second `sync` brings the clone up to date. Needs `git`.
395
+ - `docs search <query...>` ranks documents that contain every word (any word when none does) by where the words occur: title, headings, path, body; a camelCase or dotted identifier also matches by its parts (`NavPathStack` → nav, path, stack, weighing less). A document whose title is exactly a query word, or that holds the whole query in a title or heading, ranks higher; the generated API reference a little below the guides; system-API pages and tables of contents below everything. Each hit prints the path, the title, the matching sections with their line numbers and the first line. Exits 1 when nothing matches. The index is built in memory on first use (about 3 s for 5,000 documents) and answers in milliseconds.
396
+ - `docs show <path>` prints one document (a path `search` printed, relative to `en/application-dev`), or one section of it with `--heading` (a heading or a distinctive part of it, case-insensitive: from that heading to the next of the same or a higher level). Image lines and inline HTML are dropped; a long document is cut at `--lines` (default 250) with the list of its sections, so ask for a heading rather than the whole file. Exits 1 when the document or heading does not exist, suggesting close paths or the document's headings.
397
+
398
+ ```
399
+ $ oniro-app docs search --api 23 relationalStore insert
400
+ reference/apis-arkdata/arkts-apis-data-relationalStore-RdbStore.md — Interface (RdbStore)
401
+ sections: "insert" (line 94), "insert10+" (line 178), "insertSync12+" (line 443), …
402
+ Provides APIs for managing data in an RDB store.
403
+ …
404
+ $ oniro-app docs show --api 23 reference/apis-arkdata/errorcode-data-rdb.md --heading 14800011
405
+ ## 14800011 Database File Corrupted
406
+ …
407
+ ```
408
+
409
+ | Flag | Description |
410
+ | --- | --- |
411
+ | `--api <level>` / `--sdk <dir>` / `--project <dir>` | The SDK whose release picks the docs, as for `api search`. |
412
+ | `--ref <branch>` | A docs branch or tag (`OpenHarmony-6.1-Release`, `master`) instead of the SDK-chosen one. |
413
+ | `--limit <n>` | `search` only: at most n hits (default 8). |
414
+ | `--section <dir>` | `search` only: only documents under this subtree (`ui`, `database`, `reference/errorcodes`, `application-models`). |
415
+ | `--heading <text>` | `show` only: only that section. |
416
+ | `--lines <n>` | `show` only: at most n lines (default 250). |
417
+ | `--json` | `sync`: `docs` (`ref`, `dir`, `tree`, `sdk`), `files`, `updated`, `commit`. `search`: `docs`, `query`, `terms`, `total`, `hits` (`path`, `title`, `score`, `headings` `[{ line, text }]`, `snippet`). `show`: `docs`, `found`, `path`, `text`, `truncated`, `lines`, `headings`; on a miss `error` and `suggestions`. |
418
+
390
419
  #### `lint` — run the OpenHarmony codelinter
391
420
 
392
421
  `lint [project-dir]` (defaults to the current directory). Prints each finding as `file:line:col severity message [rule]`, then the totals (`N defects: E errors, W warnings, S suggestions.`). Exits 1 on error-severity findings or when codelinter fails to produce a report; warnings alone exit 0, although codelinter itself exits 1 for them. When codelinter says its report is incomplete, for example because the command-line tools' flavour (see `cmdtools status`) does not match the project's `runtimeOS`, it exits 2: it skipped checks, so a clean report is not evidence of clean code. Its notices, and a hint when the flavour is the cause, go to stderr.
@@ -1005,10 +1034,12 @@ Tools (the inputs are the command's flags in camelCase, with the same defaults;
1005
1034
  | `screens` | `screens --json` | `project` |
1006
1035
  | `api_search` | `api search --json` | `query` (required), `kit`, `kind[]`, `lang`, `limit`, `deprecated`, `systemapi`, `api`, `sdk`, `project`, `rebuild` |
1007
1036
  | `api_show` | `api show --json` | `symbol` (required), `kit`, `otherSdks`, `api`, `sdk`, `project`, `rebuild` |
1037
+ | `docs_search` | `docs search --json` | `query` (required), `limit`, `section`, `api`, `sdk`, `ref`, `project` |
1038
+ | `docs_show` | `docs show --json` | `path` (required), `heading`, `maxLines`, `api`, `sdk`, `ref`, `project` |
1008
1039
  | `doctor` | `doctor --json` | `project`, `fix`, `device` (boolean, as `--no-device`), `serial`, `arktsCheck` |
1009
1040
  | `devices` | `devices --json` | `long`; the array comes wrapped as `{ "devices": [...] }` |
1010
1041
 
1011
- - **Errors.** A tool result has `isError: true` when the command would exit non-zero: a failed build, `check_arkts` with an error or one that could not run, a failed or incomplete test run, a failed smoke check, a `perf_trace` threshold exceeded or capture failed, an `api_show` miss or an `api_search` without hits, a failing `doctor` check, or a tool that threw (no device, a timeout, bad arguments). The JSON is still there to read. `logs_crash` is not an error when there was no crash; check `crashed`. JSON-RPC errors are kept for protocol problems (an unknown tool or method).
1042
+ - **Errors.** A tool result has `isError: true` when the command would exit non-zero: a failed build, `check_arkts` with an error or one that could not run, a failed or incomplete test run, a failed smoke check, a `perf_trace` threshold exceeded or capture failed, an `api_show` miss or an `api_search` without hits, a `docs_search` without hits, a `docs_show` of a missing document or heading, or either when the release's docs are not synced (the text says to run `docs sync`), a failing `doctor` check, or a tool that threw (no device, a timeout, bad arguments). The JSON is still there to read. `logs_crash` is not an error when there was no crash; check `crashed`. JSON-RPC errors are kept for protocol problems (an unknown tool or method).
1012
1043
  - **Images.** `screenshot` returns the capture as an `image/jpeg` content block downscaled to `maxDim` (with `grid`, the 10x10 coordinate grid), plus its dimensions as JSON; `crop` and `target*` return a full-resolution crop, `contactSheet` the tiled burst, `diff` the comparison and the current screen. `output` also writes the image to a file (with `diff`, the full-resolution capture, usable as a later baseline).
1013
1044
  - **Layouts.** `dump_layout` without `flat` or `summary` returns the whole tree; `flat` (text) is the cheap way to read a screen. `output` saves what is returned, so `{ "output": "before.json" }` then `{ "diff": "before.json" }` compares two moments.
1014
1045
 
package/dist/oniro-app.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/bin/oniro-app.ts
4
4
  import { Command as Command11, CommanderError } from "commander";
5
- import { OniroError as OniroError6 } from "@oniroproject/core";
5
+ import { OniroError as OniroError7 } from "@oniroproject/core";
6
6
 
7
7
  // src/commands/sdk.ts
8
8
  import * as fs from "fs";
@@ -80,7 +80,8 @@ var ENV_VAR_BY_KEY = {
80
80
  harmonyosSdkPath: "ONIRO_HARMONYOS_SDK_PATH",
81
81
  harmonyosAuthDir: "ONIRO_HARMONYOS_AUTH_DIR",
82
82
  harmonyosSigningDir: "ONIRO_HARMONYOS_SIGNING_DIR",
83
- cacheDir: "ONIRO_CACHE_DIR"
83
+ cacheDir: "ONIRO_CACHE_DIR",
84
+ docsDir: "ONIRO_DOCS_DIR"
84
85
  };
85
86
  function createEnvConfig(env = process.env) {
86
87
  return {
@@ -3222,10 +3223,160 @@ function registerApiCommand(program) {
3222
3223
  });
3223
3224
  }
3224
3225
 
3226
+ // src/commands/docs.ts
3227
+ import {
3228
+ DocsIndex,
3229
+ docsDirFor,
3230
+ docsRefFor,
3231
+ docsTree,
3232
+ readSdkRelease,
3233
+ syncDocs,
3234
+ OniroError as OniroError5
3235
+ } from "@oniroproject/core";
3236
+ function selectDocs(opts) {
3237
+ const { config, logger } = getRuntime();
3238
+ if (opts.ref) return { ref: opts.ref, dir: docsDirFor(config, opts.ref) };
3239
+ const api = opts.api !== void 0 ? Number(opts.api) : void 0;
3240
+ let sel;
3241
+ try {
3242
+ sel = selectSdk(opts);
3243
+ } catch (err) {
3244
+ if (api === void 0 || !Number.isInteger(api) || api <= 0) throw err;
3245
+ const ref2 = docsRefFor({ api });
3246
+ logger.warn(`The API ${api} SDK is not installed; taking the ${ref2} docs for it.`);
3247
+ return { ref: ref2, dir: docsDirFor(config, ref2), sdk: { api } };
3248
+ }
3249
+ const release = readSdkRelease(sel.sdkDir);
3250
+ const ref = docsRefFor({ api: release.api ?? sel.api, version: release.version });
3251
+ return { ref, dir: docsDirFor(config, ref), sdk: { dir: sel.sdkDir, api: release.api ?? sel.api, version: release.version } };
3252
+ }
3253
+ function docsJson(sel, tree) {
3254
+ return { ref: sel.ref, dir: sel.dir, tree, sdk: sel.sdk };
3255
+ }
3256
+ function openDocs(sel) {
3257
+ const tree = docsTree(sel.dir);
3258
+ if (!tree) {
3259
+ const flag = sel.sdk?.api !== void 0 ? ` --api ${sel.sdk.api}` : sel.ref ? ` --ref ${sel.ref}` : "";
3260
+ throw new OniroError5(
3261
+ `The ${sel.ref} docs are not synced (no ${sel.dir}). Run \`oniro-app docs sync${flag}\` first (a partial clone of gitcode.com/openharmony/docs, about 100 MB).`
3262
+ );
3263
+ }
3264
+ return { tree, index: DocsIndex.load(tree) };
3265
+ }
3266
+ function runDocsSearch(query, opts) {
3267
+ const sel = selectDocs(opts);
3268
+ const { tree, index } = openDocs(sel);
3269
+ const limit = opts.limit !== void 0 ? Number(opts.limit) : void 0;
3270
+ if (limit !== void 0 && (!Number.isInteger(limit) || limit <= 0)) {
3271
+ throw new OniroError5(`--limit expects a positive number, got "${opts.limit}".`);
3272
+ }
3273
+ const result = index.search(query, { limit, section: opts.section });
3274
+ return { json: { docs: docsJson(sel, tree), ...result }, result, tree };
3275
+ }
3276
+ function runDocsShow(docPath, opts) {
3277
+ const sel = selectDocs(opts);
3278
+ const { tree, index } = openDocs(sel);
3279
+ const maxLines = opts.lines !== void 0 ? Number(opts.lines) : void 0;
3280
+ if (maxLines !== void 0 && (!Number.isInteger(maxLines) || maxLines <= 0)) {
3281
+ throw new OniroError5(`--lines expects a positive number, got "${opts.lines}".`);
3282
+ }
3283
+ const result = index.show(docPath, { heading: opts.heading, maxLines });
3284
+ return { json: { docs: docsJson(sel, tree), ...result }, result, tree };
3285
+ }
3286
+ async function runDocsSync(opts) {
3287
+ const { logger } = getRuntime();
3288
+ const sel = selectDocs(opts);
3289
+ const result = await syncDocs({ ref: sel.ref, dir: sel.dir, logger });
3290
+ const tree = docsTree(sel.dir);
3291
+ if (tree) DocsIndex.forget(tree);
3292
+ return { json: { docs: docsJson(sel, tree ?? sel.dir), sdk: sel.sdk, ...result }, result, sel };
3293
+ }
3294
+ function formatDocHit(h) {
3295
+ const lines = [`${h.path} \u2014 ${h.title}`];
3296
+ if (h.headings.length) lines.push(` sections: ${h.headings.map((x) => `"${x.text}" (line ${x.line})`).join(", ")}`);
3297
+ if (h.snippet) lines.push(` ${h.snippet}`);
3298
+ return lines.join("\n");
3299
+ }
3300
+ function registerDocsCommand(program) {
3301
+ const docs = program.command("docs").description(
3302
+ "Search the OpenHarmony application-development documentation (gitcode.com/openharmony/docs, `en/application-dev`) of the release matching the SDK: usage guides, kit API prose, business error codes and platform behaviour, which the SDK typings behind `api search` do not say. `docs sync` clones the release's tree once (a partial, sparse clone without figures, about 100 MB) under <sdkRootDir>/docs (ONIRO_DOCS_DIR)."
3303
+ );
3304
+ const docsOptions = (cmd) => cmd.option(
3305
+ "--api <level>",
3306
+ "The SDK API level whose release picks the docs. Default: the project's compileSdkVersion (from the current directory or --project), else the newest installed SDK."
3307
+ ).option("--sdk <dir>", "An SDK API directory (holding ets/) whose version picks the docs.").option("--project <dir>", "The project whose compileSdkVersion picks the SDK (default: the current directory).").option("--ref <branch>", "A docs branch or tag (`OpenHarmony-6.1-Release`, `master`) instead of the SDK-chosen one.");
3308
+ docsOptions(
3309
+ docs.command("sync").description(
3310
+ "Clone the docs of the release matching the SDK (6.1.0.x \u2192 OpenHarmony-6.1-Release), or bring an existing clone up to date. Needs git."
3311
+ )
3312
+ ).option("--json", "Print JSON: docs (ref, dir, tree, sdk), files, updated, commit.").action(async (opts) => {
3313
+ const { json, result, sel } = await runDocsSync(opts);
3314
+ if (opts.json) process.stdout.write(`${JSON.stringify(json, null, 2)}
3315
+ `);
3316
+ else {
3317
+ process.stdout.write(
3318
+ `${result.updated ? "Updated" : "Cloned"} the ${sel.ref} docs${result.commit ? ` (${result.commit})` : ""}: ${result.files} documents in ${result.dir}
3319
+ `
3320
+ );
3321
+ }
3322
+ });
3323
+ docsOptions(
3324
+ docs.command("search <query...>").description(
3325
+ "Ranked search: documents containing every word (any word when none does), scored by where the words occur (title, headings, path, body); a camelCase or dotted identifier also matches by its parts. Each hit names the sections that match, to read with `docs show`. Exits 1 when nothing matches."
3326
+ )
3327
+ ).option("--limit <n>", "At most n hits.", "8").option("--section <dir>", "Only documents under this subtree: `ui`, `database`, `reference/errorcodes`, `application-models`.").option("--json", "Print JSON: docs (ref, dir, tree, sdk), query, terms, total, hits (path, title, score, headings, snippet).").action((words, opts) => {
3328
+ const { json, result, tree } = runDocsSearch(words.join(" "), opts);
3329
+ if (opts.json) {
3330
+ process.stdout.write(`${JSON.stringify(json, null, 2)}
3331
+ `);
3332
+ } else {
3333
+ for (const h of result.hits) process.stdout.write(`${formatDocHit(h)}
3334
+
3335
+ `);
3336
+ const shown = result.hits.length < result.total ? `${result.hits.length} of ${result.total}` : `${result.total}`;
3337
+ process.stdout.write(
3338
+ result.total ? `${shown} document${result.total === 1 ? "" : "s"} match; paths are relative to ${tree}.
3339
+ ` : "No document matches; try fewer or different words (component or kit names, decorators, an error code).\n"
3340
+ );
3341
+ }
3342
+ process.exitCode = result.total ? 0 : 1;
3343
+ });
3344
+ docsOptions(
3345
+ docs.command("show <path>").description(
3346
+ "One document (a path `docs search` printed, relative to en/application-dev), or one section of it by heading. Images and inline HTML are dropped; long documents are cut at --lines with a list of their sections. Exits 1 when the document or heading does not exist, suggesting close paths or the headings."
3347
+ )
3348
+ ).option("--heading <text>", "A heading (or a distinctive part of it, case-insensitive): only that section.").option("--lines <n>", "At most n lines (default 250).").option(
3349
+ "--json",
3350
+ "Print JSON: docs (ref, dir, tree, sdk), found, path, text, truncated, lines, headings; on a miss error and suggestions."
3351
+ ).action((docPath, opts) => {
3352
+ const { json, result } = runDocsShow(docPath, opts);
3353
+ if (opts.json) {
3354
+ process.stdout.write(`${JSON.stringify(json, null, 2)}
3355
+ `);
3356
+ } else if (result.found) {
3357
+ process.stdout.write(`${result.text}
3358
+ `);
3359
+ if (!opts.heading && result.headings.length) {
3360
+ process.stdout.write(`
3361
+ [sections: ${result.headings.slice(0, 40).map((h) => h.text).join(" | ")}]
3362
+ `);
3363
+ }
3364
+ } else {
3365
+ process.stdout.write(`${result.error}
3366
+ `);
3367
+ if (result.suggestions.length) {
3368
+ process.stdout.write(` ${opts.heading ? "headings" : "close paths"}: ${result.suggestions.join(" | ")}
3369
+ `);
3370
+ }
3371
+ }
3372
+ process.exitCode = result.found ? 0 : 1;
3373
+ });
3374
+ }
3375
+
3225
3376
  // src/commands/serve.ts
3226
3377
  import * as fs10 from "fs";
3227
3378
  import * as path19 from "path";
3228
- import { OniroError as OniroError5 } from "@oniroproject/core";
3379
+ import { OniroError as OniroError6 } from "@oniroproject/core";
3229
3380
 
3230
3381
  // src/mcp/protocol.ts
3231
3382
  import * as readline2 from "readline";
@@ -4488,6 +4639,76 @@ var apiShow = {
4488
4639
  return jsonResult(run.json, !run.found);
4489
4640
  }
4490
4641
  };
4642
+ var DOCS_PROPS = {
4643
+ api: int("The SDK API level whose release picks the docs (default: the project's compileSdkVersion, else the newest installed SDK)."),
4644
+ sdk: str("An SDK API directory (holding ets/) whose version picks the docs."),
4645
+ ref: str("A docs branch or tag (`OpenHarmony-6.1-Release`, `master`) instead of the SDK-chosen one."),
4646
+ ...PROJECT
4647
+ };
4648
+ function docsOpts(a, ctx) {
4649
+ return {
4650
+ api: a.api !== void 0 ? String(a.api) : void 0,
4651
+ sdk: pathArg(a, "sdk", ctx),
4652
+ ref: a.ref,
4653
+ project: projectOf(a, ctx)
4654
+ };
4655
+ }
4656
+ var docsSearch = {
4657
+ name: "docs_search",
4658
+ title: "Search the OpenHarmony docs",
4659
+ description: "Ranked search of the OpenHarmony application-development documentation of the release matching the SDK (`oniro-app docs search --json`): usage guides (ArkUI components, navigation, state management, data storage, abilities), kit API prose, business error codes and platform behaviour, which the SDK typings behind api_search do not say. Documents containing every word rank by where the words occur (title, headings, path, body); each hit names the matching sections, to read with docs_show. Use domain words (a component, kit or decorator name, an error code), not sentences. Returns docs (ref, dir, tree, sdk), query, terms, total, hits (path, title, score, headings [{line, text}], snippet). isError when nothing matches, or when the docs are not synced (`oniro-app docs sync`).",
4660
+ inputSchema: object(
4661
+ {
4662
+ query: str('The search words, e.g. "LazyForEach reuse", "relationalStore insert", "14800011".'),
4663
+ limit: int("At most n hits.", { minimum: 1, maximum: 30, default: 8 }),
4664
+ section: str("Only documents under this subtree: `ui`, `database`, `reference/errorcodes`, `application-models`."),
4665
+ ...DOCS_PROPS
4666
+ },
4667
+ ["query"]
4668
+ ),
4669
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
4670
+ changesDevice: false,
4671
+ async call(a, ctx) {
4672
+ try {
4673
+ const run = runDocsSearch(a.query, {
4674
+ ...docsOpts(a, ctx),
4675
+ limit: a.limit !== void 0 ? String(a.limit) : void 0,
4676
+ section: a.section
4677
+ });
4678
+ return jsonResult(run.json, run.result.total === 0);
4679
+ } catch (err) {
4680
+ return errorResult(message(err));
4681
+ }
4682
+ }
4683
+ };
4684
+ var docsShow = {
4685
+ name: "docs_show",
4686
+ title: "Read one OpenHarmony doc",
4687
+ description: "One documentation file (a path docs_search returned, relative to en/application-dev), or one section of it by heading (`oniro-app docs show --json`). Images and inline HTML are dropped; a long document is cut at maxLines with the list of its sections, so ask for a heading rather than the whole file. Returns docs, found, path, text, truncated, lines, headings; isError with suggestions (close paths, or the headings when the heading did not match).",
4688
+ inputSchema: object(
4689
+ {
4690
+ path: str("The document path, e.g. `ui/arkts-navigation-jump.md`."),
4691
+ heading: str("A heading (or a distinctive part of it, case-insensitive): only that section."),
4692
+ maxLines: int("At most n lines.", { minimum: 20, maximum: 1200, default: 250 }),
4693
+ ...DOCS_PROPS
4694
+ },
4695
+ ["path"]
4696
+ ),
4697
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
4698
+ changesDevice: false,
4699
+ async call(a, ctx) {
4700
+ try {
4701
+ const run = runDocsShow(a.path, {
4702
+ ...docsOpts(a, ctx),
4703
+ heading: a.heading,
4704
+ lines: a.maxLines !== void 0 ? String(a.maxLines) : void 0
4705
+ });
4706
+ return jsonResult(run.json, !run.result.found);
4707
+ } catch (err) {
4708
+ return errorResult(message(err));
4709
+ }
4710
+ }
4711
+ };
4491
4712
  var doctor = {
4492
4713
  name: "doctor",
4493
4714
  title: "Check the toolchain",
@@ -4552,6 +4773,8 @@ var ALL_TOOLS = [
4552
4773
  screens,
4553
4774
  apiSearch,
4554
4775
  apiShow,
4776
+ docsSearch,
4777
+ docsShow,
4555
4778
  doctor,
4556
4779
  devices
4557
4780
  ];
@@ -4568,7 +4791,7 @@ function selectTools(names, readOnly) {
4568
4791
  function parseToolList(raw) {
4569
4792
  if (raw === void 0) return void 0;
4570
4793
  const names = raw.split(",").map((s) => s.trim()).filter(Boolean);
4571
- if (!names.length) throw new OniroError5("--tools needs at least one tool name.");
4794
+ if (!names.length) throw new OniroError6("--tools needs at least one tool name.");
4572
4795
  return names;
4573
4796
  }
4574
4797
  function instructions(ctx, toolCount) {
@@ -4581,9 +4804,9 @@ function registerServeCommand(program) {
4581
4804
  ).allowExcessArguments(false).option("--project <dir>", "Project the tools work on unless a call names another (default: the current directory).").option("--device <serial>", "Device the tools target unless a call names another (default: the only connected one).").option("--tools <list>", `Expose only these tools, comma-separated (of ${TOOL_NAMES.join(", ")}).`).option("--read-only", "Leave out the tools that change the device: apply, launch, input, test.").action(async (opts) => {
4582
4805
  const { logger } = getRuntime();
4583
4806
  const projectDir = path19.resolve(opts.project ?? process.cwd());
4584
- if (!fs10.existsSync(projectDir)) throw new OniroError5(`--project ${projectDir} does not exist.`);
4807
+ if (!fs10.existsSync(projectDir)) throw new OniroError6(`--project ${projectDir} does not exist.`);
4585
4808
  const tools = selectTools(parseToolList(opts.tools), !!opts.readOnly);
4586
- if (!tools.length) throw new OniroError5("No tool left to serve: --read-only removed every tool --tools named.");
4809
+ if (!tools.length) throw new OniroError6("No tool left to serve: --read-only removed every tool --tools named.");
4587
4810
  const context = { projectDir, deviceSerial: opts.device };
4588
4811
  const server = new McpServer({
4589
4812
  name: "oniro-app",
@@ -4728,7 +4951,7 @@ function registerReplayCommand(program) {
4728
4951
  }
4729
4952
 
4730
4953
  // src/bin/oniro-app.ts
4731
- var CLI_VERSION = "0.18.0";
4954
+ var CLI_VERSION = "0.19.0";
4732
4955
  function buildProgram() {
4733
4956
  const program = new Command11().name("oniro-app").description(
4734
4957
  "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."
@@ -4754,6 +4977,7 @@ function buildProgram() {
4754
4977
  registerDoctorCommand(program);
4755
4978
  registerScreensCommand(program);
4756
4979
  registerApiCommand(program);
4980
+ registerDocsCommand(program);
4757
4981
  registerTestCommand(program);
4758
4982
  registerInputCommand(program);
4759
4983
  registerRecordCommand(program);
@@ -4773,7 +4997,7 @@ async function main() {
4773
4997
  process.exitCode = err.exitCode;
4774
4998
  return;
4775
4999
  }
4776
- const message2 = err instanceof OniroError6 ? err.message : err instanceof Error ? err.message : String(err);
5000
+ const message2 = err instanceof OniroError7 ? err.message : err instanceof Error ? err.message : String(err);
4777
5001
  const stack = process.env.ONIRO_DEBUG === "1" && err instanceof Error && err.stack ? `${err.stack}
4778
5002
  ` : "";
4779
5003
  process.stderr.write(`[error] ${message2}