Spaces:
Running
Running
Commit
·
540cfa6
0
Parent(s):
first commit
Browse files- .gitignore +24 -0
- LICENSE +201 -0
- docs/conventions.md +143 -0
- docs/getting_started_with_so100.md +219 -0
- docs/planning/001_find_usb_ports.md +124 -0
- index.html +13 -0
- package.json +48 -0
- pnpm-lock.yaml +813 -0
- public/vite.svg +1 -0
- src/cli/index.ts +65 -0
- src/counter.ts +9 -0
- src/lerobot/find_port.ts +127 -0
- src/main.ts +24 -0
- src/style.css +96 -0
- src/typescript.svg +1 -0
- src/vite-env.d.ts +1 -0
- tsconfig.cli.json +21 -0
- tsconfig.json +25 -0
.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Logs
|
2 |
+
logs
|
3 |
+
*.log
|
4 |
+
npm-debug.log*
|
5 |
+
yarn-debug.log*
|
6 |
+
yarn-error.log*
|
7 |
+
pnpm-debug.log*
|
8 |
+
lerna-debug.log*
|
9 |
+
|
10 |
+
node_modules
|
11 |
+
dist
|
12 |
+
dist-ssr
|
13 |
+
*.local
|
14 |
+
|
15 |
+
# Editor directories and files
|
16 |
+
.vscode/*
|
17 |
+
!.vscode/extensions.json
|
18 |
+
.idea
|
19 |
+
.DS_Store
|
20 |
+
*.suo
|
21 |
+
*.ntvs*
|
22 |
+
*.njsproj
|
23 |
+
*.sln
|
24 |
+
*.sw?
|
LICENSE
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Apache License
|
2 |
+
Version 2.0, January 2004
|
3 |
+
http://www.apache.org/licenses/
|
4 |
+
|
5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6 |
+
|
7 |
+
1. Definitions.
|
8 |
+
|
9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
11 |
+
|
12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
13 |
+
the copyright owner that is granting the License.
|
14 |
+
|
15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
16 |
+
other entities that control, are controlled by, or are under common
|
17 |
+
control with that entity. For the purposes of this definition,
|
18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
19 |
+
direction or management of such entity, whether by contract or
|
20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
22 |
+
|
23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
24 |
+
exercising permissions granted by this License.
|
25 |
+
|
26 |
+
"Source" form shall mean the preferred form for making modifications,
|
27 |
+
including but not limited to software source code, documentation
|
28 |
+
source, and configuration files.
|
29 |
+
|
30 |
+
"Object" form shall mean any form resulting from mechanical
|
31 |
+
transformation or translation of a Source form, including but
|
32 |
+
not limited to compiled object code, generated documentation,
|
33 |
+
and conversions to other media types.
|
34 |
+
|
35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
36 |
+
Object form, made available under the License, as indicated by a
|
37 |
+
copyright notice that is included in or attached to the work
|
38 |
+
(an example is provided in the Appendix below).
|
39 |
+
|
40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
41 |
+
form, that is based on (or derived from) the Work and for which the
|
42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
44 |
+
of this License, Derivative Works shall not include works that remain
|
45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
46 |
+
the Work and Derivative Works thereof.
|
47 |
+
|
48 |
+
"Contribution" shall mean any work of authorship, including
|
49 |
+
the original version of the Work and any modifications or additions
|
50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
54 |
+
means any form of electronic, verbal, or written communication sent
|
55 |
+
to the Licensor or its representatives, including but not limited to
|
56 |
+
communication on electronic mailing lists, source code control systems,
|
57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
59 |
+
excluding communication that is conspicuously marked or otherwise
|
60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
61 |
+
|
62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
64 |
+
subsequently incorporated within the Work.
|
65 |
+
|
66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
71 |
+
Work and such Derivative Works in Source or Object form.
|
72 |
+
|
73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
76 |
+
(except as stated in this section) patent license to make, have made,
|
77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
78 |
+
where such license applies only to those patent claims licensable
|
79 |
+
by such Contributor that are necessarily infringed by their
|
80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
82 |
+
institute patent litigation against any entity (including a
|
83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
84 |
+
or a Contribution incorporated within the Work constitutes direct
|
85 |
+
or contributory patent infringement, then any patent licenses
|
86 |
+
granted to You under this License for that Work shall terminate
|
87 |
+
as of the date such litigation is filed.
|
88 |
+
|
89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
90 |
+
Work or Derivative Works thereof in any medium, with or without
|
91 |
+
modifications, and in Source or Object form, provided that You
|
92 |
+
meet the following conditions:
|
93 |
+
|
94 |
+
(a) You must give any other recipients of the Work or
|
95 |
+
Derivative Works a copy of this License; and
|
96 |
+
|
97 |
+
(b) You must cause any modified files to carry prominent notices
|
98 |
+
stating that You changed the files; and
|
99 |
+
|
100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
101 |
+
that You distribute, all copyright, patent, trademark, and
|
102 |
+
attribution notices from the Source form of the Work,
|
103 |
+
excluding those notices that do not pertain to any part of
|
104 |
+
the Derivative Works; and
|
105 |
+
|
106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
107 |
+
distribution, then any Derivative Works that You distribute must
|
108 |
+
include a readable copy of the attribution notices contained
|
109 |
+
within such NOTICE file, excluding those notices that do not
|
110 |
+
pertain to any part of the Derivative Works, in at least one
|
111 |
+
of the following places: within a NOTICE text file distributed
|
112 |
+
as part of the Derivative Works; within the Source form or
|
113 |
+
documentation, if provided along with the Derivative Works; or,
|
114 |
+
within a display generated by the Derivative Works, if and
|
115 |
+
wherever such third-party notices normally appear. The contents
|
116 |
+
of the NOTICE file are for informational purposes only and
|
117 |
+
do not modify the License. You may add Your own attribution
|
118 |
+
notices within Derivative Works that You distribute, alongside
|
119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
120 |
+
that such additional attribution notices cannot be construed
|
121 |
+
as modifying the License.
|
122 |
+
|
123 |
+
You may add Your own copyright statement to Your modifications and
|
124 |
+
may provide additional or different license terms and conditions
|
125 |
+
for use, reproduction, or distribution of Your modifications, or
|
126 |
+
for any such Derivative Works as a whole, provided Your use,
|
127 |
+
reproduction, and distribution of the Work otherwise complies with
|
128 |
+
the conditions stated in this License.
|
129 |
+
|
130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
132 |
+
by You to the Licensor shall be under the terms and conditions of
|
133 |
+
this License, without any additional terms or conditions.
|
134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
135 |
+
the terms of any separate license agreement you may have executed
|
136 |
+
with Licensor regarding such Contributions.
|
137 |
+
|
138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
140 |
+
except as required for reasonable and customary use in describing the
|
141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
142 |
+
|
143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
144 |
+
agreed to in writing, Licensor provides the Work (and each
|
145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
147 |
+
implied, including, without limitation, any warranties or conditions
|
148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
150 |
+
appropriateness of using or redistributing the Work and assume any
|
151 |
+
risks associated with Your exercise of permissions under this License.
|
152 |
+
|
153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
154 |
+
whether in tort (including negligence), contract, or otherwise,
|
155 |
+
unless required by applicable law (such as deliberate and grossly
|
156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
157 |
+
liable to You for damages, including any direct, indirect, special,
|
158 |
+
incidental, or consequential damages of any character arising as a
|
159 |
+
result of this License or out of the use or inability to use the
|
160 |
+
Work (including but not limited to damages for loss of goodwill,
|
161 |
+
work stoppage, computer failure or malfunction, or any and all
|
162 |
+
other commercial damages or losses), even if such Contributor
|
163 |
+
has been advised of the possibility of such damages.
|
164 |
+
|
165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
168 |
+
or other liability obligations and/or rights consistent with this
|
169 |
+
License. However, in accepting such obligations, You may act only
|
170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
171 |
+
of any other Contributor, and only if You agree to indemnify,
|
172 |
+
defend, and hold each Contributor harmless for any liability
|
173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
174 |
+
of your accepting any such warranty or additional liability.
|
175 |
+
|
176 |
+
END OF TERMS AND CONDITIONS
|
177 |
+
|
178 |
+
APPENDIX: How to apply the Apache License to your work.
|
179 |
+
|
180 |
+
To apply the Apache License to your work, attach the following
|
181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
182 |
+
replaced with your own identifying information. (Don't include
|
183 |
+
the brackets!) The text should be enclosed in the appropriate
|
184 |
+
comment syntax for the file format. We also recommend that a
|
185 |
+
file or class name and description of purpose be included on the
|
186 |
+
same "printed page" as the copyright notice for easier
|
187 |
+
identification within third-party archives.
|
188 |
+
|
189 |
+
Copyright 2025 Tim Pietrusky
|
190 |
+
|
191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
192 |
+
you may not use this file except in compliance with the License.
|
193 |
+
You may obtain a copy of the License at
|
194 |
+
|
195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
196 |
+
|
197 |
+
Unless required by applicable law or agreed to in writing, software
|
198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200 |
+
See the License for the specific language governing permissions and
|
201 |
+
limitations under the License.
|
docs/conventions.md
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# LeRobot.js Conventions
|
2 |
+
|
3 |
+
## Project Overview
|
4 |
+
|
5 |
+
**lerobot.js** is a TypeScript/JavaScript implementation of Hugging Face's [lerobot](https://github.com/huggingface/lerobot) robotics library. Our goal is to bring state-of-the-art AI for real-world robotics directly to the JavaScript ecosystem, enabling robot control without any Python dependencies.
|
6 |
+
|
7 |
+
### Vision Statement
|
8 |
+
|
9 |
+
> Lower the barrier to entry for robotics by making cutting-edge robotic AI accessible through JavaScript, the world's most widely used programming language.
|
10 |
+
|
11 |
+
## Project Goals
|
12 |
+
|
13 |
+
### Primary Objectives
|
14 |
+
|
15 |
+
1. **Native JavaScript/TypeScript Implementation**: Complete robotics stack running purely in JS/TS
|
16 |
+
2. **Feature Parity**: Implement core functionality from the original Python lerobot
|
17 |
+
3. **Web-First Design**: Enable robotics applications to run in browsers, Edge devices, and Node.js
|
18 |
+
4. **Real-World Robot Control**: Direct hardware interface without Python bridge
|
19 |
+
5. **Hugging Face Integration**: Seamless model and dataset loading from HF Hub
|
20 |
+
|
21 |
+
### Core Features to Implement
|
22 |
+
|
23 |
+
- **Pretrained Models**: Load and run robotics policies (ACT, Diffusion, TDMPC, VQ-BeT)
|
24 |
+
- **Dataset Management**: LeRobotDataset format with HF Hub integration
|
25 |
+
- **Simulation Environments**: Browser-based robotics simulations
|
26 |
+
- **Real Robot Support**: Hardware interfaces for motors, cameras, sensors
|
27 |
+
- **Training Infrastructure**: Policy training and evaluation tools
|
28 |
+
- **Visualization Tools**: Dataset and robot state visualization
|
29 |
+
|
30 |
+
## Technical Foundation
|
31 |
+
|
32 |
+
### Core Stack
|
33 |
+
|
34 |
+
- **Runtime**: Node.js 18+ / Modern Browsers
|
35 |
+
- **Language**: TypeScript with strict type checking
|
36 |
+
- **Build Tool**: Vite (development and production builds)
|
37 |
+
- **Package Manager**: pnpm
|
38 |
+
- **Module System**: ES Modules
|
39 |
+
- **Target**: ES2020
|
40 |
+
|
41 |
+
## Architecture Principles
|
42 |
+
|
43 |
+
### 1. Python lerobot Faithfulness (Primary Principle)
|
44 |
+
|
45 |
+
**lerobot.js must maintain UX/API compatibility with Python lerobot**
|
46 |
+
|
47 |
+
- **Identical Commands**: `npx lerobot find-port` matches `python -m lerobot.find_port`
|
48 |
+
- **Same Terminology**: Use "MotorsBus", not "robot arms" - keep Python's exact wording
|
49 |
+
- **Matching Output**: Error messages, prompts, and flow identical to Python version
|
50 |
+
- **Familiar Workflows**: Python lerobot users should feel immediately at home
|
51 |
+
- **No "Improvements"**: Resist urge to add features/UX that Python version doesn't have
|
52 |
+
|
53 |
+
> **Why?** Users are already trained on Python lerobot. Our goal is seamless migration to TypeScript, not learning a new tool.
|
54 |
+
|
55 |
+
### 2. Modular Design
|
56 |
+
|
57 |
+
```
|
58 |
+
lerobot/
|
59 |
+
├── common/
|
60 |
+
│ ├── datasets/ # Dataset loading and management
|
61 |
+
│ ├── envs/ # Simulation environments
|
62 |
+
│ ├── policies/ # AI policies and models
|
63 |
+
│ ├── devices/ # Hardware device interfaces
|
64 |
+
│ └── utils/ # Shared utilities
|
65 |
+
├── core/ # Core robotics primitives
|
66 |
+
├── web/ # Browser-specific implementations
|
67 |
+
└── node/ # Node.js-specific implementations
|
68 |
+
```
|
69 |
+
|
70 |
+
### 3. Platform Abstraction
|
71 |
+
|
72 |
+
- **Universal Core**: Platform-agnostic robotics logic
|
73 |
+
- **Web Adapters**: Browser-specific implementations (WebGL, WebAssembly, WebUSB)
|
74 |
+
- **Node Adapters**: Node.js implementations (native modules, serial ports)
|
75 |
+
|
76 |
+
### 4. Progressive Enhancement
|
77 |
+
|
78 |
+
- **Core Functionality**: Works everywhere (basic policy inference)
|
79 |
+
- **Enhanced Features**: Leverage platform capabilities (GPU acceleration, hardware access)
|
80 |
+
- **Premium Features**: Advanced capabilities (real-time training, complex simulations)
|
81 |
+
|
82 |
+
## Development Standards
|
83 |
+
|
84 |
+
### Code Style
|
85 |
+
|
86 |
+
- **Formatting**: Prettier with default settings
|
87 |
+
- **Linting**: ESLint with TypeScript recommended rules
|
88 |
+
- **Naming**:
|
89 |
+
- camelCase for variables/functions
|
90 |
+
- PascalCase for classes/types
|
91 |
+
- snake_case for file names (following lerobot convention)
|
92 |
+
- **File Structure**: Feature-based organization with index.ts barrels
|
93 |
+
|
94 |
+
### TypeScript Standards
|
95 |
+
|
96 |
+
- **Strict Mode**: All strict compiler options enabled
|
97 |
+
- **Type Safety**: Prefer types over interfaces for data structures
|
98 |
+
- **Generics**: Use generics for reusable components
|
99 |
+
- **Error Handling**: Use Result<T, E> pattern for recoverable errors
|
100 |
+
|
101 |
+
### Implementation Philosophy
|
102 |
+
|
103 |
+
- **Python First**: When in doubt, check how Python lerobot does it
|
104 |
+
- **Port, Don't Innovate**: Direct ports are better than clever improvements
|
105 |
+
- **User Expectations**: Maintain the exact experience Python users expect
|
106 |
+
- **Terminology Consistency**: Use Python lerobot's exact naming and messaging
|
107 |
+
|
108 |
+
### Development Process
|
109 |
+
|
110 |
+
- **Python Reference**: Always check Python lerobot implementation first
|
111 |
+
- **UX Matching**: Test that commands, outputs, and workflows match exactly
|
112 |
+
- **User Story Validation**: Validate against real Python lerobot users
|
113 |
+
|
114 |
+
### Testing Strategy
|
115 |
+
|
116 |
+
- **Unit Tests**: Vitest for individual functions and classes
|
117 |
+
- **Integration Tests**: Test component interactions
|
118 |
+
- **E2E Tests**: Playwright for full workflow testing
|
119 |
+
- **Hardware Tests**: Mock/stub hardware interfaces for CI
|
120 |
+
- **UX Compatibility Tests**: Verify outputs match Python version
|
121 |
+
|
122 |
+
## Package Structure
|
123 |
+
|
124 |
+
### NPM Package Name
|
125 |
+
|
126 |
+
- **Public Package**: `lerobot` (on npm)
|
127 |
+
- **Development Name**: `lerobot.js` (GitHub repository)
|
128 |
+
|
129 |
+
## Dependencies Strategy
|
130 |
+
|
131 |
+
### Core Dependencies
|
132 |
+
|
133 |
+
- **ML Inference**: ONNX.js for model execution (browser + Node.js)
|
134 |
+
- **Tensor Operations**: Custom lightweight tensor lib for data manipulation
|
135 |
+
- **Math**: Custom math utilities for robotics
|
136 |
+
- **Networking**: Fetch API (universal)
|
137 |
+
- **File I/O**: Platform-appropriate abstractions
|
138 |
+
|
139 |
+
### Optional Enhanced Dependencies
|
140 |
+
|
141 |
+
- **3D Graphics**: Three.js for simulation and visualization
|
142 |
+
- **Hardware**: Platform-specific libraries for device access
|
143 |
+
- **Development**: Vitest, ESLint, Prettier
|
docs/getting_started_with_so100.md
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## install lerobot
|
2 |
+
|
3 |
+
- clone the repo: git clone https://github.com/huggingface/lerobot.git
|
4 |
+
- go into the dir: cd lerobot
|
5 |
+
- setup venv with your favorite tool, i use uv: uv venv --python 3.10
|
6 |
+
- activate venv: windows: .venv\Scripts\activate
|
7 |
+
- install dependencies: uv pip install -e ".[feetech]"
|
8 |
+
(we install feetech here as that is needed to talk with the motors that the so100 is using)
|
9 |
+
|
10 |
+
## identify USB ports
|
11 |
+
|
12 |
+
- connect usb / power
|
13 |
+
- run `python lerobot/find_port.py`
|
14 |
+
|
15 |
+
```
|
16 |
+
python lerobot/find_port.py
|
17 |
+
Finding all available ports for the MotorsBus.
|
18 |
+
Ports before disconnecting: ['COM3', 'COM4']
|
19 |
+
Remove the USB cable from your MotorsBus and press Enter when done.
|
20 |
+
|
21 |
+
The port of this MotorsBus is 'COM3'
|
22 |
+
Reconnect the USB cable.
|
23 |
+
```
|
24 |
+
|
25 |
+
- the port it will show you is the one from the disconnected arm
|
26 |
+
- in my case on windows the follower arm is COM4 and the leader arm is COM3 (but this could change, as the port mappings on windows are not fixed)
|
27 |
+
|
28 |
+
## check motor ids
|
29 |
+
|
30 |
+
**Important: Always do this first!** This checks if your robot motors are already configured correctly. If they are, you can skip the tedious setup process entirely.
|
31 |
+
|
32 |
+
**What are motor IDs?** Each motor needs a unique ID number (1, 2, 3, 4, 5, 6) so the computer can talk to them individually. Brand new motors usually all have the same default ID (1), which doesn't work.
|
33 |
+
|
34 |
+
### check follower arm
|
35 |
+
|
36 |
+
```
|
37 |
+
python -m lerobot.check_motors --robot.port=COM4
|
38 |
+
```
|
39 |
+
|
40 |
+
### check leader arm
|
41 |
+
|
42 |
+
```
|
43 |
+
python -m lerobot.check_motors --teleop.port=COM3
|
44 |
+
```
|
45 |
+
|
46 |
+
**If you see this - you're lucky! Skip to calibration:**
|
47 |
+
|
48 |
+
```
|
49 |
+
🎉 PERFECT! This arm is correctly configured:
|
50 |
+
✅ All 6 motors found: [1, 2, 3, 4, 5, 6]
|
51 |
+
✅ Correct baudrate: 1000000
|
52 |
+
|
53 |
+
✅ This arm is ready for calibration!
|
54 |
+
```
|
55 |
+
|
56 |
+
**If you see this - continue to "setup motors" below:**
|
57 |
+
|
58 |
+
```
|
59 |
+
⚠️ This arm needs motor ID setup:
|
60 |
+
Expected IDs: [1, 2, 3, 4, 5, 6]
|
61 |
+
Found IDs: [1, 1, 1, 1, 1, 1]
|
62 |
+
Duplicate IDs: [1] (likely all motors have ID=1)
|
63 |
+
```
|
64 |
+
|
65 |
+
## setup motors
|
66 |
+
|
67 |
+
**⚠️ Only do this section if the motor check above showed your motors need setup!**
|
68 |
+
|
69 |
+
This is a one-time process where you connect each motor individually to assign unique ID numbers. It's tedious but only needed once - the ID gets permanently stored in each motor's memory.
|
70 |
+
|
71 |
+
**Why is this needed?** Motors come from the factory with the same default ID (usually 1). The computer can't tell them apart, so we need to give each motor a unique number.
|
72 |
+
|
73 |
+
**⚠️ Important safety notes:**
|
74 |
+
|
75 |
+
- Always power down (unplug power + USB) when connecting/disconnecting motors
|
76 |
+
- Connect only ONE motor at a time during this process
|
77 |
+
- The motor gears should be removed from the leader arm before this step
|
78 |
+
|
79 |
+
### follower arm
|
80 |
+
|
81 |
+
```
|
82 |
+
python -m lerobot.setup_motors --robot.type=so100_follower --robot.port=COM4
|
83 |
+
```
|
84 |
+
|
85 |
+
The script will guide you through connecting each motor one by one. Follow the prompts carefully.
|
86 |
+
|
87 |
+
### leader arm
|
88 |
+
|
89 |
+
```
|
90 |
+
python -m lerobot.setup_motors --teleop.type=so100_leader --teleop.port=COM3
|
91 |
+
```
|
92 |
+
|
93 |
+
Same process as the follower arm.
|
94 |
+
|
95 |
+
**After setup:** Run the motor check commands again to verify everything worked correctly. You should now see the "PERFECT!" message.
|
96 |
+
|
97 |
+
## calibrate
|
98 |
+
|
99 |
+
**What is calibration?** This teaches both robot arms to have the same understanding of joint positions. When both arms are in the same physical pose, they should report the same position values. This is crucial for the leader arm to properly control the follower arm.
|
100 |
+
|
101 |
+
**Why is this needed?** Even though the arms are identical, small manufacturing differences mean their position sensors might read slightly different values for the same physical position. Calibration fixes this.
|
102 |
+
|
103 |
+
**Important:** Make sure both arms have all motors properly connected (daisy-chained) before calibrating. The motor setup process has you disconnect motors individually, but for calibration you need the full arm assembled.
|
104 |
+
|
105 |
+
> **💡 Don't be intimidated by calibration!**
|
106 |
+
>
|
107 |
+
> **It's really simple:** You just manually move each joint to the correct position - no special technique required! The calibration process is just:
|
108 |
+
>
|
109 |
+
> 1. Start from the neutral position (standard in robotics)
|
110 |
+
> 2. Manually move each motor/joint to where it needs to be
|
111 |
+
> 3. Move them one at a time at whatever pace feels comfortable
|
112 |
+
> 4. The robot learns from the positions you set
|
113 |
+
>
|
114 |
+
> **That's it!** You're just physically positioning the joints - the software handles all the complex stuff automatically.
|
115 |
+
|
116 |
+
### calibrate follower arm
|
117 |
+
|
118 |
+
**Step 1:** Start the calibration script:
|
119 |
+
|
120 |
+
```
|
121 |
+
python -m lerobot.calibrate --robot.type=so100_follower --robot.port=COM4 --robot.id=my_follower_arm
|
122 |
+
```
|
123 |
+
|
124 |
+
**Step 2:** Follow the script's prompts:
|
125 |
+
|
126 |
+
1. The script will ask you to **move the arm to the middle position**. This should be a **neutral pose** where:
|
127 |
+
|
128 |
+
- **Shoulder pan (base, joint 1):** 0° - Point the arm straight forward along the +X axis (not twisted left or right)
|
129 |
+
- **Shoulder lift (joint 2):** ~45° - Lift the upper arm segment upward at roughly 45 degrees from horizontal
|
130 |
+
- **Elbow (joint 3):** ~90° - Bend the elbow to form an "L" shape, with the forearm perpendicular to the upper arm
|
131 |
+
- **Wrist flex (joint 4):** 0° - Keep the wrist straight, aligned with the forearm
|
132 |
+
- **Wrist roll (joint 5):** 0° - Keep the gripper pointing straight (not twisted around the forearm axis)
|
133 |
+
- **Gripper (joint 6):** Fully closed position (mechanical hard stop)
|
134 |
+
|
135 |
+
**Technical reference:** This is similar to a "home position" or "zero configuration" used in robot calibration - a repeatable, neutral pose that avoids joint limits and singularities. The arm should form clear geometric references (forward direction, L-shaped elbow) that are easy to reproduce.
|
136 |
+
|
137 |
+
**💡 Tip:** Once you get the arm in the correct position, consider taking a photo for future reference!
|
138 |
+
|
139 |
+
2. The script will ask you to **move joints through their full range** - gently move each joint from its furthest position in one direction to its furthest position in the other direction (the joint will stop when it hits its physical limit). Do this for all joints while the script records, then press Enter when done
|
140 |
+
3. The script will **save the calibration automatically**
|
141 |
+
|
142 |
+
### calibrate leader arm
|
143 |
+
|
144 |
+
**Step 1:** Start the calibration script:
|
145 |
+
|
146 |
+
```
|
147 |
+
python -m lerobot.calibrate --teleop.type=so100_leader --teleop.port=COM3 --teleop.id=my_leader_arm
|
148 |
+
```
|
149 |
+
|
150 |
+
**Step 2:** Follow the script's prompts:
|
151 |
+
|
152 |
+
1. The script will ask you to **move the arm to the middle position**. This should be a **neutral pose** where:
|
153 |
+
|
154 |
+
- **Shoulder pan (base, joint 1):** 0° - Point the arm straight forward along the +X axis (not twisted left or right)
|
155 |
+
- **Shoulder lift (joint 2):** ~45° - Lift the upper arm segment upward at roughly 45 degrees from horizontal
|
156 |
+
- **Elbow (joint 3):** ~90° - Bend the elbow to form an "L" shape, with the forearm perpendicular to the upper arm
|
157 |
+
- **Wrist flex (joint 4):** 0° - Keep the wrist straight, aligned with the forearm
|
158 |
+
- **Wrist roll (joint 5):** 0° - Keep the gripper pointing straight (not twisted around the forearm axis)
|
159 |
+
- **Gripper (joint 6):** Fully closed position (mechanical hard stop)
|
160 |
+
|
161 |
+
**Technical reference:** This is similar to a "home position" or "zero configuration" used in robot calibration - a repeatable, neutral pose that avoids joint limits and singularities.
|
162 |
+
|
163 |
+
2. The script will ask you to **move joints through their full range** - gently move each joint from its furthest position in one direction to its furthest position in the other direction (the joint will stop when it hits its physical limit). Do this for all joints while the script records, then press Enter when done
|
164 |
+
3. The script will **save the calibration automatically**
|
165 |
+
|
166 |
+
**✅ After both arms are calibrated, you're ready for teleoperation!**
|
167 |
+
|
168 |
+
## test teleoperation
|
169 |
+
|
170 |
+
Now test that your leader arm can control the follower arm:
|
171 |
+
|
172 |
+
```bash
|
173 |
+
python -m lerobot.teleoperate --robot.type=so100_follower --robot.port=COM4 --robot.id=my_follower_arm --teleop.type=so100_leader --teleop.port=COM3 --teleop.id=my_leader_arm
|
174 |
+
```
|
175 |
+
|
176 |
+
**What should happen:**
|
177 |
+
|
178 |
+
- Both arms should connect automatically
|
179 |
+
- When you move the leader arm (manually), the follower arm should copy the movements
|
180 |
+
- Press `Ctrl+C` to stop teleoperation
|
181 |
+
|
182 |
+
**If something goes wrong:**
|
183 |
+
|
184 |
+
- Check your COM ports are correct
|
185 |
+
- Make sure both arms are powered on
|
186 |
+
- Verify the cables are properly connected
|
187 |
+
|
188 |
+
## record demonstrations (optional)
|
189 |
+
|
190 |
+
Once teleoperation works, you can record demonstrations for training a robot learning policy.
|
191 |
+
|
192 |
+
**For example, to record 10 episodes of a simple task:**
|
193 |
+
|
194 |
+
```bash
|
195 |
+
python -m lerobot.record --robot.type=so100_follower --robot.port=COM4 --robot.id=my_follower_arm --teleop.type=so100_leader --teleop.port=COM3 --teleop.id=my_leader_arm --dataset-name=my_first_dataset --num-episodes=10 --task="Pick up the red block and place it in the box"
|
196 |
+
```
|
197 |
+
|
198 |
+
**What this does:**
|
199 |
+
|
200 |
+
- Records your demonstrations using teleoperation
|
201 |
+
- Saves data to train a neural network later
|
202 |
+
- Each "episode" is one complete demonstration of the task
|
203 |
+
|
204 |
+
## next steps
|
205 |
+
|
206 |
+
✅ **You now have working SO-100 robot arms!**
|
207 |
+
|
208 |
+
**To continue with robot learning:**
|
209 |
+
|
210 |
+
- Follow the [Getting Started with Real-World Robots](https://huggingface.co/docs/lerobot/getting_started_real_world_robot) tutorial
|
211 |
+
- Learn how to train policies and run them autonomously
|
212 |
+
- Join the [Discord community](https://discord.com/invite/s3KuuzsPFb) for help and discussions
|
213 |
+
|
214 |
+
**Common next steps:**
|
215 |
+
|
216 |
+
1. **Add cameras** for vision-based tasks
|
217 |
+
2. **Record more complex demonstrations**
|
218 |
+
3. **Train neural network policies**
|
219 |
+
4. **Run policies autonomously**
|
docs/planning/001_find_usb_ports.md
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# User Story 001: Find USB Ports
|
2 |
+
|
3 |
+
## Story
|
4 |
+
|
5 |
+
**As a** robotics developer setting up SO-100 robot arms
|
6 |
+
**I want** to identify which USB ports my robot arms are connected to
|
7 |
+
**So that** I can configure my robot arms without manually testing each port
|
8 |
+
|
9 |
+
## Background
|
10 |
+
|
11 |
+
When setting up SO-100 robot arms (or any robotics hardware), users need to identify which USB/serial ports correspond to their robot controllers. The Python lerobot provides a `find_port.py` script that:
|
12 |
+
|
13 |
+
1. Lists all available serial ports
|
14 |
+
2. Asks the user to disconnect a specific device
|
15 |
+
3. Detects which port disappeared
|
16 |
+
4. Identifies that port as belonging to the disconnected device
|
17 |
+
|
18 |
+
We need to replicate this functionality in Node.js for lerobot.js.
|
19 |
+
|
20 |
+
## Acceptance Criteria
|
21 |
+
|
22 |
+
### Core Functionality
|
23 |
+
|
24 |
+
- [ ] **Port Discovery**: List all available serial/USB ports on the system
|
25 |
+
- [ ] **Interactive Detection**: Guide user through disconnect/reconnect process
|
26 |
+
- [ ] **Port Identification**: Detect which port corresponds to which robot arm
|
27 |
+
- [ ] **Cross-Platform**: Work on Windows, macOS, and Linux
|
28 |
+
- [ ] **CLI Interface**: Provide `npx lerobot find-port` command identical to Python version
|
29 |
+
|
30 |
+
### User Experience
|
31 |
+
|
32 |
+
- [ ] **Clear Instructions**: Provide step-by-step guidance to user
|
33 |
+
- [ ] **Error Handling**: Handle cases where no ports are found or detection fails
|
34 |
+
- [ ] **Progress Feedback**: Show current status during detection process
|
35 |
+
- [ ] **Results Display**: Clearly show which port belongs to which device
|
36 |
+
|
37 |
+
### Technical Requirements
|
38 |
+
|
39 |
+
- [ ] **Node.js Native**: Use Node.js serial port libraries (no Python dependencies)
|
40 |
+
- [ ] **TypeScript**: Fully typed implementation following our conventions
|
41 |
+
- [ ] **CLI Tool**: Executable via `npx lerobot find-port` (matching Python version)
|
42 |
+
- [ ] **snake_case**: File naming follows lerobot conventions
|
43 |
+
|
44 |
+
## Expected User Flow
|
45 |
+
|
46 |
+
```bash
|
47 |
+
$ npx lerobot find-port
|
48 |
+
|
49 |
+
Finding all available ports for the MotorsBus.
|
50 |
+
Ports before disconnecting: ['COM3', 'COM4']
|
51 |
+
Remove the USB cable from your MotorsBus and press Enter when done.
|
52 |
+
|
53 |
+
The port of this MotorsBus is 'COM3'
|
54 |
+
Reconnect the USB cable.
|
55 |
+
```
|
56 |
+
|
57 |
+
**For multiple arms, run separately:**
|
58 |
+
|
59 |
+
```bash
|
60 |
+
# First arm
|
61 |
+
$ npx lerobot find-port
|
62 |
+
# Follow prompts...
|
63 |
+
|
64 |
+
# Second arm
|
65 |
+
$ npx lerobot find-port
|
66 |
+
# Follow prompts...
|
67 |
+
```
|
68 |
+
|
69 |
+
## Implementation Details
|
70 |
+
|
71 |
+
### File Structure
|
72 |
+
|
73 |
+
```
|
74 |
+
src/lerobot/
|
75 |
+
└── find_port.ts # Direct port of Python script
|
76 |
+
|
77 |
+
src/cli/
|
78 |
+
└── index.ts # Main CLI entry point with find-port command
|
79 |
+
```
|
80 |
+
|
81 |
+
### Key Dependencies
|
82 |
+
|
83 |
+
- **serialport**: For cross-platform serial port detection and management
|
84 |
+
- **readline**: Node.js built-in for simple input() equivalent
|
85 |
+
|
86 |
+
### Core Functions to Implement
|
87 |
+
|
88 |
+
```typescript
|
89 |
+
// find_port.ts (matching Python naming)
|
90 |
+
async function findAvailablePorts(): Promise<string[]>;
|
91 |
+
async function findPort(): Promise<void>; // Main interactive function
|
92 |
+
```
|
93 |
+
|
94 |
+
### Technical Considerations
|
95 |
+
|
96 |
+
#### Cross-Platform Serial Ports
|
97 |
+
|
98 |
+
- **Windows**: COM ports (COM1, COM2, etc.)
|
99 |
+
- **macOS**: /dev/cu._ or /dev/tty._
|
100 |
+
- **Linux**: /dev/ttyUSB*, /dev/ttyACM*
|
101 |
+
|
102 |
+
#### Error Scenarios to Handle
|
103 |
+
|
104 |
+
- No serial ports detected
|
105 |
+
- Multiple ports disconnected simultaneously
|
106 |
+
- Port permissions issues
|
107 |
+
- User cancels detection process
|
108 |
+
- Same port reconnected to different device
|
109 |
+
|
110 |
+
#### Performance & UX
|
111 |
+
|
112 |
+
- Fast port scanning (< 1 second)
|
113 |
+
- Clear progress indicators
|
114 |
+
- Timeout handling for user input
|
115 |
+
- Graceful interrupt handling (Ctrl+C)
|
116 |
+
|
117 |
+
## Definition of Done
|
118 |
+
|
119 |
+
- [ ] **Functional**: Successfully identifies robot arm ports on Windows/macOS/Linux
|
120 |
+
- [ ] **Tested**: Unit tests for core logic, integration tests with mock serial ports
|
121 |
+
- [ ] **Documented**: README section explaining usage
|
122 |
+
- [ ] **CLI Ready**: Installable and runnable as CLI tool
|
123 |
+
- [ ] **Type Safe**: Full TypeScript coverage with strict mode
|
124 |
+
- [ ] **Follows Conventions**: snake_case files, proper architecture
|
index.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>Vite + TS</title>
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<div id="app"></div>
|
11 |
+
<script type="module" src="/src/main.ts"></script>
|
12 |
+
</body>
|
13 |
+
</html>
|
package.json
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "lerobot",
|
3 |
+
"version": "0.0.0",
|
4 |
+
"description": "State-of-the-art AI for real-world robotics in JS",
|
5 |
+
"type": "module",
|
6 |
+
"bin": {
|
7 |
+
"lerobot": "./dist/cli/index.js"
|
8 |
+
},
|
9 |
+
"main": "./dist/lerobot/find_port.js",
|
10 |
+
"files": [
|
11 |
+
"dist/**/*",
|
12 |
+
"README.md"
|
13 |
+
],
|
14 |
+
"keywords": [
|
15 |
+
"robotics",
|
16 |
+
"ai",
|
17 |
+
"machine-learning",
|
18 |
+
"typescript",
|
19 |
+
"lerobot"
|
20 |
+
],
|
21 |
+
"scripts": {
|
22 |
+
"dev": "vite",
|
23 |
+
"build": "pnpm run build:cli",
|
24 |
+
"build:cli": "tsc --project tsconfig.cli.json",
|
25 |
+
"build:web": "tsc && vite build",
|
26 |
+
"preview": "vite preview",
|
27 |
+
"cli:find-port": "tsx src/cli/index.ts find-port",
|
28 |
+
"prepublishOnly": "pnpm run build"
|
29 |
+
},
|
30 |
+
"dependencies": {
|
31 |
+
"serialport": "^12.0.0"
|
32 |
+
},
|
33 |
+
"devDependencies": {
|
34 |
+
"typescript": "~5.8.3",
|
35 |
+
"vite": "^6.3.5",
|
36 |
+
"tsx": "^4.19.2",
|
37 |
+
"@types/node": "^22.10.5"
|
38 |
+
},
|
39 |
+
"repository": {
|
40 |
+
"type": "git",
|
41 |
+
"url": "https://github.com/timpietrusky/lerobot.js"
|
42 |
+
},
|
43 |
+
"license": "Apache-2.0",
|
44 |
+
"author": "Tim Pietrusky",
|
45 |
+
"engines": {
|
46 |
+
"node": ">=18.0.0"
|
47 |
+
}
|
48 |
+
}
|
pnpm-lock.yaml
ADDED
@@ -0,0 +1,813 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
lockfileVersion: '9.0'
|
2 |
+
|
3 |
+
settings:
|
4 |
+
autoInstallPeers: true
|
5 |
+
excludeLinksFromLockfile: false
|
6 |
+
|
7 |
+
importers:
|
8 |
+
|
9 |
+
.:
|
10 |
+
dependencies:
|
11 |
+
serialport:
|
12 |
+
specifier: ^12.0.0
|
13 |
+
version: 12.0.0
|
14 |
+
devDependencies:
|
15 |
+
'@types/node':
|
16 |
+
specifier: ^22.10.5
|
17 |
+
version: 22.15.31
|
18 |
+
tsx:
|
19 |
+
specifier: ^4.19.2
|
20 |
+
version: 4.20.3
|
21 |
+
typescript:
|
22 |
+
specifier: ~5.8.3
|
23 |
+
version: 5.8.3
|
24 |
+
vite:
|
25 |
+
specifier: ^6.3.5
|
26 |
+
version: 6.3.5(@types/[email protected])([email protected])
|
27 |
+
|
28 |
+
packages:
|
29 |
+
|
30 |
+
'@esbuild/[email protected]':
|
31 |
+
resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==}
|
32 |
+
engines: {node: '>=18'}
|
33 |
+
cpu: [ppc64]
|
34 |
+
os: [aix]
|
35 |
+
|
36 |
+
'@esbuild/[email protected]':
|
37 |
+
resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==}
|
38 |
+
engines: {node: '>=18'}
|
39 |
+
cpu: [arm64]
|
40 |
+
os: [android]
|
41 |
+
|
42 |
+
'@esbuild/[email protected]':
|
43 |
+
resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==}
|
44 |
+
engines: {node: '>=18'}
|
45 |
+
cpu: [arm]
|
46 |
+
os: [android]
|
47 |
+
|
48 |
+
'@esbuild/[email protected]':
|
49 |
+
resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==}
|
50 |
+
engines: {node: '>=18'}
|
51 |
+
cpu: [x64]
|
52 |
+
os: [android]
|
53 |
+
|
54 |
+
'@esbuild/[email protected]':
|
55 |
+
resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==}
|
56 |
+
engines: {node: '>=18'}
|
57 |
+
cpu: [arm64]
|
58 |
+
os: [darwin]
|
59 |
+
|
60 |
+
'@esbuild/[email protected]':
|
61 |
+
resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==}
|
62 |
+
engines: {node: '>=18'}
|
63 |
+
cpu: [x64]
|
64 |
+
os: [darwin]
|
65 |
+
|
66 |
+
'@esbuild/[email protected]':
|
67 |
+
resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==}
|
68 |
+
engines: {node: '>=18'}
|
69 |
+
cpu: [arm64]
|
70 |
+
os: [freebsd]
|
71 |
+
|
72 |
+
'@esbuild/[email protected]':
|
73 |
+
resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==}
|
74 |
+
engines: {node: '>=18'}
|
75 |
+
cpu: [x64]
|
76 |
+
os: [freebsd]
|
77 |
+
|
78 |
+
'@esbuild/[email protected]':
|
79 |
+
resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==}
|
80 |
+
engines: {node: '>=18'}
|
81 |
+
cpu: [arm64]
|
82 |
+
os: [linux]
|
83 |
+
|
84 |
+
'@esbuild/[email protected]':
|
85 |
+
resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==}
|
86 |
+
engines: {node: '>=18'}
|
87 |
+
cpu: [arm]
|
88 |
+
os: [linux]
|
89 |
+
|
90 |
+
'@esbuild/[email protected]':
|
91 |
+
resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==}
|
92 |
+
engines: {node: '>=18'}
|
93 |
+
cpu: [ia32]
|
94 |
+
os: [linux]
|
95 |
+
|
96 |
+
'@esbuild/[email protected]':
|
97 |
+
resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==}
|
98 |
+
engines: {node: '>=18'}
|
99 |
+
cpu: [loong64]
|
100 |
+
os: [linux]
|
101 |
+
|
102 |
+
'@esbuild/[email protected]':
|
103 |
+
resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==}
|
104 |
+
engines: {node: '>=18'}
|
105 |
+
cpu: [mips64el]
|
106 |
+
os: [linux]
|
107 |
+
|
108 |
+
'@esbuild/[email protected]':
|
109 |
+
resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==}
|
110 |
+
engines: {node: '>=18'}
|
111 |
+
cpu: [ppc64]
|
112 |
+
os: [linux]
|
113 |
+
|
114 |
+
'@esbuild/[email protected]':
|
115 |
+
resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==}
|
116 |
+
engines: {node: '>=18'}
|
117 |
+
cpu: [riscv64]
|
118 |
+
os: [linux]
|
119 |
+
|
120 |
+
'@esbuild/[email protected]':
|
121 |
+
resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==}
|
122 |
+
engines: {node: '>=18'}
|
123 |
+
cpu: [s390x]
|
124 |
+
os: [linux]
|
125 |
+
|
126 |
+
'@esbuild/[email protected]':
|
127 |
+
resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==}
|
128 |
+
engines: {node: '>=18'}
|
129 |
+
cpu: [x64]
|
130 |
+
os: [linux]
|
131 |
+
|
132 |
+
'@esbuild/[email protected]':
|
133 |
+
resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==}
|
134 |
+
engines: {node: '>=18'}
|
135 |
+
cpu: [arm64]
|
136 |
+
os: [netbsd]
|
137 |
+
|
138 |
+
'@esbuild/[email protected]':
|
139 |
+
resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==}
|
140 |
+
engines: {node: '>=18'}
|
141 |
+
cpu: [x64]
|
142 |
+
os: [netbsd]
|
143 |
+
|
144 |
+
'@esbuild/[email protected]':
|
145 |
+
resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==}
|
146 |
+
engines: {node: '>=18'}
|
147 |
+
cpu: [arm64]
|
148 |
+
os: [openbsd]
|
149 |
+
|
150 |
+
'@esbuild/[email protected]':
|
151 |
+
resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==}
|
152 |
+
engines: {node: '>=18'}
|
153 |
+
cpu: [x64]
|
154 |
+
os: [openbsd]
|
155 |
+
|
156 |
+
'@esbuild/[email protected]':
|
157 |
+
resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==}
|
158 |
+
engines: {node: '>=18'}
|
159 |
+
cpu: [x64]
|
160 |
+
os: [sunos]
|
161 |
+
|
162 |
+
'@esbuild/[email protected]':
|
163 |
+
resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==}
|
164 |
+
engines: {node: '>=18'}
|
165 |
+
cpu: [arm64]
|
166 |
+
os: [win32]
|
167 |
+
|
168 |
+
'@esbuild/[email protected]':
|
169 |
+
resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==}
|
170 |
+
engines: {node: '>=18'}
|
171 |
+
cpu: [ia32]
|
172 |
+
os: [win32]
|
173 |
+
|
174 |
+
'@esbuild/[email protected]':
|
175 |
+
resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==}
|
176 |
+
engines: {node: '>=18'}
|
177 |
+
cpu: [x64]
|
178 |
+
os: [win32]
|
179 |
+
|
180 |
+
'@rollup/[email protected]':
|
181 |
+
resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==}
|
182 |
+
cpu: [arm]
|
183 |
+
os: [android]
|
184 |
+
|
185 |
+
'@rollup/[email protected]':
|
186 |
+
resolution: {integrity: sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==}
|
187 |
+
cpu: [arm64]
|
188 |
+
os: [android]
|
189 |
+
|
190 |
+
'@rollup/[email protected]':
|
191 |
+
resolution: {integrity: sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==}
|
192 |
+
cpu: [arm64]
|
193 |
+
os: [darwin]
|
194 |
+
|
195 |
+
'@rollup/[email protected]':
|
196 |
+
resolution: {integrity: sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==}
|
197 |
+
cpu: [x64]
|
198 |
+
os: [darwin]
|
199 |
+
|
200 |
+
'@rollup/[email protected]':
|
201 |
+
resolution: {integrity: sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==}
|
202 |
+
cpu: [arm64]
|
203 |
+
os: [freebsd]
|
204 |
+
|
205 |
+
'@rollup/[email protected]':
|
206 |
+
resolution: {integrity: sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==}
|
207 |
+
cpu: [x64]
|
208 |
+
os: [freebsd]
|
209 |
+
|
210 |
+
'@rollup/[email protected]':
|
211 |
+
resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==}
|
212 |
+
cpu: [arm]
|
213 |
+
os: [linux]
|
214 |
+
|
215 |
+
'@rollup/[email protected]':
|
216 |
+
resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==}
|
217 |
+
cpu: [arm]
|
218 |
+
os: [linux]
|
219 |
+
|
220 |
+
'@rollup/[email protected]':
|
221 |
+
resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==}
|
222 |
+
cpu: [arm64]
|
223 |
+
os: [linux]
|
224 |
+
|
225 |
+
'@rollup/[email protected]':
|
226 |
+
resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==}
|
227 |
+
cpu: [arm64]
|
228 |
+
os: [linux]
|
229 |
+
|
230 |
+
'@rollup/[email protected]':
|
231 |
+
resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==}
|
232 |
+
cpu: [loong64]
|
233 |
+
os: [linux]
|
234 |
+
|
235 |
+
'@rollup/[email protected]':
|
236 |
+
resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==}
|
237 |
+
cpu: [ppc64]
|
238 |
+
os: [linux]
|
239 |
+
|
240 |
+
'@rollup/[email protected]':
|
241 |
+
resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==}
|
242 |
+
cpu: [riscv64]
|
243 |
+
os: [linux]
|
244 |
+
|
245 |
+
'@rollup/[email protected]':
|
246 |
+
resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==}
|
247 |
+
cpu: [riscv64]
|
248 |
+
os: [linux]
|
249 |
+
|
250 |
+
'@rollup/[email protected]':
|
251 |
+
resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==}
|
252 |
+
cpu: [s390x]
|
253 |
+
os: [linux]
|
254 |
+
|
255 |
+
'@rollup/[email protected]':
|
256 |
+
resolution: {integrity: sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==}
|
257 |
+
cpu: [x64]
|
258 |
+
os: [linux]
|
259 |
+
|
260 |
+
'@rollup/[email protected]':
|
261 |
+
resolution: {integrity: sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==}
|
262 |
+
cpu: [x64]
|
263 |
+
os: [linux]
|
264 |
+
|
265 |
+
'@rollup/[email protected]':
|
266 |
+
resolution: {integrity: sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==}
|
267 |
+
cpu: [arm64]
|
268 |
+
os: [win32]
|
269 |
+
|
270 |
+
'@rollup/[email protected]':
|
271 |
+
resolution: {integrity: sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==}
|
272 |
+
cpu: [ia32]
|
273 |
+
os: [win32]
|
274 |
+
|
275 |
+
'@rollup/[email protected]':
|
276 |
+
resolution: {integrity: sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==}
|
277 |
+
cpu: [x64]
|
278 |
+
os: [win32]
|
279 |
+
|
280 |
+
'@serialport/[email protected]':
|
281 |
+
resolution: {integrity: sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==}
|
282 |
+
engines: {node: '>=12.0.0'}
|
283 |
+
|
284 |
+
'@serialport/[email protected]':
|
285 |
+
resolution: {integrity: sha512-r2XOwY2dDvbW7dKqSPIk2gzsr6M6Qpe9+/Ngs94fNaNlcTRCV02PfaoDmRgcubpNVVcLATlxSxPTIDw12dbKOg==}
|
286 |
+
engines: {node: '>=16.0.0'}
|
287 |
+
|
288 |
+
'@serialport/[email protected]':
|
289 |
+
resolution: {integrity: sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==}
|
290 |
+
engines: {node: ^12.22 || ^14.13 || >=16}
|
291 |
+
|
292 |
+
'@serialport/[email protected]':
|
293 |
+
resolution: {integrity: sha512-0ei0txFAj+s6FTiCJFBJ1T2hpKkX8Md0Pu6dqMrYoirjPskDLJRgZGLqoy3/lnU1bkvHpnJO+9oJ3PB9v8rNlg==}
|
294 |
+
engines: {node: '>=12.0.0'}
|
295 |
+
|
296 |
+
'@serialport/[email protected]':
|
297 |
+
resolution: {integrity: sha512-0PfLzO9t2X5ufKuBO34DQKLXrCCqS9xz2D0pfuaLNeTkyGUBv426zxoMf3rsMRodDOZNbFblu3Ae84MOQXjnZw==}
|
298 |
+
engines: {node: '>=12.0.0'}
|
299 |
+
|
300 |
+
'@serialport/[email protected]':
|
301 |
+
resolution: {integrity: sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==}
|
302 |
+
engines: {node: '>=12.0.0'}
|
303 |
+
|
304 |
+
'@serialport/[email protected]':
|
305 |
+
resolution: {integrity: sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==}
|
306 |
+
engines: {node: '>=12.0.0'}
|
307 |
+
|
308 |
+
'@serialport/[email protected]':
|
309 |
+
resolution: {integrity: sha512-GnCh8K0NAESfhCuXAt+FfBRz1Cf9CzIgXfp7SdMgXwrtuUnCC/yuRTUFWRvuzhYKoAo1TL0hhUo77SFHUH1T/w==}
|
310 |
+
engines: {node: '>=12.0.0'}
|
311 |
+
|
312 |
+
'@serialport/[email protected]':
|
313 |
+
resolution: {integrity: sha512-p1hiCRqvGHHLCN/8ZiPUY/G0zrxd7gtZs251n+cfNTn+87rwcdUeu9Dps3Aadx30/sOGGFL6brIRGK4l/t7MuQ==}
|
314 |
+
engines: {node: '>=8.6.0'}
|
315 |
+
|
316 |
+
'@serialport/[email protected]':
|
317 |
+
resolution: {integrity: sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==}
|
318 |
+
engines: {node: '>=12.0.0'}
|
319 |
+
|
320 |
+
'@serialport/[email protected]':
|
321 |
+
resolution: {integrity: sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==}
|
322 |
+
engines: {node: '>=12.0.0'}
|
323 |
+
|
324 |
+
'@serialport/[email protected]':
|
325 |
+
resolution: {integrity: sha512-ygDwj3O4SDpZlbrRUraoXIoIqb8sM7aMKryGjYTIF0JRnKeB1ys8+wIp0RFMdFbO62YriUDextHB5Um5cKFSWg==}
|
326 |
+
engines: {node: '>=12.0.0'}
|
327 |
+
|
328 |
+
'@serialport/[email protected]':
|
329 |
+
resolution: {integrity: sha512-dCAVh4P/pZrLcPv9NJ2mvPRBg64L5jXuiRxIlyxxdZGH4WubwXVXY/kBTihQmiAMPxbT3yshSX8f2+feqWsxqA==}
|
330 |
+
engines: {node: '>=12.0.0'}
|
331 |
+
|
332 |
+
'@serialport/[email protected]':
|
333 |
+
resolution: {integrity: sha512-0APxDGR9YvJXTRfY+uRGhzOhTpU5akSH183RUcwzN7QXh8/1jwFsFLCu0grmAUfi+fItCkR+Xr1TcNJLR13VNA==}
|
334 |
+
engines: {node: '>=12.0.0'}
|
335 |
+
|
336 |
+
'@serialport/[email protected]':
|
337 |
+
resolution: {integrity: sha512-dozONxhPC/78pntuxpz/NOtVps8qIc/UZzdc/LuPvVsqCoJXiRxOg6ZtCP/W58iibJDKPZPAWPGYeZt9DJxI+Q==}
|
338 |
+
engines: {node: '>=12.0.0'}
|
339 |
+
|
340 |
+
'@serialport/[email protected]':
|
341 |
+
resolution: {integrity: sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==}
|
342 |
+
engines: {node: '>=12.0.0'}
|
343 |
+
|
344 |
+
'@types/[email protected]':
|
345 |
+
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
346 |
+
|
347 |
+
'@types/[email protected]':
|
348 |
+
resolution: {integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==}
|
349 |
+
|
350 | |
351 |
+
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
352 |
+
engines: {node: '>=6.0'}
|
353 |
+
peerDependencies:
|
354 |
+
supports-color: '*'
|
355 |
+
peerDependenciesMeta:
|
356 |
+
supports-color:
|
357 |
+
optional: true
|
358 |
+
|
359 | |
360 |
+
resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==}
|
361 |
+
engines: {node: '>=18'}
|
362 |
+
hasBin: true
|
363 |
+
|
364 | |
365 |
+
resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
|
366 |
+
peerDependencies:
|
367 |
+
picomatch: ^3 || ^4
|
368 |
+
peerDependenciesMeta:
|
369 |
+
picomatch:
|
370 |
+
optional: true
|
371 |
+
|
372 | |
373 |
+
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
374 |
+
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
375 |
+
os: [darwin]
|
376 |
+
|
377 | |
378 |
+
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
|
379 |
+
|
380 | |
381 |
+
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
382 |
+
|
383 | |
384 |
+
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
385 |
+
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
386 |
+
hasBin: true
|
387 |
+
|
388 | |
389 |
+
resolution: {integrity: sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==}
|
390 |
+
|
391 | |
392 |
+
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
|
393 |
+
hasBin: true
|
394 |
+
|
395 | |
396 |
+
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
397 |
+
|
398 | |
399 |
+
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
400 |
+
engines: {node: '>=12'}
|
401 |
+
|
402 | |
403 |
+
resolution: {integrity: sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==}
|
404 |
+
engines: {node: ^10 || ^12 || >=14}
|
405 |
+
|
406 | |
407 |
+
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
408 |
+
|
409 | |
410 |
+
resolution: {integrity: sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==}
|
411 |
+
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
412 |
+
hasBin: true
|
413 |
+
|
414 | |
415 |
+
resolution: {integrity: sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==}
|
416 |
+
engines: {node: '>=16.0.0'}
|
417 |
+
|
418 | |
419 |
+
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
420 |
+
engines: {node: '>=0.10.0'}
|
421 |
+
|
422 | |
423 |
+
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
|
424 |
+
engines: {node: '>=12.0.0'}
|
425 |
+
|
426 | |
427 |
+
resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==}
|
428 |
+
engines: {node: '>=18.0.0'}
|
429 |
+
hasBin: true
|
430 |
+
|
431 | |
432 |
+
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
|
433 |
+
engines: {node: '>=14.17'}
|
434 |
+
hasBin: true
|
435 |
+
|
436 | |
437 |
+
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
438 |
+
|
439 | |
440 |
+
resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
|
441 |
+
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
442 |
+
hasBin: true
|
443 |
+
peerDependencies:
|
444 |
+
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
445 |
+
jiti: '>=1.21.0'
|
446 |
+
less: '*'
|
447 |
+
lightningcss: ^1.21.0
|
448 |
+
sass: '*'
|
449 |
+
sass-embedded: '*'
|
450 |
+
stylus: '*'
|
451 |
+
sugarss: '*'
|
452 |
+
terser: ^5.16.0
|
453 |
+
tsx: ^4.8.1
|
454 |
+
yaml: ^2.4.2
|
455 |
+
peerDependenciesMeta:
|
456 |
+
'@types/node':
|
457 |
+
optional: true
|
458 |
+
jiti:
|
459 |
+
optional: true
|
460 |
+
less:
|
461 |
+
optional: true
|
462 |
+
lightningcss:
|
463 |
+
optional: true
|
464 |
+
sass:
|
465 |
+
optional: true
|
466 |
+
sass-embedded:
|
467 |
+
optional: true
|
468 |
+
stylus:
|
469 |
+
optional: true
|
470 |
+
sugarss:
|
471 |
+
optional: true
|
472 |
+
terser:
|
473 |
+
optional: true
|
474 |
+
tsx:
|
475 |
+
optional: true
|
476 |
+
yaml:
|
477 |
+
optional: true
|
478 |
+
|
479 |
+
snapshots:
|
480 |
+
|
481 |
+
'@esbuild/[email protected]':
|
482 |
+
optional: true
|
483 |
+
|
484 |
+
'@esbuild/[email protected]':
|
485 |
+
optional: true
|
486 |
+
|
487 |
+
'@esbuild/[email protected]':
|
488 |
+
optional: true
|
489 |
+
|
490 |
+
'@esbuild/[email protected]':
|
491 |
+
optional: true
|
492 |
+
|
493 |
+
'@esbuild/[email protected]':
|
494 |
+
optional: true
|
495 |
+
|
496 |
+
'@esbuild/[email protected]':
|
497 |
+
optional: true
|
498 |
+
|
499 |
+
'@esbuild/[email protected]':
|
500 |
+
optional: true
|
501 |
+
|
502 |
+
'@esbuild/[email protected]':
|
503 |
+
optional: true
|
504 |
+
|
505 |
+
'@esbuild/[email protected]':
|
506 |
+
optional: true
|
507 |
+
|
508 |
+
'@esbuild/[email protected]':
|
509 |
+
optional: true
|
510 |
+
|
511 |
+
'@esbuild/[email protected]':
|
512 |
+
optional: true
|
513 |
+
|
514 |
+
'@esbuild/[email protected]':
|
515 |
+
optional: true
|
516 |
+
|
517 |
+
'@esbuild/[email protected]':
|
518 |
+
optional: true
|
519 |
+
|
520 |
+
'@esbuild/[email protected]':
|
521 |
+
optional: true
|
522 |
+
|
523 |
+
'@esbuild/[email protected]':
|
524 |
+
optional: true
|
525 |
+
|
526 |
+
'@esbuild/[email protected]':
|
527 |
+
optional: true
|
528 |
+
|
529 |
+
'@esbuild/[email protected]':
|
530 |
+
optional: true
|
531 |
+
|
532 |
+
'@esbuild/[email protected]':
|
533 |
+
optional: true
|
534 |
+
|
535 |
+
'@esbuild/[email protected]':
|
536 |
+
optional: true
|
537 |
+
|
538 |
+
'@esbuild/[email protected]':
|
539 |
+
optional: true
|
540 |
+
|
541 |
+
'@esbuild/[email protected]':
|
542 |
+
optional: true
|
543 |
+
|
544 |
+
'@esbuild/[email protected]':
|
545 |
+
optional: true
|
546 |
+
|
547 |
+
'@esbuild/[email protected]':
|
548 |
+
optional: true
|
549 |
+
|
550 |
+
'@esbuild/[email protected]':
|
551 |
+
optional: true
|
552 |
+
|
553 |
+
'@esbuild/[email protected]':
|
554 |
+
optional: true
|
555 |
+
|
556 |
+
'@rollup/[email protected]':
|
557 |
+
optional: true
|
558 |
+
|
559 |
+
'@rollup/[email protected]':
|
560 |
+
optional: true
|
561 |
+
|
562 |
+
'@rollup/[email protected]':
|
563 |
+
optional: true
|
564 |
+
|
565 |
+
'@rollup/[email protected]':
|
566 |
+
optional: true
|
567 |
+
|
568 |
+
'@rollup/[email protected]':
|
569 |
+
optional: true
|
570 |
+
|
571 |
+
'@rollup/[email protected]':
|
572 |
+
optional: true
|
573 |
+
|
574 |
+
'@rollup/[email protected]':
|
575 |
+
optional: true
|
576 |
+
|
577 |
+
'@rollup/[email protected]':
|
578 |
+
optional: true
|
579 |
+
|
580 |
+
'@rollup/[email protected]':
|
581 |
+
optional: true
|
582 |
+
|
583 |
+
'@rollup/[email protected]':
|
584 |
+
optional: true
|
585 |
+
|
586 |
+
'@rollup/[email protected]':
|
587 |
+
optional: true
|
588 |
+
|
589 |
+
'@rollup/[email protected]':
|
590 |
+
optional: true
|
591 |
+
|
592 |
+
'@rollup/[email protected]':
|
593 |
+
optional: true
|
594 |
+
|
595 |
+
'@rollup/[email protected]':
|
596 |
+
optional: true
|
597 |
+
|
598 |
+
'@rollup/[email protected]':
|
599 |
+
optional: true
|
600 |
+
|
601 |
+
'@rollup/[email protected]':
|
602 |
+
optional: true
|
603 |
+
|
604 |
+
'@rollup/[email protected]':
|
605 |
+
optional: true
|
606 |
+
|
607 |
+
'@rollup/[email protected]':
|
608 |
+
optional: true
|
609 |
+
|
610 |
+
'@rollup/[email protected]':
|
611 |
+
optional: true
|
612 |
+
|
613 |
+
'@rollup/[email protected]':
|
614 |
+
optional: true
|
615 |
+
|
616 |
+
'@serialport/[email protected]':
|
617 |
+
dependencies:
|
618 |
+
'@serialport/bindings-interface': 1.2.2
|
619 |
+
debug: 4.3.4
|
620 |
+
transitivePeerDependencies:
|
621 |
+
- supports-color
|
622 |
+
|
623 |
+
'@serialport/[email protected]':
|
624 |
+
dependencies:
|
625 |
+
'@serialport/bindings-interface': 1.2.2
|
626 |
+
'@serialport/parser-readline': 11.0.0
|
627 |
+
debug: 4.3.4
|
628 |
+
node-addon-api: 7.0.0
|
629 |
+
node-gyp-build: 4.6.0
|
630 |
+
transitivePeerDependencies:
|
631 |
+
- supports-color
|
632 |
+
|
633 |
+
'@serialport/[email protected]': {}
|
634 |
+
|
635 |
+
'@serialport/[email protected]': {}
|
636 |
+
|
637 |
+
'@serialport/[email protected]': {}
|
638 |
+
|
639 |
+
'@serialport/[email protected]': {}
|
640 |
+
|
641 |
+
'@serialport/[email protected]': {}
|
642 |
+
|
643 |
+
'@serialport/[email protected]': {}
|
644 |
+
|
645 |
+
'@serialport/[email protected]': {}
|
646 |
+
|
647 |
+
'@serialport/[email protected]':
|
648 |
+
dependencies:
|
649 |
+
'@serialport/parser-delimiter': 11.0.0
|
650 |
+
|
651 |
+
'@serialport/[email protected]':
|
652 |
+
dependencies:
|
653 |
+
'@serialport/parser-delimiter': 12.0.0
|
654 |
+
|
655 |
+
'@serialport/[email protected]': {}
|
656 |
+
|
657 |
+
'@serialport/[email protected]': {}
|
658 |
+
|
659 |
+
'@serialport/[email protected]': {}
|
660 |
+
|
661 |
+
'@serialport/[email protected]': {}
|
662 |
+
|
663 |
+
'@serialport/[email protected]':
|
664 |
+
dependencies:
|
665 |
+
'@serialport/bindings-interface': 1.2.2
|
666 |
+
debug: 4.3.4
|
667 |
+
transitivePeerDependencies:
|
668 |
+
- supports-color
|
669 |
+
|
670 |
+
'@types/[email protected]': {}
|
671 |
+
|
672 |
+
'@types/[email protected]':
|
673 |
+
dependencies:
|
674 |
+
undici-types: 6.21.0
|
675 |
+
|
676 | |
677 |
+
dependencies:
|
678 |
+
ms: 2.1.2
|
679 |
+
|
680 | |
681 |
+
optionalDependencies:
|
682 |
+
'@esbuild/aix-ppc64': 0.25.5
|
683 |
+
'@esbuild/android-arm': 0.25.5
|
684 |
+
'@esbuild/android-arm64': 0.25.5
|
685 |
+
'@esbuild/android-x64': 0.25.5
|
686 |
+
'@esbuild/darwin-arm64': 0.25.5
|
687 |
+
'@esbuild/darwin-x64': 0.25.5
|
688 |
+
'@esbuild/freebsd-arm64': 0.25.5
|
689 |
+
'@esbuild/freebsd-x64': 0.25.5
|
690 |
+
'@esbuild/linux-arm': 0.25.5
|
691 |
+
'@esbuild/linux-arm64': 0.25.5
|
692 |
+
'@esbuild/linux-ia32': 0.25.5
|
693 |
+
'@esbuild/linux-loong64': 0.25.5
|
694 |
+
'@esbuild/linux-mips64el': 0.25.5
|
695 |
+
'@esbuild/linux-ppc64': 0.25.5
|
696 |
+
'@esbuild/linux-riscv64': 0.25.5
|
697 |
+
'@esbuild/linux-s390x': 0.25.5
|
698 |
+
'@esbuild/linux-x64': 0.25.5
|
699 |
+
'@esbuild/netbsd-arm64': 0.25.5
|
700 |
+
'@esbuild/netbsd-x64': 0.25.5
|
701 |
+
'@esbuild/openbsd-arm64': 0.25.5
|
702 |
+
'@esbuild/openbsd-x64': 0.25.5
|
703 |
+
'@esbuild/sunos-x64': 0.25.5
|
704 |
+
'@esbuild/win32-arm64': 0.25.5
|
705 |
+
'@esbuild/win32-ia32': 0.25.5
|
706 |
+
'@esbuild/win32-x64': 0.25.5
|
707 |
+
|
708 | |
709 |
+
optionalDependencies:
|
710 |
+
picomatch: 4.0.2
|
711 |
+
|
712 | |
713 |
+
optional: true
|
714 |
+
|
715 | |
716 |
+
dependencies:
|
717 |
+
resolve-pkg-maps: 1.0.0
|
718 |
+
|
719 |
+
[email protected]: {}
|
720 |
+
|
721 |
+
[email protected]: {}
|
722 |
+
|
723 |
+
[email protected]: {}
|
724 |
+
|
725 |
+
[email protected]: {}
|
726 |
+
|
727 |
+
[email protected]: {}
|
728 |
+
|
729 |
+
[email protected]: {}
|
730 |
+
|
731 | |
732 |
+
dependencies:
|
733 |
+
nanoid: 3.3.11
|
734 |
+
picocolors: 1.1.1
|
735 |
+
source-map-js: 1.2.1
|
736 |
+
|
737 |
+
[email protected]: {}
|
738 |
+
|
739 | |
740 |
+
dependencies:
|
741 |
+
'@types/estree': 1.0.7
|
742 |
+
optionalDependencies:
|
743 |
+
'@rollup/rollup-android-arm-eabi': 4.43.0
|
744 |
+
'@rollup/rollup-android-arm64': 4.43.0
|
745 |
+
'@rollup/rollup-darwin-arm64': 4.43.0
|
746 |
+
'@rollup/rollup-darwin-x64': 4.43.0
|
747 |
+
'@rollup/rollup-freebsd-arm64': 4.43.0
|
748 |
+
'@rollup/rollup-freebsd-x64': 4.43.0
|
749 |
+
'@rollup/rollup-linux-arm-gnueabihf': 4.43.0
|
750 |
+
'@rollup/rollup-linux-arm-musleabihf': 4.43.0
|
751 |
+
'@rollup/rollup-linux-arm64-gnu': 4.43.0
|
752 |
+
'@rollup/rollup-linux-arm64-musl': 4.43.0
|
753 |
+
'@rollup/rollup-linux-loongarch64-gnu': 4.43.0
|
754 |
+
'@rollup/rollup-linux-powerpc64le-gnu': 4.43.0
|
755 |
+
'@rollup/rollup-linux-riscv64-gnu': 4.43.0
|
756 |
+
'@rollup/rollup-linux-riscv64-musl': 4.43.0
|
757 |
+
'@rollup/rollup-linux-s390x-gnu': 4.43.0
|
758 |
+
'@rollup/rollup-linux-x64-gnu': 4.43.0
|
759 |
+
'@rollup/rollup-linux-x64-musl': 4.43.0
|
760 |
+
'@rollup/rollup-win32-arm64-msvc': 4.43.0
|
761 |
+
'@rollup/rollup-win32-ia32-msvc': 4.43.0
|
762 |
+
'@rollup/rollup-win32-x64-msvc': 4.43.0
|
763 |
+
fsevents: 2.3.3
|
764 |
+
|
765 | |
766 |
+
dependencies:
|
767 |
+
'@serialport/binding-mock': 10.2.2
|
768 |
+
'@serialport/bindings-cpp': 12.0.1
|
769 |
+
'@serialport/parser-byte-length': 12.0.0
|
770 |
+
'@serialport/parser-cctalk': 12.0.0
|
771 |
+
'@serialport/parser-delimiter': 12.0.0
|
772 |
+
'@serialport/parser-inter-byte-timeout': 12.0.0
|
773 |
+
'@serialport/parser-packet-length': 12.0.0
|
774 |
+
'@serialport/parser-readline': 12.0.0
|
775 |
+
'@serialport/parser-ready': 12.0.0
|
776 |
+
'@serialport/parser-regex': 12.0.0
|
777 |
+
'@serialport/parser-slip-encoder': 12.0.0
|
778 |
+
'@serialport/parser-spacepacket': 12.0.0
|
779 |
+
'@serialport/stream': 12.0.0
|
780 |
+
debug: 4.3.4
|
781 |
+
transitivePeerDependencies:
|
782 |
+
- supports-color
|
783 |
+
|
784 |
+
[email protected]: {}
|
785 |
+
|
786 | |
787 |
+
dependencies:
|
788 |
+
fdir: 6.4.6([email protected])
|
789 |
+
picomatch: 4.0.2
|
790 |
+
|
791 | |
792 |
+
dependencies:
|
793 |
+
esbuild: 0.25.5
|
794 |
+
get-tsconfig: 4.10.1
|
795 |
+
optionalDependencies:
|
796 |
+
fsevents: 2.3.3
|
797 |
+
|
798 |
+
[email protected]: {}
|
799 |
+
|
800 |
+
[email protected]: {}
|
801 |
+
|
802 |
+
[email protected](@types/[email protected])([email protected]):
|
803 |
+
dependencies:
|
804 |
+
esbuild: 0.25.5
|
805 |
+
fdir: 6.4.6([email protected])
|
806 |
+
picomatch: 4.0.2
|
807 |
+
postcss: 8.5.5
|
808 |
+
rollup: 4.43.0
|
809 |
+
tinyglobby: 0.2.14
|
810 |
+
optionalDependencies:
|
811 |
+
'@types/node': 22.15.31
|
812 |
+
fsevents: 2.3.3
|
813 |
+
tsx: 4.20.3
|
public/vite.svg
ADDED
|
src/cli/index.ts
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env node
|
2 |
+
|
3 |
+
/**
|
4 |
+
* lerobot.js CLI
|
5 |
+
*
|
6 |
+
* Provides command-line interface for lerobot functionality
|
7 |
+
* Maintains compatibility with Python lerobot command structure
|
8 |
+
*/
|
9 |
+
|
10 |
+
import { findPort } from "../lerobot/find_port.js";
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Show usage information
|
14 |
+
*/
|
15 |
+
function showUsage() {
|
16 |
+
console.log("Usage: lerobot <command>");
|
17 |
+
console.log("");
|
18 |
+
console.log("Commands:");
|
19 |
+
console.log(
|
20 |
+
" find-port Find the USB port associated with your MotorsBus"
|
21 |
+
);
|
22 |
+
console.log("");
|
23 |
+
console.log("Examples:");
|
24 |
+
console.log(" lerobot find-port");
|
25 |
+
console.log("");
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Main CLI function
|
30 |
+
*/
|
31 |
+
async function main() {
|
32 |
+
const args = process.argv.slice(2);
|
33 |
+
|
34 |
+
if (args.length === 0) {
|
35 |
+
showUsage();
|
36 |
+
process.exit(1);
|
37 |
+
}
|
38 |
+
|
39 |
+
const command = args[0];
|
40 |
+
|
41 |
+
try {
|
42 |
+
switch (command) {
|
43 |
+
case "find-port":
|
44 |
+
await findPort();
|
45 |
+
break;
|
46 |
+
|
47 |
+
case "help":
|
48 |
+
case "--help":
|
49 |
+
case "-h":
|
50 |
+
showUsage();
|
51 |
+
break;
|
52 |
+
|
53 |
+
default:
|
54 |
+
console.error(`Unknown command: ${command}`);
|
55 |
+
showUsage();
|
56 |
+
process.exit(1);
|
57 |
+
}
|
58 |
+
} catch (error) {
|
59 |
+
console.error("Error:", error instanceof Error ? error.message : error);
|
60 |
+
process.exit(1);
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
// Run the CLI
|
65 |
+
main();
|
src/counter.ts
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function setupCounter(element: HTMLButtonElement) {
|
2 |
+
let counter = 0
|
3 |
+
const setCounter = (count: number) => {
|
4 |
+
counter = count
|
5 |
+
element.innerHTML = `count is ${counter}`
|
6 |
+
}
|
7 |
+
element.addEventListener('click', () => setCounter(counter + 1))
|
8 |
+
setCounter(0)
|
9 |
+
}
|
src/lerobot/find_port.ts
ADDED
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Helper to find the USB port associated with your MotorsBus.
|
3 |
+
*
|
4 |
+
* Direct port of Python lerobot find_port.py
|
5 |
+
*
|
6 |
+
* Example:
|
7 |
+
* ```
|
8 |
+
* npx lerobot find-port
|
9 |
+
* ```
|
10 |
+
*/
|
11 |
+
|
12 |
+
import { SerialPort } from "serialport";
|
13 |
+
import { createInterface } from "readline";
|
14 |
+
import { platform } from "os";
|
15 |
+
import { readdir } from "fs/promises";
|
16 |
+
import { join } from "path";
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Find all available serial ports on the system
|
20 |
+
* Mirrors Python's find_available_ports() function
|
21 |
+
*/
|
22 |
+
async function findAvailablePorts(): Promise<string[]> {
|
23 |
+
if (platform() === "win32") {
|
24 |
+
// List COM ports using serialport library (equivalent to pyserial)
|
25 |
+
const ports = await SerialPort.list();
|
26 |
+
return ports.map((port) => port.path);
|
27 |
+
} else {
|
28 |
+
// List /dev/tty* ports for Unix-based systems (Linux/macOS)
|
29 |
+
try {
|
30 |
+
const devFiles = await readdir("/dev");
|
31 |
+
const ttyPorts = devFiles
|
32 |
+
.filter((file) => file.startsWith("tty"))
|
33 |
+
.map((file) => join("/dev", file));
|
34 |
+
return ttyPorts;
|
35 |
+
} catch (error) {
|
36 |
+
// Fallback to serialport library if /dev reading fails
|
37 |
+
const ports = await SerialPort.list();
|
38 |
+
return ports.map((port) => port.path);
|
39 |
+
}
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Create readline interface for user input
|
45 |
+
* Equivalent to Python's input() function
|
46 |
+
*/
|
47 |
+
function createReadlineInterface() {
|
48 |
+
return createInterface({
|
49 |
+
input: process.stdin,
|
50 |
+
output: process.stdout,
|
51 |
+
});
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Prompt user for input and wait for response
|
56 |
+
* Equivalent to Python's input() function
|
57 |
+
*/
|
58 |
+
function waitForInput(prompt: string = ""): Promise<string> {
|
59 |
+
const rl = createReadlineInterface();
|
60 |
+
return new Promise((resolve) => {
|
61 |
+
if (prompt) {
|
62 |
+
process.stdout.write(prompt);
|
63 |
+
}
|
64 |
+
rl.on("line", (answer) => {
|
65 |
+
rl.close();
|
66 |
+
resolve(answer);
|
67 |
+
});
|
68 |
+
});
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Sleep for specified milliseconds
|
73 |
+
* Equivalent to Python's time.sleep()
|
74 |
+
*/
|
75 |
+
function sleep(ms: number): Promise<void> {
|
76 |
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Main find port function - direct port of Python find_port()
|
81 |
+
* Maintains identical UX and messaging
|
82 |
+
*/
|
83 |
+
export async function findPort(): Promise<void> {
|
84 |
+
console.log("Finding all available ports for the MotorsBus.");
|
85 |
+
|
86 |
+
const portsBefore = await findAvailablePorts();
|
87 |
+
console.log("Ports before disconnecting:", portsBefore);
|
88 |
+
|
89 |
+
console.log(
|
90 |
+
"Remove the USB cable from your MotorsBus and press Enter when done."
|
91 |
+
);
|
92 |
+
await waitForInput();
|
93 |
+
|
94 |
+
// Allow some time for port to be released (equivalent to Python's time.sleep(0.5))
|
95 |
+
await sleep(500);
|
96 |
+
|
97 |
+
const portsAfter = await findAvailablePorts();
|
98 |
+
const portsDiff = portsBefore.filter((port) => !portsAfter.includes(port));
|
99 |
+
|
100 |
+
if (portsDiff.length === 1) {
|
101 |
+
const port = portsDiff[0];
|
102 |
+
console.log(`The port of this MotorsBus is '${port}'`);
|
103 |
+
console.log("Reconnect the USB cable.");
|
104 |
+
} else if (portsDiff.length === 0) {
|
105 |
+
throw new Error(
|
106 |
+
`Could not detect the port. No difference was found (${JSON.stringify(
|
107 |
+
portsDiff
|
108 |
+
)}).`
|
109 |
+
);
|
110 |
+
} else {
|
111 |
+
throw new Error(
|
112 |
+
`Could not detect the port. More than one port was found (${JSON.stringify(
|
113 |
+
portsDiff
|
114 |
+
)}).`
|
115 |
+
);
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* CLI entry point when called directly
|
121 |
+
*/
|
122 |
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
123 |
+
findPort().catch((error) => {
|
124 |
+
console.error(error.message);
|
125 |
+
process.exit(1);
|
126 |
+
});
|
127 |
+
}
|
src/main.ts
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import './style.css'
|
2 |
+
import typescriptLogo from './typescript.svg'
|
3 |
+
import viteLogo from '/vite.svg'
|
4 |
+
import { setupCounter } from './counter.ts'
|
5 |
+
|
6 |
+
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
7 |
+
<div>
|
8 |
+
<a href="https://vite.dev" target="_blank">
|
9 |
+
<img src="${viteLogo}" class="logo" alt="Vite logo" />
|
10 |
+
</a>
|
11 |
+
<a href="https://www.typescriptlang.org/" target="_blank">
|
12 |
+
<img src="${typescriptLogo}" class="logo vanilla" alt="TypeScript logo" />
|
13 |
+
</a>
|
14 |
+
<h1>Vite + TypeScript</h1>
|
15 |
+
<div class="card">
|
16 |
+
<button id="counter" type="button"></button>
|
17 |
+
</div>
|
18 |
+
<p class="read-the-docs">
|
19 |
+
Click on the Vite and TypeScript logos to learn more
|
20 |
+
</p>
|
21 |
+
</div>
|
22 |
+
`
|
23 |
+
|
24 |
+
setupCounter(document.querySelector<HTMLButtonElement>('#counter')!)
|
src/style.css
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
:root {
|
2 |
+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
3 |
+
line-height: 1.5;
|
4 |
+
font-weight: 400;
|
5 |
+
|
6 |
+
color-scheme: light dark;
|
7 |
+
color: rgba(255, 255, 255, 0.87);
|
8 |
+
background-color: #242424;
|
9 |
+
|
10 |
+
font-synthesis: none;
|
11 |
+
text-rendering: optimizeLegibility;
|
12 |
+
-webkit-font-smoothing: antialiased;
|
13 |
+
-moz-osx-font-smoothing: grayscale;
|
14 |
+
}
|
15 |
+
|
16 |
+
a {
|
17 |
+
font-weight: 500;
|
18 |
+
color: #646cff;
|
19 |
+
text-decoration: inherit;
|
20 |
+
}
|
21 |
+
a:hover {
|
22 |
+
color: #535bf2;
|
23 |
+
}
|
24 |
+
|
25 |
+
body {
|
26 |
+
margin: 0;
|
27 |
+
display: flex;
|
28 |
+
place-items: center;
|
29 |
+
min-width: 320px;
|
30 |
+
min-height: 100vh;
|
31 |
+
}
|
32 |
+
|
33 |
+
h1 {
|
34 |
+
font-size: 3.2em;
|
35 |
+
line-height: 1.1;
|
36 |
+
}
|
37 |
+
|
38 |
+
#app {
|
39 |
+
max-width: 1280px;
|
40 |
+
margin: 0 auto;
|
41 |
+
padding: 2rem;
|
42 |
+
text-align: center;
|
43 |
+
}
|
44 |
+
|
45 |
+
.logo {
|
46 |
+
height: 6em;
|
47 |
+
padding: 1.5em;
|
48 |
+
will-change: filter;
|
49 |
+
transition: filter 300ms;
|
50 |
+
}
|
51 |
+
.logo:hover {
|
52 |
+
filter: drop-shadow(0 0 2em #646cffaa);
|
53 |
+
}
|
54 |
+
.logo.vanilla:hover {
|
55 |
+
filter: drop-shadow(0 0 2em #3178c6aa);
|
56 |
+
}
|
57 |
+
|
58 |
+
.card {
|
59 |
+
padding: 2em;
|
60 |
+
}
|
61 |
+
|
62 |
+
.read-the-docs {
|
63 |
+
color: #888;
|
64 |
+
}
|
65 |
+
|
66 |
+
button {
|
67 |
+
border-radius: 8px;
|
68 |
+
border: 1px solid transparent;
|
69 |
+
padding: 0.6em 1.2em;
|
70 |
+
font-size: 1em;
|
71 |
+
font-weight: 500;
|
72 |
+
font-family: inherit;
|
73 |
+
background-color: #1a1a1a;
|
74 |
+
cursor: pointer;
|
75 |
+
transition: border-color 0.25s;
|
76 |
+
}
|
77 |
+
button:hover {
|
78 |
+
border-color: #646cff;
|
79 |
+
}
|
80 |
+
button:focus,
|
81 |
+
button:focus-visible {
|
82 |
+
outline: 4px auto -webkit-focus-ring-color;
|
83 |
+
}
|
84 |
+
|
85 |
+
@media (prefers-color-scheme: light) {
|
86 |
+
:root {
|
87 |
+
color: #213547;
|
88 |
+
background-color: #ffffff;
|
89 |
+
}
|
90 |
+
a:hover {
|
91 |
+
color: #747bff;
|
92 |
+
}
|
93 |
+
button {
|
94 |
+
background-color: #f9f9f9;
|
95 |
+
}
|
96 |
+
}
|
src/typescript.svg
ADDED
|
src/vite-env.d.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
/// <reference types="vite/client" />
|
tsconfig.cli.json
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"target": "ES2020",
|
4 |
+
"module": "NodeNext",
|
5 |
+
"moduleResolution": "NodeNext",
|
6 |
+
"outDir": "./dist",
|
7 |
+
"rootDir": "./src",
|
8 |
+
"declaration": true,
|
9 |
+
"strict": true,
|
10 |
+
"esModuleInterop": true,
|
11 |
+
"skipLibCheck": true,
|
12 |
+
"forceConsistentCasingInFileNames": true
|
13 |
+
},
|
14 |
+
"include": ["src/lerobot/**/*", "src/cli/**/*"],
|
15 |
+
"exclude": [
|
16 |
+
"src/main.ts",
|
17 |
+
"src/counter.ts",
|
18 |
+
"src/style.css",
|
19 |
+
"src/vite-env.d.ts"
|
20 |
+
]
|
21 |
+
}
|
tsconfig.json
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"target": "ES2020",
|
4 |
+
"useDefineForClassFields": true,
|
5 |
+
"module": "ESNext",
|
6 |
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
7 |
+
"skipLibCheck": true,
|
8 |
+
|
9 |
+
/* Bundler mode */
|
10 |
+
"moduleResolution": "bundler",
|
11 |
+
"allowImportingTsExtensions": true,
|
12 |
+
"verbatimModuleSyntax": true,
|
13 |
+
"moduleDetection": "force",
|
14 |
+
"noEmit": true,
|
15 |
+
|
16 |
+
/* Linting */
|
17 |
+
"strict": true,
|
18 |
+
"noUnusedLocals": true,
|
19 |
+
"noUnusedParameters": true,
|
20 |
+
"erasableSyntaxOnly": true,
|
21 |
+
"noFallthroughCasesInSwitch": true,
|
22 |
+
"noUncheckedSideEffectImports": true
|
23 |
+
},
|
24 |
+
"include": ["src"]
|
25 |
+
}
|