Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,13 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y libudev-dev libusb-1.0-0-dev

- name: Install dependencies
working-directory: axon
run: bun install --frozen-lockfile

- name: Biome check
working-directory: axon
run: bun run check

- name: Typecheck
working-directory: axon
run: bun run typecheck

- name: Test
working-directory: axon
run: bun test
run: bun run test
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ name: Release
# Bun's cross-target build because GitHub no longer guarantees a
# macOS 13 Intel runner on every plan.
#
# To cut a release, bump axon/package.json version, tag, and push:
# To cut a release, bump apps/cli/package.json version, tag, and push:
# git tag v1.0.0 && git push origin v1.0.0
#
# The workflow_dispatch trigger lets you manually re-run a build
Expand Down Expand Up @@ -95,15 +95,15 @@ jobs:
run: |
./scripts/build-release.sh \
--target ${{ matrix.bun_target }} \
--outfile axon/dist/${{ matrix.artifact }}${{ matrix.ext }}
--outfile apps/cli/dist/${{ matrix.artifact }}${{ matrix.ext }}

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: |
axon/dist/${{ matrix.artifact }}${{ matrix.ext }}
axon/dist/${{ matrix.artifact }}${{ matrix.ext }}.sha256
apps/cli/dist/${{ matrix.artifact }}${{ matrix.ext }}
apps/cli/dist/${{ matrix.artifact }}${{ matrix.ext }}.sha256
if-no-files-found: error
retention-days: 30

Expand Down
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ ENV/

# Node / Bun
node_modules/
.turbo/
*.log
axon/bin/
axon/dist/
apps/cli/bin/
apps/cli/dist/
apps/web/dist/

# Capture files (large binary data)
captures/*.sal
Expand Down
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ Full command surface: [docs/CLI_DESIGN.md](docs/CLI_DESIGN.md).
curl -fsSL https://bun.sh/install | bash

# 2. Clone, install dependencies, run from source
git clone https://github.com/caryden/servo-programmer
cd servo-programmer/axon
git clone https://github.com/caryden/servo-programmer.git
cd servo-programmer
bun install
cd apps/cli
bun run src/cli.ts status
```

Expand All @@ -87,6 +88,7 @@ prints a compact status bar plus the model docs link. For scripts and
agents, use JSON:

```bash
cd apps/cli
bun run src/cli.ts --json status
```

Expand All @@ -106,12 +108,24 @@ No `sudo` required on any platform.

**Got an Axon Max, another Micro capture, or an unknown model?** Please
help fill out the catalog. Plug it in, run
`bun run src/cli.ts read --svo > my-model.svo`, and
`cd apps/cli && bun run src/cli.ts read --svo > my-model.svo`, and
[open an issue](https://github.com/caryden/servo-programmer/issues/new)
with the file attached and the model ID string from `axon status`.
I'll add the model/defaults to
[`data/servo_catalog.json`](data/servo_catalog.json).

## Repo layout

The repo is now organized as a Bun workspace:

- [`apps/cli/`](apps/cli/) — production `axon` CLI
- [`apps/web/`](apps/web/) — browser WebHID PoC
- [`apps/desktop/`](apps/desktop/) — Electrobun desktop PoC
- [`packages/core/`](packages/core/) — shared transport-agnostic protocol/catalog logic
- [`packages/ui/`](packages/ui/) — shared probe UI used by the browser and desktop PoCs
- [`packages/transport-nodehid/`](packages/transport-nodehid/) — Node/Bun HID transport
- [`packages/transport-webhid/`](packages/transport-webhid/) — browser WebHID transport

## Firmware files

This repo does not redistribute Axon `.sfw` firmware files. Download
Expand Down
3 changes: 2 additions & 1 deletion axon/package.json → apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"test": "bun test"
},
"dependencies": {
"node-hid": "^3.3.0"
"@axon/core": "workspace:*",
"@axon/transport-nodehid": "workspace:*"
},
"devDependencies": {
"@biomejs/biome": "^2.4.11",
Expand Down
1 change: 1 addition & 0 deletions apps/cli/src/catalog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@axon/core/catalog";
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions apps/cli/src/driver/hid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@axon/transport-nodehid";
1 change: 1 addition & 0 deletions apps/cli/src/driver/nodehid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@axon/transport-nodehid/nodehid";
1 change: 1 addition & 0 deletions apps/cli/src/driver/protocol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@axon/core/driver/protocol";
1 change: 1 addition & 0 deletions apps/cli/src/driver/transport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@axon/core/driver/transport";
1 change: 1 addition & 0 deletions apps/cli/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@axon/core/errors";
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { captureIO } from "../helpers/capture-io.ts";
import { loadFixtureConfig, MockDongle } from "../mocks/mock-dongle.ts";

const __dirname = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = join(__dirname, "../../..");
const REPO_ROOT = join(__dirname, "../../../..");
const DOWNLOADS_DIR = join(REPO_ROOT, "downloads");
const MINI_SERVO_SFW = join(DOWNLOADS_DIR, "Axon_Mini_Servo_Mode.sfw");
const MINI_CR_SFW = MINI_SERVO_SFW.replace("Servo_Mode", "Modified_CR_Mode");
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**
* Tests for the production HID transport wrapper in `src/driver/hid.ts`.
*
* These use a mocked local node-hid binding wrapper so we can exercise
* the adapter wrapping logic without requiring a physical programmer.
* These use a mocked node-hid binding wrapper from the transport package
* so we can exercise the adapter logic without requiring a programmer.
*/

import { afterEach, describe, expect, mock, test } from "bun:test";
Expand Down Expand Up @@ -43,7 +43,7 @@ describe("hid transport", () => {
close(): void {}
}

await mock.module("../../src/driver/nodehid.ts", () => ({
await mock.module("@axon/transport-nodehid/nodehid", () => ({
getNodeHidBinding: () => ({
devices: () => [device],
HID: FakeHID,
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion axon/test/sfw.test.ts → apps/cli/test/sfw.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from "../src/sfw.ts";

const __dirname = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = join(__dirname, "../..");
const REPO_ROOT = join(__dirname, "../../..");
const DOWNLOADS = join(REPO_ROOT, "downloads");
const GROUND_TRUTH = join(REPO_ROOT, "research/decrypted-firmware");

Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions apps/desktop/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
57 changes: 57 additions & 0 deletions apps/desktop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Electrobun PoC

This is a minimal desktop proof-of-concept for talking to the Axon V1.3
programmer adapter from an Electrobun app.

It answers one narrow question:

> Can a desktop UI built with Electrobun keep the frontend in a WebView,
> keep HID in the Bun main process, and reuse the existing Axon
> `node-hid` and protocol code to enumerate, identify, and read config?

## Why this shape

The browser PoC in [`../web/`](../web/) proved that a
Chromium browser can talk to the adapter through WebHID. That is not a
good desktop abstraction, though:

- Safari/WebKit does not give us the same WebHID path.
- Electrobun uses the platform's native webview by default.
- The desktop app should therefore treat HID as a Bun-side transport,
not a renderer-side browser feature.

So this PoC keeps the UI browser-like but routes all hardware access
through Electrobun RPC into the Bun process.

The current implementation now uses:

- [`../../packages/ui/`](../../packages/ui/) for the shared probe UI
- [`../../packages/transport-nodehid/`](../../packages/transport-nodehid/) for desktop HID access
- [`../../packages/core/`](../../packages/core/) for shared protocol and catalog logic

## What it does

- shows runtime and transport info
- lists visible Axon adapters via `node-hid`
- opens the first visible adapter
- sends the identify command (`0x8A`)
- reads the full 95-byte config block via the same two-chunk `0xCD`
flow used by the CLI

It does **not** attempt to write config or flash firmware.

## Run it

From the repo root, make sure the CLI dependencies are present:

```bash
cd /Users/caryden/github/servo-programmer
bun install
```

Then start the Electrobun app:

```bash
cd /Users/caryden/github/servo-programmer/apps/desktop
bun run start
```
29 changes: 29 additions & 0 deletions apps/desktop/electrobun.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { ElectrobunConfig } from "electrobun";

export default {
app: {
name: "axon-electrobun-poc",
identifier: "com.caryden.axon.electrobun-poc",
version: "0.0.1",
},
build: {
views: {
mainview: {
entrypoint: "src/mainview/index.ts",
},
},
copy: {
"src/mainview/index.html": "views/mainview/index.html",
"src/mainview/index.css": "views/mainview/index.css",
},
mac: {
bundleCEF: false,
},
linux: {
bundleCEF: false,
},
win: {
bundleCEF: false,
},
},
} satisfies ElectrobunConfig;
24 changes: 24 additions & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@axon/desktop-poc",
"version": "0.0.1",
"private": true,
"type": "module",
"scripts": {
"check": "bun x @biomejs/biome check src package.json electrobun.config.ts",
"start": "electrobun dev",
"dev": "electrobun dev --watch",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@axon/core": "workspace:*",
"@axon/transport-nodehid": "workspace:*",
"@axon/ui": "workspace:*",
"electrobun": "1.16.0"
},
"devDependencies": {
"@biomejs/biome": "^2.4.11",
"@types/bun": "latest",
"@types/three": "^0.180.0",
"typescript": "^5.9.3"
}
}
Loading
Loading