From f65f34d115bf254c7b479fa51bbc80a616e4e37c Mon Sep 17 00:00:00 2001 From: Del Wang Date: Wed, 9 Apr 2025 21:59:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BC=80=E6=94=BE=E5=B0=8F=E7=88=B1?= =?UTF-8?q?=E9=9F=B3=E7=AE=B1=E6=8E=A5=E5=85=A5=20MiGPT-Next=20=E6=BA=90?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + packages/client-rust/README.md | 4 +- packages/server-node/Cargo.lock | 864 +++++++++++++++++ packages/server-node/Cargo.toml | 18 + packages/server-node/README.md | 63 ++ packages/server-node/migpt/index.ts | 21 + packages/server-node/migpt/open-xiaoai.ts | 2 + packages/server-node/migpt/speaker.ts | 236 +++++ packages/server-node/migpt/xiaoai.ts | 71 ++ packages/server-node/package.json | 27 + packages/server-node/pnpm-lock.yaml | 1049 +++++++++++++++++++++ packages/server-node/src/lib.rs | 47 + packages/server-node/src/node.rs | 93 ++ packages/server-node/src/runtime.rs | 18 + packages/server-node/src/server.rs | 120 +++ packages/server-node/tsconfig.json | 11 + packages/server-python/README.md | 2 +- 17 files changed, 2644 insertions(+), 3 deletions(-) create mode 100644 packages/server-node/Cargo.lock create mode 100644 packages/server-node/Cargo.toml create mode 100644 packages/server-node/README.md create mode 100644 packages/server-node/migpt/index.ts create mode 100644 packages/server-node/migpt/open-xiaoai.ts create mode 100644 packages/server-node/migpt/speaker.ts create mode 100644 packages/server-node/migpt/xiaoai.ts create mode 100644 packages/server-node/package.json create mode 100644 packages/server-node/pnpm-lock.yaml create mode 100644 packages/server-node/src/lib.rs create mode 100644 packages/server-node/src/node.rs create mode 100644 packages/server-node/src/runtime.rs create mode 100644 packages/server-node/src/server.rs create mode 100644 packages/server-node/tsconfig.json diff --git a/.gitignore b/.gitignore index ed30599..2644b6f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ __pycache__ *.pem .mi.json xiaozhi.json +open-xiaoai.node diff --git a/packages/client-rust/README.md b/packages/client-rust/README.md index c3b937a..813adc8 100644 --- a/packages/client-rust/README.md +++ b/packages/client-rust/README.md @@ -82,9 +82,9 @@ chmod +x /data/client > 当你在公网上运行本项目时,应当提高警惕。🚨 -如上所言,本项目仅作为一个基础的演示程序。诸如多设备连接管理、身份认证、通信数据加密、音频压缩传输等均未做处理。 +本项目只是一个基础的演示程序,抛砖引玉。诸如多设备连接管理、身份认证、通信数据加密、音频压缩传输等均未做处理。 -尤其是其提供了执行任意脚本的能力演示,虽然在启动 client 端时需要由你本人指定可信的 server 端连接地址,但还是要务必小心! +其默认提供了执行任意脚本的能力演示,虽然在启动 Client 端时需要由你本人指定可信的 Server 端连接地址,但还是要务必小心! 毕竟再怎么小心都不为过 :) diff --git a/packages/server-node/Cargo.lock b/packages/server-node/Cargo.lock new file mode 100644 index 0000000..1b65d21 --- /dev/null +++ b/packages/server-node/Cargo.lock @@ -0,0 +1,864 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[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 = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" + +[[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.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[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.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linkme" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22d227772b5999ddc0690e733f734f95ca05387e329c4084fe65678c51198ffe" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71a98813fa0073a317ed6a8055dcd4722a49d9b862af828ee68449adb799b6be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "neon" +version = "1.1.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd61d11347a66a4b5d10bf413169a99de37cf7b11191fac0c6f0b242878fa889" +dependencies = [ + "either", + "getrandom 0.2.15", + "libloading", + "linkme", + "neon-macros", + "once_cell", + "semver", + "send_wrapper", + "smallvec", + "tokio", +] + +[[package]] +name = "neon-macros" +version = "1.1.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef6445e861d1eeaf27de82311abc6e16e660c1d3afebf2b62ef9a2909cd86d0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "open-xiaoai" +version = "1.0.0" +dependencies = [ + "futures", + "serde", + "serde_json", + "tokio", + "tokio-tungstenite", + "uuid", +] + +[[package]] +name = "open_xiaoai_server" +version = "0.1.0" +dependencies = [ + "neon", + "once_cell", + "open-xiaoai", + "serde", + "serde_json", + "tokio", + "tokio-tungstenite", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha", + "rand_core", + "zerocopy", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.44.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "getrandom 0.3.2", +] + +[[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.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/packages/server-node/Cargo.toml b/packages/server-node/Cargo.toml new file mode 100644 index 0000000..96d828d --- /dev/null +++ b/packages/server-node/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "open_xiaoai_server" +version = "0.1.0" +edition = "2021" +exclude = ["index.node"] + +[lib] +crate-type = ["cdylib"] + + +[dependencies] +neon = { version = "1.1.0-alpha.1", features = ["napi-6", "futures", "tokio"] } +open-xiaoai = { path = "../client-rust" } +tokio = { version = "1.32.0", features = ["full"] } +tokio-tungstenite = "0.26.2" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +once_cell = "1.21.3" diff --git a/packages/server-node/README.md b/packages/server-node/README.md new file mode 100644 index 0000000..9820f82 --- /dev/null +++ b/packages/server-node/README.md @@ -0,0 +1,63 @@ +# Node.js Server + +[Open-XiaoAI](https://github.com/idootop/open-xiaoai) 的 Node.js 版 Server 端,用来演示小爱音箱接入[MiGPT-Next](https://github.com/idootop/migpt-next)。 + +## 环境准备 + +为了能够正常编译运行该项目,你需要安装以下依赖环境: + +- Node.js v22.x: https://nodejs.org/zh-cn/download +- Rust: https://www.rust-lang.org/learn/get-started + +## 编译运行 + +> [!NOTE] +> 请先确认你已经将小爱音箱刷机成功,并安装运行了 Rust 补丁程序 [👉 教程](../client-rust/README.md) + +```bash +# 启用 PNPM 包管理工具 +corepack enable && corepack install + +# 安装依赖 +pnpm install + +# 编译运行 +pnpm dev +``` + +## 注意事项 + +首次运行你需要先配置自己的大模型服务,才能正常获取 AI 回复。 + +> [!NOTE] +> 你可以在下面的 `onMessage` 函数中,自定义你想要的回复逻辑,更多配置参考 👉 [MiGPT-Next](https://github.com/idootop/migpt-next/tree/main/apps/next) + +```typescript +// packages/server-node/migpt/index.ts +import { sleep } from "@mi-gpt/utils"; +import { OpenXiaoAI } from "./xiaoai.js"; + +async function main() { + await OpenXiaoAI.start({ + openai: { + model: "gpt-4o-mini", + baseURL: "https://api.openai.com/v1", + apiKey: "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + }, + async onMessage(_engine, { text }) { + if (text.startsWith("你好")) { + await sleep(1000); + return { text: "你好,很高兴认识你!" }; + } + }, + }); + process.exit(0); +} + +main(); +``` + +默认 Rust Server 在启动时,并没有开启小爱音箱的录音能力。如果你需要在 Node.js 端正常接收音频输入流,或者播放音频输出流,请将 `src/server.rs` 文件中被注释掉的 `start_recording` 和 `start_play` 代码加回来,然后重新编译运行。 + +> [!NOTE] +> 本项目只是一个简单的演示程序,抛砖引玉。如果你需要更多的功能,比如唤醒词识别、语音转文字、连续对话等(甚至是对接 OpenAI 的 [Realtime API](https://platform.openai.com/docs/guides/realtime)),可参考本项目代码,自行实现想要的功能。 diff --git a/packages/server-node/migpt/index.ts b/packages/server-node/migpt/index.ts new file mode 100644 index 0000000..7214505 --- /dev/null +++ b/packages/server-node/migpt/index.ts @@ -0,0 +1,21 @@ +import { sleep } from "@mi-gpt/utils"; +import { OpenXiaoAI } from "./xiaoai.js"; + +async function main() { + await OpenXiaoAI.start({ + openai: { + model: "gpt-4o-mini", + baseURL: "https://api.openai.com/v1", + apiKey: "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + }, + async onMessage(_engine, { text }) { + if (text.startsWith("你好")) { + await sleep(1000); + return { text: "你好,很高兴认识你!" }; + } + }, + }); + process.exit(0); +} + +main(); diff --git a/packages/server-node/migpt/open-xiaoai.ts b/packages/server-node/migpt/open-xiaoai.ts new file mode 100644 index 0000000..396bdbd --- /dev/null +++ b/packages/server-node/migpt/open-xiaoai.ts @@ -0,0 +1,2 @@ +import { createRequire } from 'node:module'; +export const RustServer = createRequire(import.meta.url)('./open-xiaoai.node'); diff --git a/packages/server-node/migpt/speaker.ts b/packages/server-node/migpt/speaker.ts new file mode 100644 index 0000000..ca0b87e --- /dev/null +++ b/packages/server-node/migpt/speaker.ts @@ -0,0 +1,236 @@ +import { jsonEncode } from "@mi-gpt/utils/parse"; +import { RustServer } from "./open-xiaoai.js"; +import type { ISpeaker } from "@mi-gpt/engine/base"; + +export interface CommandResult { + stdout: string; + stderr: string; + exit_code: number; +} + +class SpeakerManager implements ISpeaker { + status: "playing" | "paused" | "idle" = "idle"; + + /** + * 获取播放状态 + */ + async getPlaying(sync = false) { + if (sync) { + // 同步远端最新状态 + const res = await this.runShell("mphelper mute_stat"); + if (res?.stdout.includes("1")) { + this.status = "playing"; + } else if (res?.stdout.includes("2")) { + this.status = "paused"; + } + } + return this.status; + } + + /** + * 播放/暂停 + */ + async setPlaying(playing = true) { + const res = await this.runShell( + playing ? "mphelper play" : "mphelper pause" + ); + return res?.stdout.includes('"code": 0'); + } + + /** + * 播放文字、音频链接、音频流 + */ + async play({ + text, + url, + bytes, + timeout = 10 * 60 * 1000, + blocking = false, + }: { + text?: string; + url?: string; + bytes?: Uint8Array; + /** + * 超时时长(毫秒) + * + * 默认 10 分钟 + */ + timeout?: number; + /** + * 是否阻塞运行(仅对播放文字、音频链接有效) + * + * 如果是则等到音频播放完毕才会返回 + */ + blocking?: boolean; + }) { + if (bytes) { + return RustServer.on_output_data(bytes) as Promise; + } + + if (blocking) { + const res = await this.runShell( + url + ? `miplayer -f '${url}'` + : `/usr/sbin/tts_play2.sh '${text || "你好"}'`, + { timeout } + ); + return res?.exit_code === 0; + } + + const res = await this.runShell( + url + ? `ubus call mediaplayer player_play_url '${jsonEncode({ + url: url, + type: 1, + })}'` + : `ubus call mibrain text_to_speech '${jsonEncode({ + text: text || "你好", + save: 0, + })}'`, + { timeout } + ); + return res?.stdout.includes('"code": 0') ?? false; + } + + /** + * (取消)唤醒小爱 + */ + async wakeUp( + awake = true, + options?: { + /** + * 静默唤醒 + */ + silent: boolean; + } + ) { + const { silent = false } = options ?? {}; + const command = awake + ? silent + ? `ubus call pnshelper event_notify '{"src":1,"event":0}'` + : `ubus call pnshelper event_notify '{"src":0,"event":0}'` + : ` + ubus call pnshelper event_notify '{"src":3, "event":7}' + sleep 0.1 + ubus call pnshelper event_notify '{"src":3, "event":8}' + `; + const res = await this.runShell(command); + return res?.stdout.includes('"code": 0'); + } + + /** + * 把文字指令交给原来的小爱执行 + */ + async askXiaoAI( + text: string, + options?: { + /** + * 静默执行 + */ + silent: boolean; + } + ) { + const { silent = false } = options ?? {}; + const res = await this.runShell( + `ubus call mibrain ai_service '${jsonEncode({ + tts: silent ? undefined : 1, + nlp: 1, + nlp_text: text, + })}'` + ); + return res?.stdout.includes('"code": 0'); + } + + /** + * 中断原来小爱的运行 + * + * 注意:重启需要大约 1-2s 的时间,在此期间无法使用小爱音箱自带的 TTS 服务 + */ + async abortXiaoAI() { + const res = await this.runShell( + "/etc/init.d/mico_aivs_lab restart >/dev/null 2>&1" + ); + return res?.exit_code === 0; + } + + /** + * 获取启动分区 + */ + async getBoot() { + const res = await this.runShell("echo $(fw_env -g boot_part)"); + return res?.stdout.trim(); + } + + /** + * 设置启动分区 + */ + async setBoot(boot_part: "boot0" | "boot1") { + const res = await this.runShell( + `fw_env -s boot_part ${boot_part} >/dev/null 2>&1 && echo $(fw_env -g boot_part)` + ); + return res?.stdout.includes(boot_part); + } + + /** + * 获取设备型号、序列号信息 + */ + async getDevice() { + const res = await this.runShell("echo $(micocfg_model) $(micocfg_sn)"); + const info = res?.stdout.trim().split(" "); + return { + model: info?.[0] ?? "unknown", + sn: info?.[1] ?? "unknown", + }; + } + + /** + * 获取麦克风状态 + */ + async getMic() { + const res = await this.runShell( + "[ ! -f /tmp/mipns/mute ] && echo on || echo off" + ); + let status: "on" | "off" = "off"; + if (res?.stdout.includes("on")) { + status = "on"; + } + return status; + } + + /** + * 打开/关闭麦克风 + */ + async setMic(on = true) { + const res = await this.runShell( + on + ? `ubus -t1 -S call pnshelper event_notify '{"src":3, "event":7}' 2>&1` + : `ubus -t1 -S call pnshelper event_notify '{"src":3, "event":8}' 2>&1` + ); + return res?.stdout.includes('"code":0'); + } + + /** + * 执行脚本 + */ + async runShell( + script: string, + options?: { + /** + * 超时时间(单位:毫秒) + */ + timeout?: number; + } + ): Promise { + const { timeout = 10 * 1000 } = options ?? {}; + try { + const res = await RustServer.run_shell(script, timeout); + if (res) { + return JSON.parse(res); + } + } catch (_) { + return undefined; + } + } +} + +export const OpenXiaoAISpeaker = new SpeakerManager(); diff --git a/packages/server-node/migpt/xiaoai.ts b/packages/server-node/migpt/xiaoai.ts new file mode 100644 index 0000000..66a6a25 --- /dev/null +++ b/packages/server-node/migpt/xiaoai.ts @@ -0,0 +1,71 @@ +import { type EngineConfig, MiGPTEngine } from "@mi-gpt/engine"; +import { deepMerge } from "@mi-gpt/utils"; +import { jsonDecode } from "@mi-gpt/utils/parse"; +import type { Prettify } from "@mi-gpt/utils/typing"; +import { RustServer } from "./open-xiaoai.js"; +import { OpenXiaoAISpeaker } from "./speaker.js"; +import { randomUUID } from "node:crypto"; + +type OpenXiaoAIConfig = Prettify>; + +const kDefaultOpenXiaoAIConfig: OpenXiaoAIConfig = { + // +}; + +class OpenXiaoAIEngine extends MiGPTEngine { + speaker = OpenXiaoAISpeaker; + + async start(config: OpenXiaoAIConfig) { + await super.start(deepMerge(kDefaultOpenXiaoAIConfig, config)); + // 注册全局回调函数 + (global as any).RUST_CALLBACKS = { + on_event: this.onEvent, + on_input_data: this.onRecord, + }; + // 启动服务 + console.log("✅ 服务已启动..."); + await RustServer.start(); + } + + /** + * 收到事件 + */ + onEvent = (event: string) => { + const e = JSON.parse(event); + if (e.event === "playing") { + // 更新播放状态 + OpenXiaoAISpeaker.status = + e.data === "Playing" + ? "playing" + : e.data === "Paused" + ? "paused" + : "idle"; + } else if (e.event === "instruction" && e.data.NewLine) { + // 收到语音识别结果 + const line = jsonDecode(e.data.NewLine); + if ( + line?.header?.namespace === "SpeechRecognizer" && + line?.header?.name === "RecognizeResult" && + line?.payload?.is_final && + line?.payload?.results?.[0]?.text + ) { + const text = line.payload.results[0].text; + this.onMessage({ + text, + id: randomUUID(), + sender: "user", + timestamp: Date.now(), + }); + } + } + }; + + /** + * 收到录音音频流 + */ + onRecord = (data: Uint8Array) => { + console.log("🔥 收到录音音频流", data.length); + }; +} + +export const OpenXiaoAI = new OpenXiaoAIEngine(); diff --git a/packages/server-node/package.json b/packages/server-node/package.json new file mode 100644 index 0000000..189f3cd --- /dev/null +++ b/packages/server-node/package.json @@ -0,0 +1,27 @@ +{ + "name": "open-xiaoai-node", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "cargo-cp-artifact -ac open_xiaoai_server migpt/open-xiaoai.node -- cargo build --release --message-format=json-render-diagnostics", + "start": "tsx migpt/index.ts", + "dev": "pnpm build && pnpm start" + }, + "dependencies": { + "@mi-gpt/engine": "1.1.1", + "@mi-gpt/utils": "1.0.0" + }, + "devDependencies": { + "@mi-gpt/config": "1.0.0", + "@neon-rs/cli": "0.1.82", + "@types/node": "^22.13.11", + "cargo-cp-artifact": "^0.1.9", + "tsx": "^4.19.3", + "typescript": "^5.8.2" + }, + "packageManager": "pnpm@9.15.9", + "engines": { + "node": ">=16" + } +} diff --git a/packages/server-node/pnpm-lock.yaml b/packages/server-node/pnpm-lock.yaml new file mode 100644 index 0000000..cdb5bbf --- /dev/null +++ b/packages/server-node/pnpm-lock.yaml @@ -0,0 +1,1049 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@mi-gpt/engine': + specifier: 1.1.1 + version: 1.1.1 + '@mi-gpt/utils': + specifier: 1.0.0 + version: 1.0.0 + devDependencies: + '@mi-gpt/config': + specifier: 1.0.0 + version: 1.0.0 + '@neon-rs/cli': + specifier: 0.1.82 + version: 0.1.82 + '@types/node': + specifier: ^22.13.11 + version: 22.14.0 + cargo-cp-artifact: + specifier: ^0.1.9 + version: 0.1.9 + tsx: + specifier: ^4.19.3 + version: 4.19.3 + typescript: + specifier: ^5.8.2 + version: 5.8.3 + +packages: + + '@cargo-messages/android-arm-eabi@0.1.81': + resolution: {integrity: sha512-cpRbgb56e9LmAj96Tixtz9/bTlaJAeplWNNv4obu+eqQyZd3ZjX04TJd9fM1bjHDGVyR9GTVlgBsbQEntIGkwg==} + cpu: [arm] + os: [android] + + '@cargo-messages/darwin-arm64@0.1.81': + resolution: {integrity: sha512-OwGqsw+tbJx37a/vH4T8R9qkrrFYoTIOnckbA9+MhQodE2FSWyk3HvLh+z8jjl+QZa1RSOU9Ax6gt/46h0BiTg==} + cpu: [arm64] + os: [darwin] + + '@cargo-messages/darwin-x64@0.1.81': + resolution: {integrity: sha512-Iu761bPk25Ce6yUdDCjjeVuT8/xbBmczyaNB7oYBmAZEE5rshvCJ42TqSShYYP+S7pKkN42nBhkFooaZrqaz9g==} + cpu: [x64] + os: [darwin] + + '@cargo-messages/linux-arm-gnueabihf@0.1.81': + resolution: {integrity: sha512-iIuy7KTJAEhbiqlIlcxQOdW6opI6M9LXlgd/jdsHbP2FjmTyhTLnd3JCJ6JeAeidwknCDs+CFlaVmPxTKSytsg==} + cpu: [arm] + os: [linux] + + '@cargo-messages/linux-arm64-gnu@0.1.81': + resolution: {integrity: sha512-QPQRsHj9m/9ga8wRBlLh8t2AXyr40+/H55FIKVj7zIjV++waY/TbSTPofDZQhMycd5VSGLKztfhahiCO7c/RAQ==} + cpu: [arm64] + os: [linux] + + '@cargo-messages/linux-arm64-musl@0.1.81': + resolution: {integrity: sha512-9O0ATesIOjDTz2L01OtlGHYwP86I31/mzXMC+ryQkzbbIKj7KUiSqmEc29I14I517UYO8/sMeow6q6MVBpehlA==} + cpu: [arm64] + os: [linux] + + '@cargo-messages/linux-x64-gnu@0.1.81': + resolution: {integrity: sha512-wEFYxCdtHNiEvp5KEg17CfRCUdfRTtkL+1GASROyvYEUV6DQf6TXxfHuuWg2xlVxh5fqiTGFSRfiqFrCDL/xrw==} + cpu: [x64] + os: [linux] + + '@cargo-messages/linux-x64-musl@0.1.81': + resolution: {integrity: sha512-zi5pKIh60oPwOCDQapAZ3Mya4y56MI2BoGvY8JtztYaXTorGmAoyf6jjb50Wt+HfoYYjM3OlPt03XMlCFZJnIQ==} + cpu: [x64] + os: [linux] + + '@cargo-messages/win32-arm64-msvc@0.1.81': + resolution: {integrity: sha512-oyiT8AYLoguF7cFOMYDsPv3eirzBcFafOOfRsFyd3+wmaPTl/DdbCq446oThRmSAsEGJpzhzj7TafcnXMBkHbg==} + cpu: [arm64] + os: [win32] + + '@cargo-messages/win32-x64-msvc@0.1.81': + resolution: {integrity: sha512-B5Ukf4AohtIv27uCP/AgM+7vYwQ4RacI6m8ZBr2XKeSrjZXcXguzlZd+wD7bD5+wa0capvXKUskZDnpG/DcYiA==} + cpu: [x64] + os: [win32] + + '@esbuild/aix-ppc64@0.25.2': + resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.2': + resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.2': + resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.2': + resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.2': + resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.2': + resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.2': + resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.2': + resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.2': + resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.2': + resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.2': + resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.2': + resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.2': + resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.2': + resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.2': + resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.2': + resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.2': + resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.2': + resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.2': + resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.2': + resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.2': + resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.2': + resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.2': + resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.2': + resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.2': + resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@mi-gpt/chat@1.0.0': + resolution: {integrity: sha512-PyyQ4Vg3s1OcUxr9GXZTpBgGCRQldhm9vGKc3UpAA4QDyVJWSP6ILhXKfFHO+UfnxDr45y9wlk5PpMcgkmPNkQ==} + engines: {node: '>=16'} + + '@mi-gpt/config@1.0.0': + resolution: {integrity: sha512-oo/roYiRcGiHsZESjukwPTsRFqddShTJ8gkccR5ZMYn8SySj07A0nCsEsLEfB9mLkvNY8D6PCO1NxbRDDRBBlA==} + engines: {node: '>=16'} + + '@mi-gpt/engine@1.1.1': + resolution: {integrity: sha512-TyA6JCq/yGvJI+ysFuDOwLaDUJ1fjjtxHIGB3Nyr40R0Ujb32Ssz/FoCDpbVmTKj2niebZQAGuUl7/juBuQwtw==} + engines: {node: '>=16'} + + '@mi-gpt/openai@1.0.0': + resolution: {integrity: sha512-BZI2gJkcJ4TuHcyyD5fzvFzEMlSkfkkuxpycQ/mSiMC8Z3umQbRSk2z1Tg3ho8DFxf5tYiIsFzbvWCBw2WXlwg==} + engines: {node: '>=16'} + + '@mi-gpt/stream@1.0.0': + resolution: {integrity: sha512-nzYKGODEhTbBUv6WGmgEY4rioXfWRvs2te99nQWjl+agmN9bT7KTbHKm8tzTDApQcQltx5BTDXrXMairmLVTYQ==} + engines: {node: '>=16'} + + '@mi-gpt/utils@1.0.0': + resolution: {integrity: sha512-hAU/uYPNiNJgXWkQrVqRLe/9Uz8AgtoiNwcbbwQs8iXJcyq3NjcSHkkwUseJAnaQFE9iv/kvHZZsx3r2HhlKjw==} + engines: {node: '>=16'} + + '@neon-rs/cli@0.1.82': + resolution: {integrity: sha512-QrlGPQp9KOGuMvjjua79lEV2QTcE16m8JatG5ITdQpBAwRQpDw5xab57W9130y2iUEfMzYtp7v6pcN1fUB0Exg==} + hasBin: true + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@types/node-fetch@2.6.12': + resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + + '@types/node@18.19.86': + resolution: {integrity: sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ==} + + '@types/node@22.14.0': + resolution: {integrity: sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + cargo-cp-artifact@0.1.9: + resolution: {integrity: sha512-6F+UYzTaGB+awsTXg0uSJA1/b/B3DDJzpKVRu0UmyI7DmNeaAl2RFHuTGIN6fEgpadRxoXGb7gbC1xo4C3IdyA==} + hasBin: true + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.2: + resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} + engines: {node: '>=18'} + hasBin: true + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + engines: {node: '>= 6'} + + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + + get-uri@6.0.4: + resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==} + engines: {node: '>= 14'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + openai@4.93.0: + resolution: {integrity: sha512-2kONcISbThKLfm7T9paVzg+QCE1FOZtNMMUfXyXckUAoXRRS/mTP89JSDHPMp8uM5s0bz28RISbvQjArD6mgUQ==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + pac-proxy-agent@7.2.0: + resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.4: + resolution: {integrity: sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.19.3: + resolution: {integrity: sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + +snapshots: + + '@cargo-messages/android-arm-eabi@0.1.81': + optional: true + + '@cargo-messages/darwin-arm64@0.1.81': + optional: true + + '@cargo-messages/darwin-x64@0.1.81': + optional: true + + '@cargo-messages/linux-arm-gnueabihf@0.1.81': + optional: true + + '@cargo-messages/linux-arm64-gnu@0.1.81': + optional: true + + '@cargo-messages/linux-arm64-musl@0.1.81': + optional: true + + '@cargo-messages/linux-x64-gnu@0.1.81': + optional: true + + '@cargo-messages/linux-x64-musl@0.1.81': + optional: true + + '@cargo-messages/win32-arm64-msvc@0.1.81': + optional: true + + '@cargo-messages/win32-x64-msvc@0.1.81': + optional: true + + '@esbuild/aix-ppc64@0.25.2': + optional: true + + '@esbuild/android-arm64@0.25.2': + optional: true + + '@esbuild/android-arm@0.25.2': + optional: true + + '@esbuild/android-x64@0.25.2': + optional: true + + '@esbuild/darwin-arm64@0.25.2': + optional: true + + '@esbuild/darwin-x64@0.25.2': + optional: true + + '@esbuild/freebsd-arm64@0.25.2': + optional: true + + '@esbuild/freebsd-x64@0.25.2': + optional: true + + '@esbuild/linux-arm64@0.25.2': + optional: true + + '@esbuild/linux-arm@0.25.2': + optional: true + + '@esbuild/linux-ia32@0.25.2': + optional: true + + '@esbuild/linux-loong64@0.25.2': + optional: true + + '@esbuild/linux-mips64el@0.25.2': + optional: true + + '@esbuild/linux-ppc64@0.25.2': + optional: true + + '@esbuild/linux-riscv64@0.25.2': + optional: true + + '@esbuild/linux-s390x@0.25.2': + optional: true + + '@esbuild/linux-x64@0.25.2': + optional: true + + '@esbuild/netbsd-arm64@0.25.2': + optional: true + + '@esbuild/netbsd-x64@0.25.2': + optional: true + + '@esbuild/openbsd-arm64@0.25.2': + optional: true + + '@esbuild/openbsd-x64@0.25.2': + optional: true + + '@esbuild/sunos-x64@0.25.2': + optional: true + + '@esbuild/win32-arm64@0.25.2': + optional: true + + '@esbuild/win32-ia32@0.25.2': + optional: true + + '@esbuild/win32-x64@0.25.2': + optional: true + + '@mi-gpt/chat@1.0.0': + dependencies: + '@mi-gpt/openai': 1.0.0 + '@mi-gpt/stream': 1.0.0 + '@mi-gpt/utils': 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + - ws + - zod + + '@mi-gpt/config@1.0.0': {} + + '@mi-gpt/engine@1.1.1': + dependencies: + '@mi-gpt/chat': 1.0.0 + '@mi-gpt/openai': 1.0.0 + '@mi-gpt/stream': 1.0.0 + '@mi-gpt/utils': 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + - ws + - zod + + '@mi-gpt/openai@1.0.0': + dependencies: + '@mi-gpt/utils': 1.0.0 + openai: 4.93.0 + proxy-agent: 6.5.0 + transitivePeerDependencies: + - encoding + - supports-color + - ws + - zod + + '@mi-gpt/stream@1.0.0': + dependencies: + '@mi-gpt/utils': 1.0.0 + + '@mi-gpt/utils@1.0.0': {} + + '@neon-rs/cli@0.1.82': + optionalDependencies: + '@cargo-messages/android-arm-eabi': 0.1.81 + '@cargo-messages/darwin-arm64': 0.1.81 + '@cargo-messages/darwin-x64': 0.1.81 + '@cargo-messages/linux-arm-gnueabihf': 0.1.81 + '@cargo-messages/linux-arm64-gnu': 0.1.81 + '@cargo-messages/linux-arm64-musl': 0.1.81 + '@cargo-messages/linux-x64-gnu': 0.1.81 + '@cargo-messages/linux-x64-musl': 0.1.81 + '@cargo-messages/win32-arm64-msvc': 0.1.81 + '@cargo-messages/win32-x64-msvc': 0.1.81 + + '@tootallnate/quickjs-emscripten@0.23.0': {} + + '@types/node-fetch@2.6.12': + dependencies: + '@types/node': 22.14.0 + form-data: 4.0.2 + + '@types/node@18.19.86': + dependencies: + undici-types: 5.26.5 + + '@types/node@22.14.0': + dependencies: + undici-types: 6.21.0 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + agent-base@7.1.3: {} + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + ast-types@0.13.4: + dependencies: + tslib: 2.8.1 + + asynckit@0.4.0: {} + + basic-ftp@5.0.5: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + cargo-cp-artifact@0.1.9: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + data-uri-to-buffer@6.0.2: {} + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + + delayed-stream@1.0.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + esbuild@0.25.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.2 + '@esbuild/android-arm': 0.25.2 + '@esbuild/android-arm64': 0.25.2 + '@esbuild/android-x64': 0.25.2 + '@esbuild/darwin-arm64': 0.25.2 + '@esbuild/darwin-x64': 0.25.2 + '@esbuild/freebsd-arm64': 0.25.2 + '@esbuild/freebsd-x64': 0.25.2 + '@esbuild/linux-arm': 0.25.2 + '@esbuild/linux-arm64': 0.25.2 + '@esbuild/linux-ia32': 0.25.2 + '@esbuild/linux-loong64': 0.25.2 + '@esbuild/linux-mips64el': 0.25.2 + '@esbuild/linux-ppc64': 0.25.2 + '@esbuild/linux-riscv64': 0.25.2 + '@esbuild/linux-s390x': 0.25.2 + '@esbuild/linux-x64': 0.25.2 + '@esbuild/netbsd-arm64': 0.25.2 + '@esbuild/netbsd-x64': 0.25.2 + '@esbuild/openbsd-arm64': 0.25.2 + '@esbuild/openbsd-x64': 0.25.2 + '@esbuild/sunos-x64': 0.25.2 + '@esbuild/win32-arm64': 0.25.2 + '@esbuild/win32-ia32': 0.25.2 + '@esbuild/win32-x64': 0.25.2 + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + esprima@4.0.1: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + event-target-shim@5.0.1: {} + + form-data-encoder@1.7.2: {} + + form-data@4.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + mime-types: 2.1.35 + + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-tsconfig@4.10.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + get-uri@6.0.4: + dependencies: + basic-ftp: 5.0.5 + data-uri-to-buffer: 6.0.2 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + ip-address@9.0.5: + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 + + jsbn@1.1.0: {} + + lru-cache@7.18.3: {} + + math-intrinsics@1.1.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + ms@2.1.3: {} + + netmask@2.0.2: {} + + node-domexception@1.0.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + openai@4.93.0: + dependencies: + '@types/node': 18.19.86 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + pac-proxy-agent@7.2.0: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.3 + debug: 4.4.0 + get-uri: 6.0.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.0.2 + + proxy-agent@6.5.0: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 7.18.3 + pac-proxy-agent: 7.2.0 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + proxy-from-env@1.1.0: {} + + resolve-pkg-maps@1.0.0: {} + + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + socks: 2.8.4 + transitivePeerDependencies: + - supports-color + + socks@2.8.4: + dependencies: + ip-address: 9.0.5 + smart-buffer: 4.2.0 + + source-map@0.6.1: + optional: true + + sprintf-js@1.1.3: {} + + tr46@0.0.3: {} + + tslib@2.8.1: {} + + tsx@4.19.3: + dependencies: + esbuild: 0.25.2 + get-tsconfig: 4.10.0 + optionalDependencies: + fsevents: 2.3.3 + + typescript@5.8.3: {} + + undici-types@5.26.5: {} + + undici-types@6.21.0: {} + + web-streams-polyfill@4.0.0-beta.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 diff --git a/packages/server-node/src/lib.rs b/packages/server-node/src/lib.rs new file mode 100644 index 0000000..08321a1 --- /dev/null +++ b/packages/server-node/src/lib.rs @@ -0,0 +1,47 @@ +use neon::prelude::*; +use node::NodeManager; +use open_xiaoai::services::connect::{message::MessageManager, rpc::RPC}; + +use runtime::runtime; +use serde_json::json; +use server::AppServer; + +mod node; +mod runtime; +mod server; + +#[neon::export] +async fn start() -> () { + let _ = AppServer::run().await; +} + +#[neon::export] +async fn run_shell(script: String, timeout_millis: f64) -> String { + let res = RPC::instance() + .call_remote( + "run_shell", + Some(json!(script)), + Some(timeout_millis as u64), + ) + .await; + match res { + Err(e) => format!("run_shell error: {}", e), + Ok(res) => serde_json::to_string(&res.data.unwrap()).unwrap(), + } +} + +#[neon::export] +async fn on_output_data(bytes: Vec) -> bool { + MessageManager::instance() + .send_stream("play", bytes, None) + .await + .is_ok() +} + +#[neon::main] +fn main(mut cx: ModuleContext) -> NeonResult<()> { + let _ = neon::set_global_executor(&mut cx, runtime()); + neon::registered().export(&mut cx)?; + NodeManager::instance().init(cx); + Ok(()) +} diff --git a/packages/server-node/src/node.rs b/packages/server-node/src/node.rs new file mode 100644 index 0000000..de13d89 --- /dev/null +++ b/packages/server-node/src/node.rs @@ -0,0 +1,93 @@ +use neon::prelude::*; +use std::sync::{Arc, LazyLock, Mutex}; +use tokio::sync::oneshot; + +use crate::runtime::run_async; + +pub struct NodeManager { + channel: Arc>>, +} + +static INSTANCE: LazyLock = LazyLock::new(NodeManager::new); + +impl NodeManager { + pub fn new() -> Self { + Self { + channel: Arc::new(Mutex::new(None)), + } + } + + pub fn instance() -> &'static Self { + &INSTANCE + } + + pub fn init(&self, mut cx: ModuleContext) { + let channel = cx.channel(); + *self.channel.lock().unwrap() = Some(channel); + } + + pub async fn call_fn(&self, key: &str, map_arg: F, map_res: F2) -> Result + where + R: Send + 'static, + F: for<'a> Fn(&mut TaskContext<'a>) -> Handle<'a, JsValue> + Send + 'static, + F2: Fn(&mut TaskContext<'_>, Handle<'_, JsValue>) -> Result + + Send + + Sync + + 'static, + { + let channel = match self.channel.lock().unwrap().as_ref() { + Some(channel) => channel.clone(), + None => return Err("NodeManager 尚未初始化".into()), + }; + + let key = key.to_string(); + let (tx, rx) = oneshot::channel::>(); + + channel.send(move |mut cx| { + let Ok(callbacks) = cx.global::("RUST_CALLBACKS") else { + let _ = tx.send(Err("无法获取 RUST_CALLBACKS 对象".into())); + return Ok(()); + }; + + let Ok(callback) = callbacks.get::(&mut cx, key.as_str()) else { + let _ = tx.send(Err("找不到函数".into())); + return Ok(()); + }; + + let arg = map_arg(&mut cx); + let this = cx.undefined(); + + let Ok(res) = callback.call(&mut cx, this, [arg]) else { + let _ = tx.send(Err("函数调用失败".into())); + return Ok(()); + }; + + if res.is_a::(&mut cx) { + let future = res + .downcast::(&mut cx) + .unwrap() + .to_future(&mut cx, move |mut cx, res| match res { + Ok(res) => Ok(map_res(&mut cx, res)), + Err(err) => Ok(map_res(&mut cx, err)), + }) + .unwrap(); + + run_async(async move { + let res = future.await.unwrap(); + let _ = tx.send(res); + }); + } else { + let _ = tx.send(map_res(&mut cx, res)); + } + + Ok(()) + }); + + let res = rx.await; + + match res { + Ok(res) => res, + Err(_) => Err("接收数据失败".into()), + } + } +} diff --git a/packages/server-node/src/runtime.rs b/packages/server-node/src/runtime.rs new file mode 100644 index 0000000..e7b63fc --- /dev/null +++ b/packages/server-node/src/runtime.rs @@ -0,0 +1,18 @@ +use std::future::Future; + +use once_cell::sync::OnceCell; +use tokio::runtime::Runtime; + +static RUNTIME: OnceCell = OnceCell::new(); + +pub fn runtime() -> &'static Runtime { + RUNTIME.get_or_try_init(Runtime::new).unwrap() +} + +pub fn run_async(future: F) +where + F: Future + Send + 'static, + F::Output: Send + 'static, +{ + runtime().spawn(future); +} diff --git a/packages/server-node/src/server.rs b/packages/server-node/src/server.rs new file mode 100644 index 0000000..c02e1d9 --- /dev/null +++ b/packages/server-node/src/server.rs @@ -0,0 +1,120 @@ +use neon::prelude::Context; +use neon::types::JsUint8Array; +use open_xiaoai::base::{AppError, VERSION}; +use open_xiaoai::services::connect::data::{Event, Request, Response, Stream}; +use open_xiaoai::services::connect::handler::MessageHandler; +use open_xiaoai::services::connect::message::{MessageManager, WsStream}; +use open_xiaoai::services::connect::rpc::RPC; +use open_xiaoai::services::speaker::SpeakerManager; +use open_xiaoai::utils::task::TaskManager; + +use serde_json::json; +use tokio::net::{TcpListener, TcpStream}; +use tokio_tungstenite::accept_async; + +use crate::node::NodeManager; + +pub struct AppServer; + +async fn test() -> Result<(), AppError> { + SpeakerManager::play_text("已连接").await?; + + // let _ = RPC::instance() + // .call_remote("start_recording", None, None) + // .await; + + // let _ = RPC::instance().call_remote("start_play", None, None).await; + + Ok(()) +} + +impl AppServer { + pub async fn connect(stream: TcpStream) -> Result { + let ws_stream = accept_async(stream).await?; + Ok(WsStream::Server(ws_stream)) + } + + pub async fn run() { + let addr = "0.0.0.0:4399"; + let listener = TcpListener::bind(&addr) + .await + .expect(format!("❌ 绑定地址失败: {}", &addr).as_str()); + println!("✅ 已启动: {:?}", addr); + while let Ok((stream, addr)) = listener.accept().await { + // 同一时刻只处理一个连接 + AppServer::handle_connection(stream, addr).await; + } + } + + async fn handle_connection(stream: TcpStream, addr: std::net::SocketAddr) { + let Ok(ws_stream) = AppServer::connect(stream).await else { + println!("❌ 连接异常: {}", addr); + return; + }; + println!("✅ 已连接: {:?}", addr); + AppServer::init(ws_stream).await; + if let Err(e) = MessageManager::instance().process_messages().await { + println!("❌ 消息处理异常: {}", e); + } + AppServer::dispose().await; + println!("❌ 已断开连接"); + } + + async fn init(ws_stream: WsStream) { + MessageManager::instance().init(ws_stream).await; + MessageHandler::::instance() + .set_handler(on_event) + .await; + MessageHandler::::instance() + .set_handler(on_stream) + .await; + + let rpc = RPC::instance(); + rpc.add_command("get_version", get_version).await; + + let test = tokio::spawn(async move { + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + let _ = test().await; + }); + TaskManager::instance().add("test", test).await; + } + + async fn dispose() { + MessageManager::instance().dispose().await; + TaskManager::instance().dispose("test").await; + } +} + +async fn get_version(_: Request) -> Result { + let data = json!(VERSION.to_string()); + Ok(Response::from_data(data)) +} + +async fn on_stream(stream: Stream) -> Result<(), AppError> { + let Stream { tag, bytes, .. } = stream; + match tag.as_str() { + "record" => { + NodeManager::instance() + .call_fn::<(), _, _>( + "on_input_data", + move |cx| JsUint8Array::from_slice(cx, &bytes).unwrap().upcast(), + |_, _| Ok(()), + ) + .await?; + } + _ => {} + } + Ok(()) +} + +async fn on_event(event: Event) -> Result<(), AppError> { + let event_json = serde_json::to_string(&event)?; + NodeManager::instance() + .call_fn::<(), _, _>( + "on_event", + move |cx| cx.string(&event_json).upcast(), + |_, _| Ok(()), + ) + .await?; + Ok(()) +} diff --git a/packages/server-node/tsconfig.json b/packages/server-node/tsconfig.json new file mode 100644 index 0000000..07665a5 --- /dev/null +++ b/packages/server-node/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@mi-gpt/config/typescript", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["migpt/*"] + } + }, + "include": ["migpt"], + "exclude": ["node_modules"] +} diff --git a/packages/server-python/README.md b/packages/server-python/README.md index a9cf594..16b1c87 100644 --- a/packages/server-python/README.md +++ b/packages/server-python/README.md @@ -42,4 +42,4 @@ uv run main.py --mode xiaozhi 请按照提示注册你的小智 AI 账号,然后创建 Agent 绑定设备即可开始体验。 > [!NOTE] -> 本项目只是一个简单的演示程序,抛砖引玉。如果你需要更多的功能,比如唤醒词识别、语音转文字、连续对话等,可以参考开源的 [xiaozhi-esp32-server](https://github.com/xinnan-tech/xiaozhi-esp32-server) 项目,借助 Python 丰富的 AI 生态,自行实现自己想要的功能。 +> 本项目只是一个简单的演示程序,抛砖引玉。如果你需要更多的功能,比如唤醒词识别、语音转文字、连续对话等,可以参考开源的 [xiaozhi-esp32-server](https://github.com/xinnan-tech/xiaozhi-esp32-server) 项目,借助 Python 丰富的 AI 生态,自行实现想要的功能。