@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 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.