@dimensionalos/dimcode 0.1.0-next.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -0
- package/LICENSES/pi-installer.txt +21 -0
- package/README.md +277 -0
- package/install.sh +300 -0
- package/npm-shrinkwrap.json +4721 -0
- package/package.json +52 -0
- package/skills/dimensional/SKILL.md +32 -0
- package/skills/dimensional-install/SKILL.md +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Dimensional Inc.
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mario Zechner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# dimcode
|
|
2
|
+
|
|
3
|
+
Persistent terminal agent for DimOS, built on Pi. The gateway runs independently of robot blueprints. Existing MCP exposes skills; Pi's coding tools use the existing DimOS CLI/Python APIs. Media uses the existing Web SDK only while a renderer needs it.
|
|
4
|
+
|
|
5
|
+
## Install or update
|
|
6
|
+
|
|
7
|
+
Current prerelease: **`0.1.0-next.6`**. With Node 24 or 26 installed, install it directly from npm:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install -g --ignore-scripts @dimensionalos/dimcode@next
|
|
11
|
+
dimcode setup
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The standalone shell installer shows Pi’s original animated logo before downloading Node or Dimcode. It installs a private Node 24 runtime and creates `~/.local/bin/dimcode`. It checks which executable wins PATH and offers to update your shell profile when another installation takes precedence. The final prompt names an existing config when one is found; otherwise it offers first-time setup. The Node/npm bootstrap needs no sudo. Continuing setup then attempts the DimOS developer installation; its system-package and transport steps can request sudo in your terminal.
|
|
15
|
+
|
|
16
|
+
The standalone script works through `curl … | sh` without an existing Node/npm installation, but the private GitHub repository does not expose a public installer URL yet. Download `install.sh` from a successful Actions run when testing the managed Node bootstrap.
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
dimcode setup # view saved settings and change only what you choose
|
|
20
|
+
dimcode # open the terminal
|
|
21
|
+
dimcode --version # CLI package version
|
|
22
|
+
dimcode doctor # CLI/build path, Node runtime, config path and running gateway
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
On subsequent releases:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
dimcode update ./dimensionalos-dimcode-VERSION.tgz
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Installation switches the launcher to the selected build and preserves older releases and settings. It does not replace a running process. Once existing sessions are idle, run `dimcode stop`, then `dimcode` to start the new gateway. `doctor` identifies a different or older gateway explicitly. Prerelease versions publish under npm’s `next` tag; stable versions publish under `latest`.
|
|
32
|
+
|
|
33
|
+
With Node **24 or 26** already active, npm is also supported:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm install -g --ignore-scripts @dimensionalos/dimcode@next
|
|
37
|
+
dimcode --version
|
|
38
|
+
dimcode doctor
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`npm install -g` replaces the package only in that npm installation’s global prefix. It does not update another Node installation or reorder PATH. `type -a dimcode` lists competing commands; the first wins (run `hash -r` after a PATH change). The Node 22 installed by Pi is sufficient for Pi but incompatible with Dimcode’s WebTransport dependency; use the managed installer above. No uninstall is needed for a normal update through the same installer.
|
|
42
|
+
|
|
43
|
+
Setup reads `~/.dimcode` and displays the saved provider, model, theme and gateway startup choice together using Pi’s native settings list. Continue is selected; opening a row changes that setting through Pi’s native selectors. Existing credentials are reused. For older configurations without a gateway preference, setup reads the existing user service. Keeping “Start at login” does not restart an already configured gateway. A first installation attempts **DimOS developer setup**, then opens native Pi authentication and model selection. Repeating setup reuses the installation report and saved choices. A failed installation opens the setup agent with the recorded failure after authentication.
|
|
44
|
+
|
|
45
|
+
Credentials use a disposable masked field. Pi’s actual installer animation plays once when interactive setup opens; Pi’s animated loader represents waiting states. The bootstrap delegates DimOS installation to its reviewed upstream developer installer. Linux user-service startup remains optional; Windows, Homebrew and WinGet distribution are not part of this prerelease.
|
|
46
|
+
|
|
47
|
+
`dimcode` opens the fullscreen Pi-based terminal: scrollable chat, a multiline editor, live shell output and persistent model/status footer. Running shell cards show up to twelve recent output lines with elapsed time; completed cards collapse to three lines, and `/expand` restores the full tool result. Package names and stages come from actual command output; a command that emits no progress shows a waiting indicator without a fabricated percentage.
|
|
48
|
+
|
|
49
|
+
Shift+Enter or Ctrl+J adds a line; Ctrl-C detaches from the gateway. Older Warp versions send Shift+Enter as plain Enter; update Warp for Kitty keyboard reporting or use Ctrl+J. The footer shows the compatible shortcut.
|
|
50
|
+
|
|
51
|
+
Memory results appear **inside the agent's tool card**, with an overview combining DimOS's timeline, spatial map and selected frames. Click a panel label or enter `/panel 1`, `/panel 2`, etc. to see a larger individual view; `/panel 0` returns to the overview. `/expand` shows the latest tool's full output and source hashes. Use a terminal with image support (such as Kitty) for the SVG/PNG views; other terminals show image metadata.
|
|
52
|
+
|
|
53
|
+
To inspect a saved cloud without a model call, enter these commands **inside dimcode**:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
/inspect /absolute/path/to/cloud.json
|
|
57
|
+
/view
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Cloud exports use `{ "points": [[x, y, z]], "frame": "world", "timestamp": 123.45 }`. In the inspector, **←/→** rotates, **+/-** zooms, **g** switches graphics/Braille, **d** shows source/hash, **0** resets the view, and **Esc** returns to chat. An export can supply `selectedIndices` to highlight the exact selection produced by its owning operation. Rotation and zoom change the view only; memory analysis and filtering stay in DimOS.
|
|
61
|
+
|
|
62
|
+
Point-cloud results from `dimcode_render` open the same inspector through `/view`. The terminal verifies the source hash before loading interactive geometry; if the file changed, the original tool preview remains available. `/expand` toggles details for the latest tool. Images use supported terminal graphics; Braille supports point-cloud inspection without image support.
|
|
63
|
+
|
|
64
|
+
A fresh `dimcode` launch opens setup automatically. Repeating `dimcode setup` lets you change the choices; configured providers offer **Use configured credentials** so you do not need to paste a key again. Credentials never enter chat.
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
dimcode setup # repeat interactive setup
|
|
68
|
+
dimcode tui # open the terminal (same as dimcode)
|
|
69
|
+
dimcode --help # all launch commands
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Developer installation and recovery
|
|
73
|
+
|
|
74
|
+
By default, first-time `dimcode` / `dimcode setup` does the following:
|
|
75
|
+
|
|
76
|
+
1. Reports platform, available disk and the installation log. Installs managed **uv** into `~/.dimcode/bin` and **Python 3.12** into `~/.dimcode/python`, without activating an unrelated virtual environment.
|
|
77
|
+
2. Downloads a checksum-verified snapshot of DimOS’s [main installer](https://github.com/dimensionalOS/dimos/blob/4396873e4fa9f769a7b264596439f67b108843d2/scripts/install.sh). Runs developer mode with an explicit `main` branch, cloning **`~/.dimcode/dimos`** and creating its editable **`.venv`**. DimOS owns OS/GPU detection, system dependencies, Git LFS and transport settings. Its current developer recipe runs `uv sync --extra all`; allow disk/time for those dependencies. Real package output streams directly to the terminal and log.
|
|
78
|
+
3. Verifies the Git checkout, editable distribution metadata, actual import location and CLI help. An upstream success message alone is insufficient. Installation does not launch a blueprint; upstream robot smoke tests are disabled.
|
|
79
|
+
4. Opens Pi’s provider/model/settings wizard. On success it saves the verified executable/workspace paths. On failure it passes `install.json` and the log to the [`dimensional-install` skill](skills/dimensional-install/SKILL.md), which attempts repair with normal coding tools after login. API keys and sudo passwords never enter the conversation; sudo runs in your local terminal.
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
dimcode install # explicitly attempt or verify local developer setup
|
|
83
|
+
dimcode install --dimos-dir ~/robot/dimos # choose another source checkout
|
|
84
|
+
dimcode install --non-interactive # install/verify only; returns nonzero on failure
|
|
85
|
+
dimcode setup # resume login or agent repair from the saved report
|
|
86
|
+
dimcode setup --skip-dimos # manual/remote setup without automatic local install
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Existing directories are verified **without pull, rebase or environment replacement**. An incomplete directory goes to agent repair. An active recorded installer is left in its original terminal. Ctrl-C saves progress and exits; a later setup resumes from the report. A failed automatic attempt is not silently retried on every settings review. An explicit `dimcode install` rechecks it. `DIMCODE_INSTALL_DIMOS=0` disables automatic local installation for new setups; provider-only `setup --provider NAME --key-env VAR` also skips it. Agent repair requires a working model. Unattended installation never launches an agent or marks first-time authentication complete.
|
|
90
|
+
|
|
91
|
+
`install.json` records status, destination, failing stage/command and log path. Successful reports also record verified Python/CLI paths, version and Git commit. `dimcode doctor` displays that report alongside CLI/gateway build identities. Setup completion lists configuration, credentials, sessions and install-log locations. Messaging-platform setup is not offered yet: this release has no Telegram/webhook adapter.
|
|
92
|
+
|
|
93
|
+
## What is installed / where is the source?
|
|
94
|
+
|
|
95
|
+
This package uses **upstream Pi 0.85.1**, pinned as npm dependencies. It is not a Pi fork or a native binary. `dimcode` is a Node CLI with a Dimensional gateway, terminal frontend and extensions. Pi supplies the agent runtime, provider login, coding tools, message components and tool cards. A fork is unnecessary for these customizations.
|
|
96
|
+
|
|
97
|
+
All source is in this repository's [`src/`](src): [`main.ts`](src/main.ts) launches the CLI; [`install.ts`](src/install.ts) delegates and verifies developer installation; [`setup.ts`](src/setup.ts) handles private authentication/settings; [`skills/dimensional-install`](skills/dimensional-install) guides agent-led installation; [`terminal.ts`](src/terminal.ts) renders chat; [`gateway.ts`](src/gateway.ts) owns persistent sessions; [`media.ts`](src/media.ts) lazily connects the existing Web SDK. [`skills/dimensional`](skills/dimensional) supplies DimOS instructions.
|
|
98
|
+
|
|
99
|
+
## Authentication and service
|
|
100
|
+
|
|
101
|
+
API keys are entered outside chat and stored by Pi in the user-only auth file. An environment key can be installed with `dimcode setup --provider openai --key-env OPENAI_API_KEY`. ChatGPT subscription login uses `--provider openai-codex --oauth`. Use Anthropic API keys for Claude in dimcode. Refresh/logout use Pi's provider implementation.
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
dimcode service install
|
|
105
|
+
dimcode service status
|
|
106
|
+
dimcode service uninstall
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This is a Linux **user** service. Without it, first attach starts a detached gateway. `dimcode stop` stops that gateway; `dimcode gateway` runs it in the foreground for diagnostics, or reports that it is already running. `dimcode --foreground` runs gateway and terminal in one foreground process.
|
|
110
|
+
|
|
111
|
+
Config, Pi settings, credentials and editable context live in **`~/.dimcode`**. `DIMCODE_HOME` overrides that directory. On first launch after upgrading, an existing `~/.config/dimcode` directory (or its XDG equivalent) is moved intact and the old location becomes a compatibility link. Existing processes keep accessing the same files. If both directories already exist, the new home wins and neither is overwritten.
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
~/.dimcode/
|
|
115
|
+
├── config.json # workspace, DimOS endpoints, gateway startup choice
|
|
116
|
+
├── settings.json # Pi model and theme preferences
|
|
117
|
+
├── auth.json # private Pi credentials; never loaded as agent context
|
|
118
|
+
├── install.json # installation progress and verified source/CLI paths
|
|
119
|
+
├── install-logs/ # timestamped command output and reviewed installer
|
|
120
|
+
├── bin/ # managed uv and Python launchers
|
|
121
|
+
├── python/ # managed Python runtimes
|
|
122
|
+
├── dimos/ # Git clone; editable environment in dimos/.venv
|
|
123
|
+
├── AGENTS.md # optional global instructions
|
|
124
|
+
├── APPEND_SYSTEM.md # optional additions to the built-in instructions
|
|
125
|
+
└── skills/ # custom skills/<name>/SKILL.md
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Sessions remain in `$XDG_STATE_HOME/dimcode` and previews in `$XDG_CACHE_HOME/dimcode`; recordings remain owned by DimOS. The runtime socket is user-only. Service removal preserves credentials, sessions and DimOS data. Use `/reload` between turns to reload changed context and skills.
|
|
129
|
+
|
|
130
|
+
## Connect and work
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
dimcode connect go2 http://127.0.0.1:9990/mcp
|
|
134
|
+
dimcode relay http://127.0.0.1:7780 my-robot
|
|
135
|
+
dimcode workspace /path/to/app # save the default workspace
|
|
136
|
+
dimcode --cwd /path/to/app
|
|
137
|
+
dimcode sessions
|
|
138
|
+
dimcode --session SESSION_ID
|
|
139
|
+
dimcode --session SESSION_ID --view
|
|
140
|
+
dimcode run "inspect the app and explain its blueprint"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Endpoints are explicit; ports are examples, not instance identities. Use `/reload` after changing endpoints. Every advertised skill is registered with its original schema, metadata and remote routing. Tool names are bounded and collision-resistant. There is no static copy of the robot's tools or a parallel lifecycle service.
|
|
144
|
+
|
|
145
|
+
Terminal commands: `/new`, `/sessions`, `/resume ID`, `/models`, `/model PROVIDER MODEL`, `/login PROVIDER [oauth]`, `/logout PROVIDER`, `/abort`, `/steer TEXT`, `/follow TEXT`, `/reload`, `/image PATH`, `/panel N`, `/inspect PATH`, `/view`, `/expand`, `/exit`. Ctrl-C detaches. One terminal owns input; other viewers may observe. Detaching keeps the turn running. Restart restores Pi history and never automatically replays external actions. Very large histories show a bounded recent transcript with an omission notice; the complete agent history remains on disk. Session events carry their source identity so switching sessions cannot mix transcripts.
|
|
146
|
+
|
|
147
|
+
Pi owns context loading, skills, compaction, models and coding-tool behavior. Workspace instructions and configured Pi extensions load normally. Attached terminals support serialized dialogs/notifications; executable extension UI factories belong in the terminal renderer and cannot be sent through a socket.
|
|
148
|
+
|
|
149
|
+
## Tool rendering
|
|
150
|
+
|
|
151
|
+
DimOS owns reusable visualizations and encodings for agent understanding: geometry, axes, units, timestamps, legends and selections. Dimcode owns terminal styling and interaction: cards, themes, camera/point presentation, resizing, controls and graphics/Braille adaptation. Terminal styling preserves the meaning of colors and labels in supplied visuals.
|
|
152
|
+
|
|
153
|
+
DimOS memory owns video/frame/point-cloud analysis. Evaluate the memory operation once with `materialize()`, then use its existing `Space.to_svg()` / `Plot.to_svg()` and selected-frame exports. Pass those exports together to `dimcode_render`:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{"kind":"image","title":"Memory · plant search","views":[{"label":"Timeline","path":"timeline.svg"},{"label":"Spatial","path":"space.svg"},{"label":"Frames","path":"frames.png"}]}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The tool rasterizes the original SVG/PNG views for terminal display and model context. Every view reaches the model; the terminal presents a selectable overview. Each export retains its source path and SHA-256. MCP tool results containing multiple images also appear together automatically. No query or filter runs inside the renderer. Existing skills that return only a pose or JSON still require an explicit export; the harness does not invent missing views.
|
|
160
|
+
|
|
161
|
+
Built-in presentation stays constrained to **point clouds and images/SVGs**, with one player for finite sequences of either. `dimcode_render({path:"result.json"})` detects a cloud or frame index; other image paths are rasterized. Numeric plots and graphs use DimOS/Python SVG exports, not a harness plotting engine. Cloud JSON may also supply `colors: [[r,g,b]]` (0–255) and `selectedIndices`; source RGB is preserved and explicit selections are highlighted.
|
|
162
|
+
|
|
163
|
+
For a memory/replay interval, materialize the selection in DimOS and export an index alongside the existing frames:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"type": "points",
|
|
168
|
+
"timeOrigin": 1766747348.2995782,
|
|
169
|
+
"source": "Go2 memory · lidar · seconds 1–5",
|
|
170
|
+
"frames": [
|
|
171
|
+
{"path":"cloud-0.json","timestamp":1766747349.3433642},
|
|
172
|
+
{"path":"cloud-1.json","timestamp":1766747349.4723642}
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Use `type:"image"` for PNG/JPEG frames or any custom SVG animation. Paths are relative to the index, timestamps are seconds and must increase; optional per-frame `sha256` checks the original exports. Use one recording origin for synchronized camera/cloud windows. This file indexes already exported results; it is not a new query or transport API.
|
|
178
|
+
|
|
179
|
+
Call `dimcode_render({path:"clip.json",title:"Memory · seconds 1–5"})`, or use these commands **inside dimcode** without a model call:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
/inspect /absolute/path/to/clip.json
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
While preparing a clip, the tool card shows verified source frames as they finish, with a compact progress bar and Pi’s animated loader. Updates are coalesced to approximately 10 Hz; the last frame remains visible while the GIF is encoded. These are actual rendered source frames, not synthetic sensor motion. Cancellation, tool completion and session changes dispose the animation.
|
|
186
|
+
|
|
187
|
+
The latest clip loops automatically, including after reconnect, with no player buttons or timeline. Ask the agent “show a single frame at 2 seconds” to inspect a still; it selects the timestamped result and renders that frame. A single-frame result stays static. Leaving the session or closing the terminal stops playback. Point-cloud clips share one camera, bounds and height scale; mismatched coordinate frames must be aligned in DimOS first.
|
|
188
|
+
|
|
189
|
+
The terminal plays timestamped PNGs. A looping GIF is exported for sharing (GIF timing rounds to centiseconds); the model receives a contact sheet of up to six labeled frames. `dimcode_render({path:"clip.json",frame:17})` returns original zero-based frame 17 for closer inspection, even if preview sampling omitted it. The renderer reads saved files only and never repeats the source query.
|
|
190
|
+
|
|
191
|
+
Previews are bounded to 60 seconds, at most 120 frames and approximately 10 Hz, retaining first/last timestamps and original indexes. Source-frame gaps remain visible in playback. Per-file reads are limited to 32 MiB, a selected clip to 128 MiB/two million points, generated PNGs to 64 MiB and GIFs to 32 MiB. If a limit is reached, export a smaller preview with DimOS. Individual clouds label point display sampling. Original recordings and exports remain unchanged.
|
|
192
|
+
|
|
193
|
+
The agent is instructed to visualize each meaningful sensor/memory operation. When no supported type or existing visualizer applies, it generates a self-contained SVG with inline Python from the evaluated result and displays it through `dimcode_render({kind:"image",path:"result.svg"})`. This supports arbitrary plots, images, simple graphs and labeled proposed overlays. The same rendered image is returned to the model for visual inspection. Rendering failures remain explicit.
|
|
194
|
+
|
|
195
|
+
Live tools select an existing relay/robot/channel. The terminal receives frames directly through the Web SDK and coalesces drawing to 10 Hz. The gateway retains one final snapshot for model context. Closing/cancelling the tool releases consumers; the last consumer closes the connection. MediaPool is generic over decoded SDK slots and accepts existing decoder registries. The initial live image renderer handles JPEG; other channel types use their owning decoder/renderer or saved exports.
|
|
196
|
+
|
|
197
|
+
Graphics use Pi terminal-image support with text fallback. In Warp, terminal images are rendered as complete cell-row slices so partial scrolling crops the image without compressing its aspect ratio. Derived PNGs/GIFs have a bounded 128 MiB cache; evicted previews show an explicit unavailable state and can be regenerated from retained exports; original DimOS recordings stay with DimOS. No recording, raw continuous video, new transport protocol or new DimOS gateway is introduced.
|
|
198
|
+
|
|
199
|
+
## Source and architecture
|
|
200
|
+
|
|
201
|
+
Dimcode is a standalone repository extracted from `agent/` at DimOS commit
|
|
202
|
+
[`20a9b4b`](https://github.com/dimensionalOS/dimos/commit/20a9b4b552fec15ee91e1c3f085e47dab8627c13).
|
|
203
|
+
Pi 0.85.1 remains an unmodified npm dependency. The currently unpublished DimOS Web SDK is
|
|
204
|
+
bundled from the unchanged, pinned snapshot described in [vendor/README.md](vendor/README.md).
|
|
205
|
+
Normal builds require no DimOS checkout; a pip install, source checkout or remote instance
|
|
206
|
+
can be selected at runtime.
|
|
207
|
+
|
|
208
|
+
- `src/main.ts`, `src/setup.ts`: CLI, private model authentication and agent-led setup.
|
|
209
|
+
- `src/gateway.ts`, `src/session.ts`, `src/protocol.ts`: persistent Pi sessions and local terminal attachment.
|
|
210
|
+
- `src/terminal.ts`: terminal UI; `src/render.ts`, `src/points.ts`, `src/clips.ts`: constrained visual presentation.
|
|
211
|
+
- `src/mcp.ts`, `src/media.ts`: existing MCP tools and lazy Web SDK media subscriptions.
|
|
212
|
+
- `skills/`: DimOS operation and installation instructions for the agent.
|
|
213
|
+
|
|
214
|
+
DimOS continues to own blueprints, robot lifecycle, transport, memory and general-purpose
|
|
215
|
+
visualization. Its existing MCP/CLI fixes remain in the DimOS repository.
|
|
216
|
+
|
|
217
|
+
## Permissions and privileged commands
|
|
218
|
+
|
|
219
|
+
Pi's Bash tool runs as the gateway's OS user, with that user's filesystem, network, process
|
|
220
|
+
and device access. Neither Pi nor dimcode currently adds a sandbox or per-command approval
|
|
221
|
+
policy. Pi extensions can block or request confirmation for tool calls, but a shell-command
|
|
222
|
+
filter is not an OS security boundary.
|
|
223
|
+
|
|
224
|
+
Bash uses pipes, not an interactive terminal, and cannot collect a sudo password. Passwordless
|
|
225
|
+
sudo can work under the host's sudo policy; password-required commands need a local terminal.
|
|
226
|
+
Keep passwords out of chat, tool arguments and session history. A future local terminal
|
|
227
|
+
handoff could let the user type directly into sudo while exposing only output and status to
|
|
228
|
+
the agent; that handoff is not implemented.
|
|
229
|
+
|
|
230
|
+
DimOS transport autoconfiguration includes system changes such as socket buffers and multicast
|
|
231
|
+
configuration. Its current `confirm()` defaults to yes without a TTY, then invokes sudo.
|
|
232
|
+
`autoconf(check_only=True)` inspects recommendations without applying fixes. Run privileged
|
|
233
|
+
configuration interactively in a local terminal; do not run the gateway as root.
|
|
234
|
+
|
|
235
|
+
## Contributor development and validation
|
|
236
|
+
|
|
237
|
+
These commands are for working on the source, not the user installation:
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
git clone git@github.com:dimensionalOS/dimcode.git
|
|
241
|
+
cd dimcode
|
|
242
|
+
npm ci
|
|
243
|
+
npm run check
|
|
244
|
+
npm pack
|
|
245
|
+
sh ./install.sh ./dimensionalos-dimcode-0.1.0-next.6.tgz
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
This builds and tests the exact local tarball through the same managed installer. To run without installing a launcher, use `node dist/main.js setup` then `node dist/main.js`. `npm pack` produces the distributable tarball. `npm-shrinkwrap.json` pins the published dependency tree.
|
|
249
|
+
|
|
250
|
+
The [check workflow](.github/workflows/check.yml) checks Linux/macOS and uploads installable tarballs as private Actions artifacts. After both platforms pass on a push to `main`, it publishes an unpublished package version to `@dimensionalos/dimcode`. Prereleases use the `next` dist-tag; stable versions use `latest`. The workflow skips an already-published version, so documentation-only pushes remain green.
|
|
251
|
+
|
|
252
|
+
Installer options: `DIMCODE_VERSION` selects a version/tag (default `next`), `DIMCODE_INSTALL_DIR` selects private runtime/releases, `DIMCODE_BIN_DIR` selects the launcher directory, and `DIMCODE_SETUP=0` skips the setup offer. Node downloads and npm tarballs are verified before installation. The installer resolves npm’s version metadata directly and checks the package SHA-512, so a newly published release does not depend on npm’s package-index propagation. Run the installer again to update the launcher; older releases remain available to running sessions. Existing gateways continue using their current release until stopped with `dimcode stop` after active turns finish; the next launch starts the selected build.
|
|
253
|
+
|
|
254
|
+
To exercise the reviewed upstream installer in dry-run mode, download the pinned script from the link above and set `DIMCODE_TEST_INSTALLER_SCRIPT=/absolute/install.sh` for `npm test`. This checks its recipe and verification handoff without modifying system packages or starting robots.
|
|
255
|
+
|
|
256
|
+
To run the complete integration suite with an existing DimOS environment and Go2 recording:
|
|
257
|
+
|
|
258
|
+
```sh
|
|
259
|
+
DIMCODE_TEST_DIMOS=/path/to/dimos \
|
|
260
|
+
DIMCODE_TEST_PYTHON=/path/to/dimos/.venv/bin/python \
|
|
261
|
+
DIMCODE_TEST_DENO=/path/to/deno \
|
|
262
|
+
DIMCODE_TEST_GO2_DB=/path/to/go2_bigoffice.db \
|
|
263
|
+
npm test
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The Go2 E2E test starts the standard `unitree-go2` blueprint in **recorded-data replay**, composed with the existing cockpit relay bridge. It starts its own relay and isolates transport discovery. No robot hardware or model API key is required. It verifies:
|
|
267
|
+
|
|
268
|
+
- Multiple distinct, successfully decoded JPEG video frames and timestamped odometry.
|
|
269
|
+
- Full XYZ float32 point clouds, every coordinate finite, source timestamps/frame metadata, and byte-for-byte source SHA-256 matches. A test-only encoder/SDK decoder uses the existing codec registry; it does not mislabel the default XY lidar projection as XYZ.
|
|
270
|
+
- One shared SDK connection, lazy subscriptions, continued cloud reception after video closes, then zero viewers/subscriptions after the last renderer closes.
|
|
271
|
+
- Cleanup of the blueprint and relay, including failure paths.
|
|
272
|
+
|
|
273
|
+
A separate memory test materializes seconds 1–5 once, exports full cloud/camera frames and a native DimOS plot, then checks GIF frame counts, shared time origin and every preview frame’s source hash. Playback unit tests cover looping, cancellation, stale files, RGB preservation and disposal. A scrolling regression checks exact visible pixels at multiple scroll positions and widths.
|
|
274
|
+
|
|
275
|
+
Set `DIMCODE_TEST_REPORT=/absolute/path/report.json` to save measured counts. Without `DIMCODE_TEST_GO2_DB`, the large recording test is explicitly skipped. The Python variable enables MCP-handler tests against that environment. `DIMCODE_TEST_DIMOS` optionally selects a source checkout ahead of installed Python packages; it is required with Deno for relay/Go2 tests. No neighboring checkout is assumed. Use a DimOS revision compatible with the SDK commit in `vendor/dimos/source.json`. Unit/CLI tests also cover onboarding, private credentials, cancellation, `tui`, existing-gateway handling, session ownership, detach/recovery and renderer provenance.
|
|
276
|
+
|
|
277
|
+
A video stream here means consecutive JPEG frames over WebTransport. This does not claim an H.264/WebCodecs decoder, browser UI coverage, or a physical robot test. The harness's initial live terminal renderer displays JPEG; saved point clouds, timed sequences and SVGs render from exported results. Other live types can plug into the generic SDK codec registry.
|
package/install.sh
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Bootstrap the harness, then hand off to resumable developer setup.
|
|
3
|
+
set -eu
|
|
4
|
+
|
|
5
|
+
main() {
|
|
6
|
+
package='@dimensionalos/dimcode'
|
|
7
|
+
archive_path=${1:-}
|
|
8
|
+
if [ -n "$archive_path" ]; then
|
|
9
|
+
case "$archive_path" in /*) ;; *) archive_path="$PWD/$archive_path" ;; esac
|
|
10
|
+
[ -f "$archive_path" ] || { echo 'Package tarball does not exist.' >&2; exit 1; }
|
|
11
|
+
fi
|
|
12
|
+
active_path=$(command -v dimcode 2>/dev/null || true)
|
|
13
|
+
version=${DIMCODE_VERSION:-next}
|
|
14
|
+
data=${DIMCODE_INSTALL_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/dimcode}
|
|
15
|
+
bin=${DIMCODE_BIN_DIR:-$HOME/.local/bin}
|
|
16
|
+
case "$data:$bin" in /*:/*) ;; *) echo 'Install directories must be absolute.' >&2; exit 1 ;; esac
|
|
17
|
+
case "$(uname -s)-$(uname -m)" in
|
|
18
|
+
Linux-x86_64) platform=linux-x64 ;;
|
|
19
|
+
Linux-aarch64|Linux-arm64) platform=linux-arm64 ;;
|
|
20
|
+
Darwin-arm64) platform=darwin-arm64 ;;
|
|
21
|
+
*) echo 'This prerelease supports Linux x64/arm64 and Apple Silicon.' >&2; exit 1 ;;
|
|
22
|
+
esac
|
|
23
|
+
case "$version" in ''|*[!a-zA-Z0-9._-]*) echo 'Invalid DIMCODE_VERSION.' >&2; exit 1 ;; esac
|
|
24
|
+
command -v curl >/dev/null || { echo 'curl is required.' >&2; exit 1; }
|
|
25
|
+
mkdir -p "$data" "$bin"
|
|
26
|
+
staging=$(mktemp -d "$data/.install.XXXXXX")
|
|
27
|
+
trap 'rm -rf "$staging"' EXIT
|
|
28
|
+
trap 'exit 130' INT
|
|
29
|
+
trap 'exit 143' TERM
|
|
30
|
+
pi_logo_animation
|
|
31
|
+
printf '\ndimcode Installer\nInstall or update your Dimensional agent. Existing settings are preserved.\n\n'
|
|
32
|
+
printf 'Detected: %s\n' "$platform"
|
|
33
|
+
if [ -n "$active_path" ]; then printf 'Current shell command: %s\n' "$active_path"; fi
|
|
34
|
+
|
|
35
|
+
node="$data/node/bin/node"
|
|
36
|
+
if ! "$node" -e 'process.exit(+process.versions.node.split(".")[0] === 24 ? 0 : 1)' 2>/dev/null; then
|
|
37
|
+
dist=https://nodejs.org/dist/latest-v24.x
|
|
38
|
+
printf 'Installing a private Node 24 runtime…\n'
|
|
39
|
+
curl -fsSL "$dist/SHASUMS256.txt" -o "$staging/checksums"
|
|
40
|
+
archive=$(awk -v suffix="-$platform.tar.gz" '$2 ~ /^node-v[0-9.]+-/ && substr($2,length($2)-length(suffix)+1)==suffix {print $2; exit}' "$staging/checksums")
|
|
41
|
+
[ -n "$archive" ] || { echo 'No compatible Node archive found.' >&2; exit 1; }
|
|
42
|
+
curl -fsSL "$dist/$archive" -o "$staging/$archive"
|
|
43
|
+
awk -v file="$archive" '$2==file {print}' "$staging/checksums" > "$staging/selected.sha256"
|
|
44
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
45
|
+
(cd "$staging" && sha256sum -c selected.sha256)
|
|
46
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
47
|
+
(cd "$staging" && shasum -a 256 -c selected.sha256)
|
|
48
|
+
else
|
|
49
|
+
echo 'sha256sum or shasum is required to verify Node.' >&2; exit 1
|
|
50
|
+
fi
|
|
51
|
+
tar -xzf "$staging/$archive" -C "$staging"
|
|
52
|
+
if [ -e "$data/node" ] || [ -L "$data/node" ]; then
|
|
53
|
+
echo "Existing runtime at $data/node is unusable; choose another DIMCODE_INSTALL_DIR." >&2
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
mv "$staging/${archive%.tar.gz}" "$data/node"
|
|
57
|
+
fi
|
|
58
|
+
printf '✓ Node %s · %s\n' "$("$node" --version)" "$node"
|
|
59
|
+
PATH="$data/node/bin:$PATH"
|
|
60
|
+
export PATH
|
|
61
|
+
npm="$data/node/bin/npm"
|
|
62
|
+
if [ -n "$archive_path" ]; then
|
|
63
|
+
cp "$archive_path" "$staging/dimcode.tgz"
|
|
64
|
+
tar -xOf "$staging/dimcode.tgz" package/package.json > "$staging/metadata.json"
|
|
65
|
+
else
|
|
66
|
+
curl -fsSL "https://registry.npmjs.org/$package/$version" -o "$staging/metadata.json"
|
|
67
|
+
tarball=$("$node" -p 'JSON.parse(require("node:fs").readFileSync(process.argv[1])).dist.tarball' "$staging/metadata.json")
|
|
68
|
+
case "$tarball" in https://registry.npmjs.org/*) ;; *) echo 'Unexpected package registry.' >&2; exit 1 ;; esac
|
|
69
|
+
curl -fsSL "$tarball" -o "$staging/dimcode.tgz"
|
|
70
|
+
fi
|
|
71
|
+
resolved=$("$node" -p 'const p=JSON.parse(require("node:fs").readFileSync(process.argv[1])); if(p.name!==process.argv[2]) throw new Error("Not a dimcode package"); p.version' "$staging/metadata.json" "$package")
|
|
72
|
+
case "$resolved" in ''|*[!a-zA-Z0-9._-]*) echo 'Invalid package version from registry.' >&2; exit 1 ;; esac
|
|
73
|
+
digest=$("$node" -p 'require("node:crypto").createHash("sha256").update(require("node:fs").readFileSync(process.argv[1])).digest("hex").slice(0,12)' "$staging/dimcode.tgz")
|
|
74
|
+
release="$data/releases/$resolved-$digest"
|
|
75
|
+
entry="$release/node_modules/$package/dist/main.js"
|
|
76
|
+
if [ ! -f "$entry" ]; then
|
|
77
|
+
printf 'Installing %s@%s…\n' "$package" "$resolved"
|
|
78
|
+
if [ -z "$archive_path" ]; then
|
|
79
|
+
"$node" - "$staging/metadata.json" "$staging/dimcode.tgz" "$package" <<'NODE'
|
|
80
|
+
const fs = require('node:fs');
|
|
81
|
+
const crypto = require('node:crypto');
|
|
82
|
+
const metadata = JSON.parse(fs.readFileSync(process.argv[2]));
|
|
83
|
+
const actual = 'sha512-' + crypto.createHash('sha512').update(fs.readFileSync(process.argv[3])).digest('base64');
|
|
84
|
+
if (metadata.name !== process.argv[4] || actual !== metadata.dist.integrity) throw new Error('Package integrity check failed');
|
|
85
|
+
NODE
|
|
86
|
+
fi
|
|
87
|
+
"$npm" install --prefix "$staging/package" --ignore-scripts --min-release-age=0 --omit=dev --no-audit --no-fund "$staging/dimcode.tgz"
|
|
88
|
+
"$node" "$staging/package/node_modules/$package/dist/main.js" --help >/dev/null
|
|
89
|
+
mkdir -p "$data/releases"
|
|
90
|
+
[ ! -e "$release" ] || { echo "Incomplete release at $release; choose another DIMCODE_INSTALL_DIR." >&2; exit 1; }
|
|
91
|
+
mv "$staging/package" "$release"
|
|
92
|
+
fi
|
|
93
|
+
"$node" - "$bin/dimcode" "$node" "$entry" <<'NODE'
|
|
94
|
+
const fs = require('node:fs');
|
|
95
|
+
const [dest, node, entry] = process.argv.slice(2);
|
|
96
|
+
const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
|
|
97
|
+
const temporary = `${dest}.install-${process.pid}`;
|
|
98
|
+
fs.writeFileSync(temporary, `#!/bin/sh\nexec ${quote(node)} ${quote(entry)} "$@"\n`, { mode: 0o755, flag: 'wx' });
|
|
99
|
+
fs.renameSync(temporary, dest);
|
|
100
|
+
NODE
|
|
101
|
+
printf '\nInstalled %s@%s\n' "$package" "$resolved"
|
|
102
|
+
# Like Pi, check which command wins PATH rather than whether the directory exists.
|
|
103
|
+
hash -r 2>/dev/null || true
|
|
104
|
+
active_path=$(command -v dimcode 2>/dev/null || true)
|
|
105
|
+
if [ "$active_path" != "$bin/dimcode" ]; then
|
|
106
|
+
printf 'Your shell resolves dimcode to: %s\nInstalled launcher: %s/dimcode\n' "${active_path:-not found}" "$bin"
|
|
107
|
+
if [ "${DIMCODE_SETUP:-1}" = 1 ] && ( : </dev/tty ) 2>/dev/null; then
|
|
108
|
+
case "$(basename "${SHELL:-sh}")" in
|
|
109
|
+
bash) profile="$HOME/.bashrc" ;;
|
|
110
|
+
zsh) profile="${ZDOTDIR:-$HOME}/.zshrc" ;;
|
|
111
|
+
*) profile="$HOME/.profile" ;;
|
|
112
|
+
esac
|
|
113
|
+
printf 'Put %s first on PATH in %s? [Y/n] ' "$bin" "$profile" >/dev/tty
|
|
114
|
+
IFS= read -r answer </dev/tty || answer=n
|
|
115
|
+
case "$answer" in n|N|no|NO) ;; *)
|
|
116
|
+
"$node" - "$profile" "$bin" <<'NODE'
|
|
117
|
+
const fs = require('node:fs');
|
|
118
|
+
const [profile, bin] = process.argv.slice(2);
|
|
119
|
+
const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
|
|
120
|
+
fs.appendFileSync(profile, `\n# dimcode\nexport PATH=${quote(bin)}:"$PATH"\n`);
|
|
121
|
+
NODE
|
|
122
|
+
;; esac
|
|
123
|
+
fi
|
|
124
|
+
printf 'Restart your shell, or run: export PATH="%s:$PATH"; hash -r\n' "$bin"
|
|
125
|
+
fi
|
|
126
|
+
printf 'Verify: %s/dimcode --version\nInspect CLI and gateway: %s/dimcode doctor\n' "$bin" "$bin"
|
|
127
|
+
printf 'Existing gateways keep their loaded version. Once idle: dimcode stop, then dimcode.\n'
|
|
128
|
+
if [ "${DIMCODE_SETUP:-1}" = 1 ] && ( : </dev/tty ) 2>/dev/null; then
|
|
129
|
+
config_dir=${DIMCODE_HOME:-$HOME/.dimcode}
|
|
130
|
+
legacy_config=${XDG_CONFIG_HOME:-$HOME/.config}/dimcode
|
|
131
|
+
if [ -z "${DIMCODE_HOME:-}" ] && [ ! -e "$config_dir" ] && [ ! -L "$config_dir" ] && [ -d "$legacy_config" ] && [ ! -L "$legacy_config" ]; then
|
|
132
|
+
config_dir=$legacy_config
|
|
133
|
+
fi
|
|
134
|
+
if [ -f "$config_dir/config.json" ]; then
|
|
135
|
+
printf '\nOpen setup with saved settings from %s? [Y/n] ' "$config_dir" >/dev/tty
|
|
136
|
+
else
|
|
137
|
+
printf '\nOpen first-time setup now? [Y/n] ' >/dev/tty
|
|
138
|
+
fi
|
|
139
|
+
IFS= read -r answer </dev/tty || answer=n
|
|
140
|
+
case "$answer" in n|N|no|NO) ;; *)
|
|
141
|
+
if ! "$bin/dimcode" setup </dev/tty; then
|
|
142
|
+
printf '\nDimcode is installed. Setup is unfinished; saved progress is preserved.\n'
|
|
143
|
+
printf 'Resume: %s/dimcode setup\n' "$bin"
|
|
144
|
+
fi
|
|
145
|
+
;; esac
|
|
146
|
+
fi
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# Logo functions below are unchanged from https://pi.dev/install.sh.
|
|
150
|
+
# Retrieved 2026-09-12; full source SHA-256: a3a3604ee550bf72c5da7da3c3014cc361c14ab3b91b1b24f097d9022bd8de5b
|
|
151
|
+
# MIT license: LICENSES/pi-installer.txt
|
|
152
|
+
PI_ESC=$(printf '\033')
|
|
153
|
+
pi_logo_animation() {
|
|
154
|
+
if [ ! -t 1 ] || [ "${TERM:-}" = "dumb" ]; then
|
|
155
|
+
print_static_logo
|
|
156
|
+
return
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
esc="${PI_ESC}["
|
|
160
|
+
reset="${PI_ESC}[0m"
|
|
161
|
+
hide="${esc}?25l"
|
|
162
|
+
show="${esc}?25h"
|
|
163
|
+
clear="${esc}H"
|
|
164
|
+
|
|
165
|
+
trap 'printf "%s%s\n" "$reset" "$show"; trap - INT TERM; exit 130' INT TERM
|
|
166
|
+
printf '%s%s' "$hide" "${esc}2J${esc}H"
|
|
167
|
+
|
|
168
|
+
for y in 0 1 2 3; do draw_logo_frame "$clear" "$reset" 0 left 2 "$y" 0 0; sleep 0.075; done
|
|
169
|
+
for y in 0 1 2; do draw_logo_frame "$clear" "$reset" 1 top 2 "$y" 0 0; sleep 0.075; done
|
|
170
|
+
for y in 0 1 2 3 4; do draw_logo_frame "$clear" "$reset" 2 right 5 "$y" 0 0; sleep 0.075; done
|
|
171
|
+
|
|
172
|
+
draw_logo_frame "$clear" "$reset" 3 none 0 0 0 0; sleep 0.25
|
|
173
|
+
draw_logo_frame "$clear" "$reset" 3 none 0 0 1 0; sleep 0.08
|
|
174
|
+
draw_logo_frame "$clear" "$reset" 3 none 0 0 0 0; sleep 0.08
|
|
175
|
+
draw_logo_frame "$clear" "$reset" 3 none 0 0 1 0; sleep 0.08
|
|
176
|
+
draw_logo_frame "$clear" "$reset" 4 none 0 0 0 0; sleep 0.10
|
|
177
|
+
draw_logo_frame "$clear" "$reset" 5 none 0 0 0 0; sleep 0.45
|
|
178
|
+
draw_logo_frame "$clear" "$reset" 5 none 0 0 0 1; sleep 0.12
|
|
179
|
+
draw_logo_frame "$clear" "$reset" 5 none 0 0 0 0; sleep 0.12
|
|
180
|
+
draw_logo_frame "$clear" "$reset" 5 none 0 0 0 1; sleep 0.45
|
|
181
|
+
|
|
182
|
+
printf '%s%s\n' "$reset" "$show"
|
|
183
|
+
trap - INT TERM
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
draw_logo_frame() {
|
|
187
|
+
clear="$1"; reset="$2"; phase="$3"; active="$4"; ax="$5"; ay="$6"; flash="$7"; white="$8"
|
|
188
|
+
|
|
189
|
+
left=0
|
|
190
|
+
top=0
|
|
191
|
+
|
|
192
|
+
panel_cell="${reset} "
|
|
193
|
+
cyan_cell="${PI_ESC}[36m██"
|
|
194
|
+
red_cell="${PI_ESC}[31m██"
|
|
195
|
+
green_cell="${PI_ESC}[32m██"
|
|
196
|
+
orange_cell="${PI_ESC}[33m██"
|
|
197
|
+
white_cell="${PI_ESC}[39m██"
|
|
198
|
+
flash_cell="${PI_ESC}[33m██"
|
|
199
|
+
|
|
200
|
+
pad=$(repeat_space "$left")
|
|
201
|
+
clear_cell="$panel_cell"
|
|
202
|
+
frame="$clear"
|
|
203
|
+
i=0
|
|
204
|
+
while [ "$i" -lt "$top" ]; do frame="${frame}\n"; i=$((i + 1)); done
|
|
205
|
+
|
|
206
|
+
for y in 0 1 2 3 4 5 6 7 8; do
|
|
207
|
+
frame="${frame}${pad}"
|
|
208
|
+
for x in 1 2 3 4 5 6 7 8; do
|
|
209
|
+
set_logo_cell_color "$phase" "$active" "$ax" "$ay" "$flash" "$white" "$y" "$x"
|
|
210
|
+
case "$LOGO_COLOR" in
|
|
211
|
+
cyan) cell="$cyan_cell" ;;
|
|
212
|
+
red) cell="$red_cell" ;;
|
|
213
|
+
green) cell="$green_cell" ;;
|
|
214
|
+
orange) cell="$orange_cell" ;;
|
|
215
|
+
white) cell="$white_cell" ;;
|
|
216
|
+
flash) cell="$flash_cell" ;;
|
|
217
|
+
*) cell="$clear_cell" ;;
|
|
218
|
+
esac
|
|
219
|
+
frame="${frame}${cell}"
|
|
220
|
+
done
|
|
221
|
+
frame="${frame}${reset}\n"
|
|
222
|
+
done
|
|
223
|
+
printf '%b' "$frame" 2>/dev/null || true
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
set_logo_cell_color() {
|
|
227
|
+
phase="$1"; active="$2"; ax="$3"; ay="$4"; flash="$5"; white="$6"; y="$7"; x="$8"
|
|
228
|
+
|
|
229
|
+
if [ "$white" = 1 ]; then
|
|
230
|
+
if in_cells "$y" "$x" "3,2 3,3 3,4 4,2 4,4 5,2 5,3 5,5 6,2 6,5"; then LOGO_COLOR=white; else LOGO_COLOR=panel; fi
|
|
231
|
+
return
|
|
232
|
+
fi
|
|
233
|
+
if [ "$flash" = 1 ] && [ "$y" = 6 ] && [ "$x" -ge 1 ] && [ "$x" -le 6 ]; then LOGO_COLOR=flash; return; fi
|
|
234
|
+
|
|
235
|
+
case "$active" in
|
|
236
|
+
left) if in_piece "$y" "$x" "$ay" "$ax" "0,0 1,0 1,1 2,0"; then LOGO_COLOR=red; return; fi ;;
|
|
237
|
+
top) if in_piece "$y" "$x" "$ay" "$ax" "0,0 0,1 0,2 1,2"; then LOGO_COLOR=cyan; return; fi ;;
|
|
238
|
+
right) if in_piece "$y" "$x" "$ay" "$ax" "0,0 1,0 2,0 2,1"; then LOGO_COLOR=green; return; fi ;;
|
|
239
|
+
esac
|
|
240
|
+
|
|
241
|
+
if [ "$phase" = 4 ]; then
|
|
242
|
+
if in_cells "$y" "$x" "2,2 2,3 2,4 3,4"; then LOGO_COLOR=cyan; return; fi
|
|
243
|
+
if in_cells "$y" "$x" "3,2 4,2 4,3 5,2"; then LOGO_COLOR=red; return; fi
|
|
244
|
+
if in_cells "$y" "$x" "4,5 5,5"; then LOGO_COLOR=green; return; fi
|
|
245
|
+
LOGO_COLOR=panel; return
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
if [ "$phase" -ge 5 ]; then
|
|
249
|
+
if in_cells "$y" "$x" "3,2 3,3 3,4 4,4"; then LOGO_COLOR=cyan; return; fi
|
|
250
|
+
if in_cells "$y" "$x" "4,2 5,2 5,3 6,2"; then LOGO_COLOR=red; return; fi
|
|
251
|
+
if in_cells "$y" "$x" "5,5 6,5"; then LOGO_COLOR=green; return; fi
|
|
252
|
+
LOGO_COLOR=panel; return
|
|
253
|
+
fi
|
|
254
|
+
|
|
255
|
+
if [ "$phase" -le 3 ] && in_cells "$y" "$x" "6,1 6,2 6,3 6,4"; then LOGO_COLOR=orange; return; fi
|
|
256
|
+
if [ "$phase" -ge 2 ] && in_cells "$y" "$x" "2,2 2,3 2,4 3,4"; then LOGO_COLOR=cyan; return; fi
|
|
257
|
+
if [ "$phase" -ge 1 ] && in_cells "$y" "$x" "3,2 4,2 4,3 5,2"; then LOGO_COLOR=red; return; fi
|
|
258
|
+
if [ "$phase" -ge 3 ] && in_cells "$y" "$x" "4,5 5,5 6,5 6,6"; then LOGO_COLOR=green; return; fi
|
|
259
|
+
|
|
260
|
+
LOGO_COLOR=panel
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
in_piece() {
|
|
264
|
+
y="$1"; x="$2"; py="$3"; px="$4"; cells="$5"
|
|
265
|
+
for item in $cells; do
|
|
266
|
+
dy=${item%,*}; dx=${item#*,}
|
|
267
|
+
[ "$y" -eq $((py + dy)) ] && [ "$x" -eq $((px + dx)) ] && return 0
|
|
268
|
+
done
|
|
269
|
+
return 1
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
in_cells() {
|
|
273
|
+
y="$1"; x="$2"; shift 2
|
|
274
|
+
for item in $1; do
|
|
275
|
+
[ "$item" = "$y,$x" ] && return 0
|
|
276
|
+
done
|
|
277
|
+
return 1
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
repeat_space() {
|
|
281
|
+
count="$1"; out=""
|
|
282
|
+
while [ "$count" -gt 0 ]; do out=" $out"; count=$((count - 1)); done
|
|
283
|
+
printf '%s' "$out"
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
print_static_logo() {
|
|
287
|
+
cat <<'EOF'
|
|
288
|
+
|
|
289
|
+
██████
|
|
290
|
+
██ ██
|
|
291
|
+
████ ██
|
|
292
|
+
██ ██
|
|
293
|
+
|
|
294
|
+
EOF
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
case "${1:-}" in
|
|
298
|
+
--logo) pi_logo_animation ;;
|
|
299
|
+
*) main "$@" ;;
|
|
300
|
+
esac
|