@aubron/ankerts 0.1.0
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 +21 -0
- package/README.md +118 -0
- package/dist/index.d.ts +766 -0
- package/dist/index.js +2514 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aubron
|
|
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,118 @@
|
|
|
1
|
+
# @aubron/ankerts
|
|
2
|
+
|
|
3
|
+
An **agent-first TypeScript SDK** for AnkerMake / eufyMake **M5**-class printers.
|
|
4
|
+
It speaks the printer's three transports behind one typed surface and is built to
|
|
5
|
+
be driven by agents and scripts — not just humans at a terminal.
|
|
6
|
+
|
|
7
|
+
The CLI wrapper lives in [`@aubron/ankerts-cli`](../ankerts-cli) (`ankerts`). This
|
|
8
|
+
package is the SDK: **all protocol logic, fully typed, with no console I/O.** It
|
|
9
|
+
returns values and throws typed errors; the CLI owns formatting and exit codes.
|
|
10
|
+
|
|
11
|
+
## The three transports (and the cloud-vs-LAN split)
|
|
12
|
+
|
|
13
|
+
| Transport | Used for | Reachability |
|
|
14
|
+
| --------------------- | ------------------------------------ | -------------------------------- |
|
|
15
|
+
| **MQTT over TLS** | gcode, status/telemetry, job control | anywhere (internet + creds) |
|
|
16
|
+
| **PPPP** (P2P/UDP) | LAN file upload, camera | **LAN only** (needs a stored IP) |
|
|
17
|
+
| **HTTPS** (cloud API) | login, account & printer list | anywhere |
|
|
18
|
+
|
|
19
|
+
Cloud-reachable ≠ LAN-reachable: gcode can work while a file upload cannot, until
|
|
20
|
+
the printer's IP is discovered on the LAN.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm add @aubron/ankerts
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { AnkerClient } from "@aubron/ankerts";
|
|
32
|
+
|
|
33
|
+
// Log in (HTTPS) and persist credentials + printer list.
|
|
34
|
+
const client = await AnkerClient.login({
|
|
35
|
+
email: "me@example.com",
|
|
36
|
+
password: process.env.ANKER_PASSWORD!,
|
|
37
|
+
country: "US",
|
|
38
|
+
save: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Or load a previously stored config.
|
|
42
|
+
const c = AnkerClient.fromStoredConfig();
|
|
43
|
+
|
|
44
|
+
// Send gcode and get a parsed response (MQTT). `truncated` tells you whether the
|
|
45
|
+
// firmware's ~512-byte reply snapshot was partial — short reads come back whole.
|
|
46
|
+
const r = await c.gcode("M105"); // temps
|
|
47
|
+
if (!r.truncated) console.log(r.raw); // "ok T:29.12 /0.00 B:30.31 /0.00"
|
|
48
|
+
|
|
49
|
+
// Status, normalized to °C and 0–100% (bogus third-party-gcode ETA suppressed).
|
|
50
|
+
const status = await c.getStatus();
|
|
51
|
+
|
|
52
|
+
// Wait for a condition — re-attachable, derived from server state.
|
|
53
|
+
import { parseWaitCondition } from "@aubron/ankerts";
|
|
54
|
+
await c.waitFor(parseWaitCondition("printing"), { timeoutMs: 120_000 });
|
|
55
|
+
|
|
56
|
+
// Upload + start over the LAN (auto-discovers the IP; throws exit-6 if off-LAN).
|
|
57
|
+
await c.uploadAndPrint("tower.gcode");
|
|
58
|
+
await c.close();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## The centerpiece: honest gcode responses
|
|
62
|
+
|
|
63
|
+
A command's `resData` reply is a single point-in-time snapshot of the firmware's
|
|
64
|
+
~512-byte serial ring buffer (verified on real hardware — _not_ the multi-frame
|
|
65
|
+
stream the original design assumed). Short replies come back whole; replies that
|
|
66
|
+
exceed the window are capped at 512 bytes; and a reply caught mid-write truncates
|
|
67
|
+
early with a `+ringbuf:` marker — the real `echo:Ad` bug. The reference returned
|
|
68
|
+
these silently as if complete. This SDK strips ANSI, parses the result, and —
|
|
69
|
+
crucially — **detects truncation and flags it** so you never mistake a partial
|
|
70
|
+
line for the full output:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
interface GcodeResult {
|
|
74
|
+
command: string;
|
|
75
|
+
raw: string; // collected reply text, ANSI-free
|
|
76
|
+
lines: string[];
|
|
77
|
+
ok: boolean; // a terminal `ok` was seen
|
|
78
|
+
recognized: boolean; // false iff `echo:Unknown command`
|
|
79
|
+
fields: Record<string, string>; // echo:Key=Value / KEY:VALUE
|
|
80
|
+
reports: Record<string, string>; // Marlin report lines keyed by M-code
|
|
81
|
+
durationMs: number;
|
|
82
|
+
timedOut: boolean; // distinct from `recognized: false`
|
|
83
|
+
truncated: boolean; // reply hit the 512B window / has a +ringbuf marker → may be partial
|
|
84
|
+
frames: number; // diagnostic: chunks collected
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Completion is detected by a trailing `ok` (after a short settle for the firmware's
|
|
89
|
+
leading double-`ok`), a quiet period, or a hard timeout. Timeouts are
|
|
90
|
+
latency-class aware (`M109/M190/G29/M303` get minutes), and `{ waitMotion: true }`
|
|
91
|
+
appends `M400` so a move returns on true completion, not queue-accept.
|
|
92
|
+
|
|
93
|
+
> Fully recovering a large reply that the firmware truncates needs a read
|
|
94
|
+
> primitive the reference doesn't expose (the official app's "read last command
|
|
95
|
+
> output"); until that's reverse-engineered, `truncated: true` is your signal
|
|
96
|
+
> that `raw`/`fields` are incomplete.
|
|
97
|
+
|
|
98
|
+
## Typed errors → exit codes
|
|
99
|
+
|
|
100
|
+
Every error carries `{ code, message, transport?, retriable, hint, input }` and
|
|
101
|
+
maps to a documented CLI exit code:
|
|
102
|
+
|
|
103
|
+
| Error | Exit | Meaning |
|
|
104
|
+
| --------------------------- | ---- | -------------------------------------- |
|
|
105
|
+
| `UsageError` | 2 | bad/missing args |
|
|
106
|
+
| `AuthError` | 3 | login required/expired/captcha |
|
|
107
|
+
| `PrinterNotFoundError` | 4 | printer not found / not selected |
|
|
108
|
+
| `TimeoutError` | 5 | connectivity/timeout — **retriable** |
|
|
109
|
+
| `TransportUnavailableError` | 6 | op needs a transport the printer lacks |
|
|
110
|
+
| `PrinterRejectedError` | 7 | printer-side rejection |
|
|
111
|
+
|
|
112
|
+
## Notes
|
|
113
|
+
|
|
114
|
+
- ESM only (`"type": "module"`), Node ≥ 22.
|
|
115
|
+
- Crypto, MQTT framing, and the PPPP protocol are ported faithfully from the
|
|
116
|
+
AnkerMake reference implementation; framing/crypto are unit-tested via
|
|
117
|
+
round-trips, and the gcode parser/status/transcoder are tested against the
|
|
118
|
+
observed fixtures. The live transports require a real printer to exercise.
|