diff --git a/.gitea/workflows/sync-release.yml b/.gitea/workflows/sync-release.yml
new file mode 100644
index 0000000..cfa6836
--- /dev/null
+++ b/.gitea/workflows/sync-release.yml
@@ -0,0 +1,32 @@
+name: Register plugin release
+on:
+ workflow_dispatch:
+ inputs:
+ repository:
+ description: Plugin repository (owner/name)
+ required: true
+ type: string
+ tag:
+ description: Published release tag
+ required: true
+ type: string
+jobs:
+ register:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: https://github.com/actions/checkout@v4
+ - uses: https://github.com/actions/setup-node@v4
+ with:
+ node-version: '22'
+ - run: corepack enable && corepack pnpm install --frozen-lockfile
+ - name: Register completed release
+ run: node tools/sync-release.mjs --publish
+ env:
+ GITEA_SERVER_URL: ${{ gitea.server_url }}
+ GITEA_TOKEN: ${{ secrets.PLUGIN_RELEASE_TOKEN }}
+ CATALOG_WRITE_TOKEN: ${{ secrets.GITEA_TOKEN }}
+ CATALOG_REPOSITORY: ${{ gitea.repository }}
+ RELEASE_REPOSITORY: ${{ inputs.repository }}
+ RELEASE_TAG: ${{ inputs.tag }}
diff --git a/.gitignore b/.gitignore
index e7c9ba2..b63ec40 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,17 +1,11 @@
node_modules/
dist/
-target/
-.trove/
.env
.env.*
*.curio
-!/source/packages/**/*.curio
.DS_Store
-/curios/*/backend/
-/curios/harmony-connector/legal/sources/
-/curios/harmony-connector/legal/*-COPYING
-/curios/harmony-connector/legal/build.mjs
-/curios/harmony-connector/legal/rust/
-/curios/harmony-connector/legal/hypium/
-# Keep local credentials from the retired companion experiment private.
-/curios/harmony-connector/companion/
+target/
+.trove/
+/curios/
+/shared/
+/vendor/
diff --git a/README.md b/README.md
index a67efc2..984457e 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Trove 官方插件库。发现喜欢的 Curio,装进你的工作台,让常
每个 Curio 专注一件事,用轻巧的独立工具承接日常操作,把更多空间留给手头的工作。
-### Harmony Connector
+### [Harmony Connector](https://git.render.ink/trove-plugins/harmony-connector)
让设备,更近一步。连接 Mac 与鸿蒙手机,整夹收发照片与文档,随时掌握传输速度;管理应用,在独立窗口中投屏,用鼠标和键盘完成日常操作。
diff --git a/curios/.gitkeep b/curios/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/curios/harmony-connector/assets/icon.svg b/curios/harmony-connector/assets/icon.svg
deleted file mode 100644
index 26a13f3..0000000
--- a/curios/harmony-connector/assets/icon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/curios/harmony-connector/build.mjs b/curios/harmony-connector/build.mjs
deleted file mode 100644
index 133b1fc..0000000
--- a/curios/harmony-connector/build.mjs
+++ /dev/null
@@ -1,262 +0,0 @@
-import {
- readFile,
- writeFile,
- mkdir,
- cp,
- copyFile,
- rm,
- readdir,
-} from "node:fs/promises";
-import { createHash } from "node:crypto";
-import { spawnSync } from "node:child_process";
-import { fileURLToPath } from "node:url";
-import path from "node:path";
-
-const plugin = path.dirname(fileURLToPath(import.meta.url));
-const root = path.resolve(plugin, "../..");
-const build = path.join(plugin, "dist/native-deps");
-const prefix = path.join(build, "install");
-const sources = [
- {
- name: "libusb-1.0.30",
- archive: "libusb-1.0.30.tar.bz2",
- sha256: "fea36f34f9156400209595e300840767ab1a385ede1dc7ee893015aea9c6dbaf",
- options: [],
- },
- {
- name: "libmtp-1.1.23",
- archive: "libmtp-1.1.23.tar.gz",
- sha256: "74a2b6e8cb4a0304e95b995496ea3ac644c29371649b892b856e22f12a0bdeed",
- options: ["--disable-mtpz"],
- patch: "libmtp-close-timeout.patch",
- },
-];
-function run(command, args, cwd = plugin, env = {}) {
- const result = spawnSync(command, args, {
- cwd,
- stdio: "inherit",
- env: { ...process.env, ...env },
- });
- if (result.error) throw result.error;
- if (result.status !== 0)
- throw new Error(`${command} exited with ${result.status}`);
-}
-if (process.platform !== "darwin")
- throw new Error("Harmony Connector native builds require macOS.");
-const env = {
- MACOSX_DEPLOYMENT_TARGET: "13.0",
- PKG_CONFIG_PATH: path.join(prefix, "lib/pkgconfig"),
- HARMONY_NATIVE_PREFIX: prefix,
-};
-await mkdir(build, { recursive: true });
-await mkdir(path.join(plugin, "legal/sources"), { recursive: true });
-for (const source of sources) {
- const archive = path.join(root, "vendor/harmony-connector", source.archive);
- const bytes = await readFile(archive);
- if (createHash("sha256").update(bytes).digest("hex") !== source.sha256)
- throw new Error(`Source checksum mismatch: ${source.archive}`);
- const directory = path.join(build, source.name);
- const stamp = path.join(directory, ".harmony-built");
- const patchFile =
- source.patch && path.join(root, "vendor/harmony-connector", source.patch);
- const patchBytes = patchFile ? await readFile(patchFile) : null;
- const patchHash = patchBytes
- ? createHash("sha256").update(patchBytes).digest("hex")
- : "";
- const identity = `${process.arch}:macos13:${source.sha256}${patchHash ? ":" + patchHash : ""}`;
- const previous = await readFile(stamp, "utf8").catch(() => "");
- if (previous !== identity) {
- await rm(directory, { recursive: true, force: true });
- run("tar", ["-xf", archive, "-C", build]);
- if (patchFile) run("patch", ["--batch", "-p1", "-i", patchFile], directory);
- run(
- "./configure",
- [
- `--prefix=${prefix}`,
- "--disable-static",
- "--enable-shared",
- "--disable-dependency-tracking",
- ...source.options,
- ],
- directory,
- env,
- );
- run("make", ["-j4"], directory, env);
- run("make", ["install"], directory, env);
- await writeFile(stamp, identity);
- }
- await copyFile(
- path.join(directory, "COPYING"),
- path.join(plugin, "legal", `${source.name}-COPYING`),
- );
- await copyFile(archive, path.join(plugin, "legal/sources", source.archive));
- if (patchFile)
- await copyFile(patchFile, path.join(plugin, "legal/sources", source.patch));
-}
-if (process.argv.includes("--dependencies-only")) process.exit(0);
-const agentDirectory = path.join(root, "vendor/harmony-connector/hypium");
-const agentProvenance = JSON.parse(
- await readFile(path.join(agentDirectory, "provenance.json"), "utf8"),
-);
-const agentBytes = await readFile(
- path.join(agentDirectory, "uitest_agent_v1.2.2.so"),
-);
-if (
- createHash("sha256").update(agentBytes).digest("hex") !==
- agentProvenance.sha256
-)
- throw new Error("Hypium device agent checksum mismatch");
-await mkdir(path.join(plugin, "legal/hypium"), { recursive: true });
-for (const name of ["LICENSE", "provenance.json"])
- await copyFile(
- path.join(agentDirectory, name),
- path.join(plugin, "legal/hypium", name),
- );
-const latestAgents = JSON.parse(await readFile(path.join(agentDirectory, "provenance-26.json"), "utf8"));
-for (const entry of latestAgents.files) {
- const bytes = await readFile(path.join(agentDirectory, entry.file));
- if (createHash("sha256").update(bytes).digest("hex") !== entry.sha256)
- throw new Error(`Official device component checksum mismatch: ${entry.file}`);
-}
-for (const name of ["LICENSE-26", "provenance-26.json"])
- await copyFile(path.join(agentDirectory, name), path.join(plugin, "legal/hypium", name));
-run(
- "cargo",
- ["build", "--release", "--locked", "--manifest-path", "native/Cargo.toml"],
- plugin,
- env,
-);
-const backend = path.join(plugin, "backend");
-await mkdir(path.join(backend, "lib"), { recursive: true });
-const executable = path.join(backend, "harmony-connector");
-await copyFile(
- path.join(plugin, "native/target/release/harmony-connector"),
- executable,
-);
-const viewer = path.join(backend, "harmony-screen");
-run("xcrun", ["swiftc", "-O", "-target", `${process.arch === "arm64" ? "arm64" : "x86_64"}-apple-macosx13.0`,
- "-framework", "AppKit", "-framework", "QuartzCore", "-framework", "AVFoundation", "-framework", "VideoToolbox",
- "viewer/Protocol.swift", "viewer/VideoDecoder.swift", "viewer/ScreenView.swift", "viewer/Main.swift", "-o", viewer]);
-const libraries = ["libmtp.9.dylib", "libusb-1.0.0.dylib"];
-for (const library of libraries)
- await copyFile(
- path.join(prefix, "lib", library),
- path.join(backend, "lib", library),
- );
-for (const file of [
- executable,
- viewer,
- ...libraries.map((name) => path.join(backend, "lib", name)),
-]) {
- const isExecutable = file === executable || file === viewer;
- const listing = spawnSync("otool", ["-L", file], { encoding: "utf8" });
- if (listing.status !== 0)
- throw new Error("Cannot inspect Mach-O dependencies");
- for (const line of listing.stdout.split("\n").slice(1)) {
- const dependency = line.trim().split(" (")[0];
- if (libraries.includes(path.basename(dependency)) && dependency !== file) {
- run("install_name_tool", [
- "-change",
- dependency,
- `${isExecutable ? "@executable_path/lib" : "@loader_path"}/${path.basename(dependency)}`,
- file,
- ]);
- } else if (
- dependency &&
- !dependency.startsWith("/System/") &&
- !dependency.startsWith("/usr/lib/")
- ) {
- throw new Error(`Unbundled native dependency: ${dependency}`);
- }
- }
- if (!isExecutable)
- run("install_name_tool", [
- "-id",
- `@loader_path/${path.basename(file)}`,
- file,
- ]);
- run("codesign", ["--force", "--sign", "-", file]);
-}
-const ui = path.join(plugin, "dist/ui");
-await rm(ui, { recursive: true, force: true });
-await cp(path.join(plugin, "ui"), ui, {
- recursive: true,
- filter: (source) => !source.endsWith(".test.js"),
-});
-await cp(
- path.dirname(fileURLToPath(import.meta.resolve("@trove/plugin-sdk"))),
- path.join(ui, "sdk"),
- { recursive: true },
-);
-await copyFile(
- path.join(plugin, "build.mjs"),
- path.join(plugin, "legal", "build.mjs"),
-);
-const metadata = spawnSync(
- "cargo",
- [
- "metadata",
- "--locked",
- "--format-version",
- "1",
- "--filter-platform",
- `${process.arch === "arm64" ? "aarch64" : "x86_64"}-apple-darwin`,
- "--manifest-path",
- "native/Cargo.toml",
- ],
- { cwd: plugin, encoding: "utf8", maxBuffer: 10 * 1024 * 1024 },
-);
-if (metadata.status !== 0)
- throw new Error("Cannot collect Rust dependency notices");
-const graph = JSON.parse(metadata.stdout);
-const manifest = JSON.parse(
- await readFile(path.join(plugin, "curio.json"), "utf8"),
-);
-if (
- graph.packages.find((pkg) => pkg.id === graph.resolve.root)?.version !==
- manifest.version
-)
- throw new Error(
- "curio.json and native/Cargo.toml must declare the same version",
- );
-await rm(path.join(plugin, "legal/rust"), { recursive: true, force: true });
-for (const dependency of graph.packages.filter((pkg) => pkg.source)) {
- const directory = path.dirname(dependency.manifest_path);
- const destination = path.join(
- plugin,
- "legal/rust",
- `${dependency.name}-${dependency.version}`,
- );
- await mkdir(destination, { recursive: true });
- await writeFile(
- path.join(destination, "package.json"),
- JSON.stringify(
- {
- name: dependency.name,
- version: dependency.version,
- license: dependency.license,
- repository: dependency.repository,
- },
- null,
- 2,
- ) + "\n",
- );
- let noticeCount = 0;
- for (const entry of await readdir(directory, { withFileTypes: true })) {
- if (
- entry.isFile() &&
- /^(license|licence|copying|copyright|notice)([.-]|$)/i.test(entry.name)
- ) {
- await copyFile(
- path.join(directory, entry.name),
- path.join(destination, entry.name),
- );
- noticeCount++;
- }
- }
- if (!noticeCount)
- throw new Error(`Missing license notice: ${dependency.name}`);
-}
-console.log(
- `Built Harmony Connector for ${process.arch}; USB libraries and their corresponding source are bundled.`,
-);
diff --git a/curios/harmony-connector/curio.json b/curios/harmony-connector/curio.json
deleted file mode 100644
index 5bb59c1..0000000
--- a/curios/harmony-connector/curio.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "schemaVersion": 1,
- "id": "dev.trove.harmony-connector",
- "name": "Harmony Connector",
- "description": "连接鸿蒙设备,收发完整文件夹、管理应用,在独立窗口中投屏并通过键鼠操作。",
- "version": "0.5.2",
- "engine": { "trove": ">=0.1.0 <1.0.0" },
- "icon": "assets/icon.svg",
- "ui": {
- "entry": "dist/ui/index.html",
- "window": { "title": "Harmony Connector", "width": 1080, "height": 760, "minWidth": 760, "minHeight": 580, "resizable": true }
- },
- "backend": {
- "entry": "backend/harmony-connector",
- "lifecycle": "on-demand", "transport": "unix", "startWithUi": true,
- "startupTimeoutMs": 5000, "shutdownGraceMs": 5000, "idleTimeoutMs": 0
- },
- "services": { "requires": [{ "id": "host.dialog", "version": "^1.0.0" }, { "id": "host.runtime", "version": "^1.0.0" }] },
- "build": { "command": ["node", "build.mjs"] },
- "package": { "files": ["dist/ui", "backend", "assets", "legal"] }
-}
diff --git a/curios/harmony-connector/legal/THIRD-PARTY.md b/curios/harmony-connector/legal/THIRD-PARTY.md
deleted file mode 100644
index 09bbd06..0000000
--- a/curios/harmony-connector/legal/THIRD-PARTY.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# Harmony Connector — Third-party software
-
-Harmony Connector dynamically links the following libraries. This distribution
-includes their original license texts, complete upstream source archives and
-the patches applied to those sources.
-
-| Library | Version | Source | License text in this package |
-| --- | --- | --- | --- |
-| libmtp | 1.1.23 | https://github.com/libmtp/libmtp | `libmtp-1.1.23-COPYING` |
-| libusb | 1.0.30 | https://github.com/libusb/libusb | `libusb-1.0.30-COPYING` |
-
-Copyright belongs to the authors identified in the corresponding source files.
-libmtp's distributed COPYING is LGPL 2.1; individual source headers retain their
-original version 2 or later notices. libusb is LGPL 2.1 or later. These notices
-do not replace the original terms. The source archives are in `sources/`.
-
-The native executable also uses Rust crates listed in `rust/`, with the license,
-copyright and notice files provided by each crate. Trove's UI SDK is distributed
-from the versioned SDK archive in the source repository.
-
-The executable also embeds the unchanged `uitest_agent_v1.2.2.so` distributed in
-`xdevice-devicetest` 6.1.0.210. Its package includes the Apache License 2.0; that
-license and the original download URL, wheel member and SHA-256 checksums are in
-`hypium/`. The binary is not modified or stripped. It is copied to a temporary
-device file for an authorized HDC session and removed when that session closes.
-The upstream wheel does not include this native agent's source; embedding this
-binary does not make it independently rebuildable from this repository.
-
-The native viewer additionally uses the unmodified Huawei components distributed
-in `xdevice-devicetest` 26.0.0.500 (Hypium 26):
-
-- `devicetest/res/prototype/native/uitest_agent_v1.2.3.so` for touch control.
-- `devicetest/res/recorder/libscrcpy_server_unix_*.z.so` for H.264 video:
- 6.6-20260629, 6.6-20260418, 6.5-20260313, 6.4-20260113 and 6.3.1-20260113.
- These original official variants are tried in that order when a device cannot
- start the previous variant; all use a device Unix socket.
-
-Their wheel license is preserved in `hypium/LICENSE-26`; file identities and
-SHA-256 checksums are in `hypium/provenance-26.json`. Upstream copyright belongs
-to Huawei Device Co., Ltd. and the authors identified in the official package.
-These signed device binaries are neither modified nor stripped. The package
-contains no separate NOTICE for them. This attribution is written by Trove;
-it is not an upstream NOTICE. The macOS viewer is built from this repository's
-Swift sources and uses the macOS AppKit, VideoToolbox and AVFoundation frameworks.
-
-## Building the distributed libraries
-
-The exact build commands and SHA-256 pins are in the accompanying `build.mjs`.
-The source repository contains the same script at
-`curios/harmony-connector/build.mjs` and archives under `vendor/harmony-connector/`.
-Requires macOS, Xcode Command Line Tools, make and pkg-config. Libraries are
-compiled with deployment target macOS 13.0, shared linkage, no static library,
-and libmtp's MTPZ support disabled. `sources/libmtp-close-timeout.patch` bounds
-CloseSession to two seconds without reducing file-transfer timeouts. The build
-cache includes the patch hash; the existing release/reset/close sequence is retained.
-Mach-O install names are adjusted after compilation; libraries are ad-hoc signed.
-
-From the source repository, run:
-
-```sh
-node curios/harmony-connector/build.mjs --dependencies-only
-```
-
-The resulting libraries are under
-`curios/harmony-connector/dist/native-deps/install/lib/`.
-
-## Replacing or modifying the libraries
-
-You may modify and replace these libraries with ABI-compatible versions. No
-restriction is imposed on reverse engineering needed to debug such modifications.
-Copy compatible libraries to the plugin's `backend/lib/`, retaining their names
-`libmtp.9.dylib` and `libusb-1.0.0.dylib`. Use `install_name_tool` to set the library
-IDs and inter-library references to `@loader_path/` as the build
-script does. Ad-hoc sign each changed Mach-O using `codesign --force --sign -`.
-
-Use the source project and Trove's local development/install workflow for modified
-packages. Change the plugin version before packing a modified release: published
-source artifacts are immutable, and changing their checksum in place is not the
-way to install a modified library. The executable has no library validation or
-publisher signature restriction that prevents loading a compatible replacement.
-
-The LGPL libraries are provided without warranty, as detailed in their licenses.
diff --git a/curios/harmony-connector/native/Cargo.lock b/curios/harmony-connector/native/Cargo.lock
deleted file mode 100644
index 822bd88..0000000
--- a/curios/harmony-connector/native/Cargo.lock
+++ /dev/null
@@ -1,769 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "adler2"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
-
-[[package]]
-name = "anyhow"
-version = "1.0.104"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
-
-[[package]]
-name = "arbitrary"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
-dependencies = [
- "derive_arbitrary",
-]
-
-[[package]]
-name = "atomic-waker"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
-
-[[package]]
-name = "autocfg"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
-
-[[package]]
-name = "base64"
-version = "0.22.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
-
-[[package]]
-name = "bitflags"
-version = "2.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
-
-[[package]]
-name = "block-buffer"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "bumpalo"
-version = "3.20.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
-
-[[package]]
-name = "bytemuck"
-version = "1.25.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
-
-[[package]]
-name = "byteorder-lite"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
-
-[[package]]
-name = "bytes"
-version = "1.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
-
-[[package]]
-name = "cc"
-version = "1.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80"
-dependencies = [
- "find-msvc-tools",
- "shlex",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
-
-[[package]]
-name = "cpufeatures"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "crc32fast"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "crypto-common"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
-dependencies = [
- "generic-array",
- "typenum",
-]
-
-[[package]]
-name = "derive_arbitrary"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.119",
-]
-
-[[package]]
-name = "digest"
-version = "0.10.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
-dependencies = [
- "block-buffer",
- "crypto-common",
-]
-
-[[package]]
-name = "either"
-version = "1.18.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
-
-[[package]]
-name = "equivalent"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
-
-[[package]]
-name = "errno"
-version = "0.3.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
-dependencies = [
- "libc",
- "windows-sys",
-]
-
-[[package]]
-name = "fastrand"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
-
-[[package]]
-name = "fdeflate"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
-dependencies = [
- "simd-adler32",
-]
-
-[[package]]
-name = "find-msvc-tools"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d"
-
-[[package]]
-name = "flate2"
-version = "1.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
-dependencies = [
- "crc32fast",
- "miniz_oxide 0.9.1",
- "zlib-rs",
-]
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "futures-core"
-version = "0.3.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
-
-[[package]]
-name = "futures-sink"
-version = "0.3.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d"
-
-[[package]]
-name = "generic-array"
-version = "0.14.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
-dependencies = [
- "cfg-if",
- "libc",
- "r-efi",
-]
-
-[[package]]
-name = "h2"
-version = "0.4.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
-dependencies = [
- "atomic-waker",
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "http",
- "indexmap",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
-name = "harmony-connector"
-version = "0.5.2"
-dependencies = [
- "anyhow",
- "base64",
- "bytes",
- "cc",
- "h2",
- "http",
- "image",
- "libc",
- "png",
- "prost",
- "serde",
- "serde_json",
- "sha2",
- "tempfile",
- "tokio",
- "zip",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.17.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
-
-[[package]]
-name = "http"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
-dependencies = [
- "bytes",
- "itoa",
-]
-
-[[package]]
-name = "image"
-version = "0.25.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
-dependencies = [
- "bytemuck",
- "byteorder-lite",
- "moxcms",
- "num-traits",
- "png",
- "zune-core",
- "zune-jpeg",
-]
-
-[[package]]
-name = "indexmap"
-version = "2.14.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
-dependencies = [
- "equivalent",
- "hashbrown",
-]
-
-[[package]]
-name = "itertools"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
-
-[[package]]
-name = "libc"
-version = "0.2.189"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
-
-[[package]]
-name = "log"
-version = "0.4.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
-
-[[package]]
-name = "memchr"
-version = "2.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
-
-[[package]]
-name = "miniz_oxide"
-version = "0.8.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
-dependencies = [
- "adler2",
- "simd-adler32",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
-dependencies = [
- "adler2",
- "simd-adler32",
-]
-
-[[package]]
-name = "mio"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8"
-dependencies = [
- "libc",
- "wasi",
- "windows-sys",
-]
-
-[[package]]
-name = "moxcms"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
-dependencies = [
- "num-traits",
- "pxfm",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.21.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
-
-[[package]]
-name = "png"
-version = "0.18.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
-dependencies = [
- "bitflags",
- "crc32fast",
- "fdeflate",
- "flate2",
- "miniz_oxide 0.8.9",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.107"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "prost"
-version = "0.14.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1"
-dependencies = [
- "bytes",
- "prost-derive",
-]
-
-[[package]]
-name = "prost-derive"
-version = "0.14.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf"
-dependencies = [
- "anyhow",
- "itertools",
- "proc-macro2",
- "quote",
- "syn 2.0.119",
-]
-
-[[package]]
-name = "pxfm"
-version = "0.1.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
-
-[[package]]
-name = "quote"
-version = "1.0.47"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "r-efi"
-version = "6.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
-
-[[package]]
-name = "rustix"
-version = "1.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
-dependencies = [
- "bitflags",
- "errno",
- "libc",
- "linux-raw-sys",
- "windows-sys",
-]
-
-[[package]]
-name = "serde"
-version = "1.0.229"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
-dependencies = [
- "serde_core",
- "serde_derive",
-]
-
-[[package]]
-name = "serde_core"
-version = "1.0.229"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.229"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 3.0.5",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.151"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
-dependencies = [
- "itoa",
- "memchr",
- "serde",
- "serde_core",
- "zmij",
-]
-
-[[package]]
-name = "sha2"
-version = "0.10.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
-[[package]]
-name = "shlex"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
-
-[[package]]
-name = "simd-adler32"
-version = "0.3.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
-
-[[package]]
-name = "slab"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
-
-[[package]]
-name = "socket2"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
-dependencies = [
- "libc",
- "windows-sys",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.119"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "syn"
-version = "3.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "tempfile"
-version = "3.27.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
-dependencies = [
- "fastrand",
- "getrandom",
- "once_cell",
- "rustix",
- "windows-sys",
-]
-
-[[package]]
-name = "tokio"
-version = "1.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
-dependencies = [
- "bytes",
- "libc",
- "mio",
- "pin-project-lite",
- "socket2",
- "tokio-macros",
- "windows-sys",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "2.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 3.0.5",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.7.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "libc",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "tracing"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
-dependencies = [
- "pin-project-lite",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
-dependencies = [
- "once_cell",
-]
-
-[[package]]
-name = "typenum"
-version = "1.20.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
-
-[[package]]
-name = "version_check"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
-[[package]]
-name = "wasi"
-version = "0.11.1+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
-
-[[package]]
-name = "windows-link"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
-
-[[package]]
-name = "windows-sys"
-version = "0.61.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
-dependencies = [
- "windows-link",
-]
-
-[[package]]
-name = "zip"
-version = "4.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
-dependencies = [
- "arbitrary",
- "crc32fast",
- "flate2",
- "indexmap",
- "memchr",
- "zopfli",
-]
-
-[[package]]
-name = "zlib-rs"
-version = "0.6.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12"
-
-[[package]]
-name = "zmij"
-version = "1.0.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
-
-[[package]]
-name = "zopfli"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
-dependencies = [
- "bumpalo",
- "crc32fast",
- "log",
- "simd-adler32",
-]
-
-[[package]]
-name = "zune-core"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b"
-
-[[package]]
-name = "zune-jpeg"
-version = "0.5.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
-dependencies = [
- "zune-core",
-]
diff --git a/curios/harmony-connector/native/Cargo.toml b/curios/harmony-connector/native/Cargo.toml
deleted file mode 100644
index ba2c416..0000000
--- a/curios/harmony-connector/native/Cargo.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[package]
-name = "harmony-connector"
-version = "0.5.2"
-edition = "2021"
-publish = false
-
-[dependencies]
-anyhow = "1"
-serde = { version = "1", features = ["derive"] }
-serde_json = "1"
-tempfile = "3"
-libc = "0.2"
-sha2 = "0.10"
-zip = { version = "4.6", default-features = false, features = ["deflate"] }
-png = "0.18"
-image = { version = "0.25", default-features = false, features = ["jpeg", "png"] }
-base64 = "0.22"
-bytes = "1"
-h2 = "0.4"
-http = "1"
-prost = "0.14"
-tokio = { version = "1", features = ["rt", "net", "time", "sync", "macros"] }
-
-[build-dependencies]
-cc = "1"
diff --git a/curios/harmony-connector/native/build.rs b/curios/harmony-connector/native/build.rs
deleted file mode 100644
index ed72123..0000000
--- a/curios/harmony-connector/native/build.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use std::{env, path::PathBuf};
-fn main() {
- let prefix = PathBuf::from(
- env::var("HARMONY_NATIVE_PREFIX")
- .expect("Build native dependencies with node ../build.mjs first"),
- );
- cc::Build::new()
- .file("src/mtp_bridge.c")
- .include(prefix.join("include"))
- .warnings(true)
- .compile("harmony_mtp_bridge");
- println!(
- "cargo:rustc-link-search=native={}",
- prefix.join("lib").display()
- );
- println!("cargo:rustc-link-lib=dylib=mtp");
- println!("cargo:rustc-link-lib=dylib=usb-1.0");
- println!("cargo:rerun-if-env-changed=HARMONY_NATIVE_PREFIX");
- println!("cargo:rerun-if-changed=src/mtp_bridge.c");
-}
diff --git a/curios/harmony-connector/native/src/command.rs b/curios/harmony-connector/native/src/command.rs
deleted file mode 100644
index 02d6fe4..0000000
--- a/curios/harmony-connector/native/src/command.rs
+++ /dev/null
@@ -1,186 +0,0 @@
-use crate::model::{Fault, Result};
-use std::{
- io::{ErrorKind, Read},
- os::{fd::AsRawFd, unix::process::CommandExt},
- path::Path,
- process::{Command, Stdio},
- sync::atomic::{AtomicBool, Ordering},
- thread,
- time::{Duration, Instant},
-};
-
-fn nonblocking(stream: &impl AsRawFd) -> std::io::Result<()> {
- let fd = stream.as_raw_fd();
- let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
- if flags < 0 || unsafe { libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK) } < 0 {
- return Err(std::io::Error::last_os_error());
- }
- Ok(())
-}
-fn drain(stream: &mut impl Read, output: &mut Vec, limit: usize) -> Result {
- let mut buffer = [0; 32 * 1024];
- loop {
- match stream.read(&mut buffer) {
- Ok(0) => return Ok(true),
- Ok(size) => {
- if output.len().saturating_add(size) > limit {
- return Err(Fault::new(
- "hdc.output_too_large",
- "工具输出超过限制,操作已停止。",
- ));
- }
- output.extend_from_slice(&buffer[..size]);
- }
- Err(e) if e.kind() == ErrorKind::WouldBlock => return Ok(false),
- Err(e) if e.kind() == ErrorKind::Interrupted => continue,
- Err(e) => return Err(e.into()),
- }
- }
-}
-pub fn run(
- executable: &Path,
- args: &[String],
- timeout: Duration,
- cancel: &AtomicBool,
- limit: usize,
-) -> Result {
- if cancel.load(Ordering::Acquire) {
- return Err(Fault::new("operation.cancelled", "操作已取消。"));
- }
- let mut child = Command::new(executable)
- .args(args)
- .stdin(Stdio::null())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .process_group(0)
- .spawn()
- .map_err(|e| {
- Fault::new(
- "hdc.tool_unavailable",
- format!("无法运行工具:{e}。请保留官方工具旁的依赖文件。"),
- )
- })?;
- let result = (|| {
- let mut out = child.stdout.take().unwrap();
- let mut err = child.stderr.take().unwrap();
- nonblocking(&out)?;
- nonblocking(&err)?;
- let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
- let started = Instant::now();
- let status = loop {
- if cancel.load(Ordering::Acquire) {
- return Err(Fault::new(
- "operation.cancelled",
- "操作已取消;设备端已完成的更改不会自动撤销。",
- ));
- }
- if started.elapsed() >= timeout {
- return Err(Fault::new(
- "hdc.timeout",
- "设备操作超时,请检查手机授权和连接状态。",
- ));
- }
- let out_done = drain(&mut out, &mut stdout, limit)?;
- let err_done = drain(&mut err, &mut stderr, limit)?;
- if let Some(status) = child.try_wait()? {
- if out_done && err_done {
- break status;
- }
- }
- thread::sleep(Duration::from_millis(20));
- };
- let stdout = String::from_utf8_lossy(&stdout).into_owned();
- let stderr = String::from_utf8_lossy(&stderr);
- if !status.success() {
- return Err(Fault::new(
- "hdc.command_failed",
- format!("{}{}", stdout, stderr)
- .chars()
- .take(4000)
- .collect::(),
- ));
- }
- check_remote_errors(&stderr)?;
- Ok(stdout)
- })();
- if result.is_err() {
- unsafe {
- libc::kill(-(child.id() as i32), libc::SIGKILL);
- }
- let _ = child.wait();
- }
- result
-}
-// HDC does not consistently propagate remote exit codes. Inspect structured command output,
-// but do not interpret log contents as commands failing.
-pub fn check_remote_errors(output: &str) -> Result<()> {
- if output.lines().any(|line| {
- let line = line.trim().to_lowercase();
- line.starts_with("[fail]")
- || line.starts_with("error:")
- || line.starts_with("failed ")
- || line.starts_with("/bin/sh:")
- || line.starts_with("sh:")
- || line == "permission denied"
- }) {
- return Err(Fault::new(
- "hdc.command_failed",
- output.chars().take(4000).collect::(),
- ));
- }
- Ok(())
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::os::unix::fs::PermissionsExt;
- fn script(text: &str) -> (tempfile::TempDir, std::path::PathBuf) {
- let dir = tempfile::tempdir().unwrap();
- let path = dir.path().join("tool");
- std::fs::write(&path, format!("#!/bin/sh\n{text}\n")).unwrap();
- std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o700)).unwrap();
- (dir, path)
- }
- #[test]
- fn bounds_output_timeout_and_remote_errors_without_a_phone() {
- let cancel = AtomicBool::new(false);
- let (_dir, path) = script("printf '[Fail] device unauthorized'; exit 0");
- assert_eq!(
- check_remote_errors(&run(&path, &[], Duration::from_secs(1), &cancel, 1024).unwrap())
- .unwrap_err()
- .code,
- "hdc.command_failed"
- );
- let (_dir, path) = script("sleep 10 & exit 0");
- assert_eq!(
- run(&path, &[], Duration::from_millis(80), &cancel, 1024)
- .unwrap_err()
- .code,
- "hdc.timeout"
- );
- let (_dir, path) = script("while true; do printf '0123456789'; done");
- assert_eq!(
- run(&path, &[], Duration::from_secs(5), &cancel, 32)
- .unwrap_err()
- .code,
- "hdc.output_too_large"
- );
- }
- #[test]
- fn passes_arguments_literally_without_a_host_shell() {
- let (_dir, path) = script("printf '%s' \"$1\"");
- let value = "a; echo surprise $(pwd)".to_string();
- assert_eq!(
- run(
- &path,
- &[value.clone()],
- Duration::from_secs(1),
- &AtomicBool::new(false),
- 1024
- )
- .unwrap(),
- value
- );
- }
-}
diff --git a/curios/harmony-connector/native/src/connector.rs b/curios/harmony-connector/native/src/connector.rs
deleted file mode 100644
index 0c7cab4..0000000
--- a/curios/harmony-connector/native/src/connector.rs
+++ /dev/null
@@ -1,424 +0,0 @@
-use crate::{
- model::*,
- mtp::{self, DeviceIo, NativeDevice, ROOT},
-};
-use serde::{de::DeserializeOwned, Deserialize};
-use serde_json::{json, Value};
-use std::{
- path::{Component, Path},
- sync::{
- atomic::{AtomicBool, AtomicU64, Ordering},
- Arc, Mutex,
- },
- thread,
-};
-
-struct Gate(Arc);
-impl Drop for Gate {
- fn drop(&mut self) {
- self.0.store(false, Ordering::Release);
- }
-}
-struct Session {
- device: Box,
-}
-fn close_in_background(session: Session, gate: Gate) {
- thread::spawn(move || {
- let _gate = gate;
- drop(session);
- });
-}
-struct Job {
- id: u64,
- progress: Arc