@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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/config.ts","../src/crypto.ts","../src/errors.ts","../src/protocol/commands.ts","../src/protocol/transcoder.ts","../src/transport/mqtt.ts","../src/protocol/gcode.ts","../src/protocol/status.ts","../src/transport/certs.ts","../src/binary.ts","../src/transport/mqttframe.ts","../src/transport/https.ts","../src/transport/pppp/client.ts","../src/transport/pppp/packets.ts","../src/transport/pppp/channel.ts","../src/wait.ts","../src/protocol/safety.ts"],"sourcesContent":["/**\n * `AnkerClient` — the SDK entrypoint (brief §7).\n *\n * Ties the three transports together behind one typed surface: HTTPS for login\n * and config, MQTT for gcode/telemetry/status, PPPP for LAN discovery and file\n * upload. All logic lives here and below; the client returns values and throws\n * typed {@link AnkerError}s — it NEVER writes to stdout/stderr or calls\n * `process.exit`. The CLI owns all I/O, formatting, and exit codes.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { basename } from \"node:path\";\nimport {\n ConfigStore,\n findPrinter,\n mqttHostFor,\n mqttPassword,\n mqttUsername,\n type AnkerConfig,\n type AnkerPrinter,\n} from \"./config.js\";\nimport { unhex } from \"./crypto.js\";\nimport {\n PrinterNotFoundError,\n TimeoutError,\n TransportUnavailableError,\n UsageError,\n} from \"./errors.js\";\nimport { MqttCommandType, PrintControl } from \"./protocol/commands.js\";\nimport type { GcodeResult } from \"./protocol/gcode.js\";\nimport type { PrinterStatus, RawNotice } from \"./protocol/status.js\";\nimport { transcodeMetadata } from \"./protocol/transcoder.js\";\nimport { AnkerMqttClient } from \"./transport/mqtt.js\";\nimport { loginAndBuildConfig, type LoginOptions } from \"./transport/https.js\";\nimport {\n AnkerPpppClient,\n discoverLan as ppppDiscover,\n type UploadProgress,\n} from \"./transport/pppp/client.js\";\nimport { conditionHolds, type WaitCondition } from \"./wait.js\";\n\nexport type PrinterEvent = RawNotice;\nexport type Unsubscribe = () => void;\nexport type Logger = (msg: string) => void;\n\nexport interface AnkerClientOptions {\n store?: ConfigStore;\n log?: Logger;\n printer?: string | number;\n /** Disable TLS verification on transports (testing only). */\n insecure?: boolean;\n}\n\nexport interface GcodeOptions {\n timeoutMs?: number;\n quietMs?: number;\n wait?: boolean;\n /** Append M400 semantics so the call returns on true motion completion. */\n waitMotion?: boolean;\n}\n\nexport interface WaitOptions {\n source?: \"events\" | \"poll\" | \"hybrid\";\n pollMs?: number;\n timeoutMs?: number;\n onTick?: (status: PrinterStatus) => void;\n}\n\nexport interface MachineSettings {\n /**\n * The M503 result. NOTE: M503's output usually exceeds the firmware's\n * ~512-byte reply window, so this can be partial — check `result.truncated`\n * before treating `reports` as the complete settings set.\n */\n result: GcodeResult;\n reports: Record<string, string>;\n linearAdvanceK?: number;\n hotendPid?: { p: number; i: number; d: number };\n steps?: string;\n probeZOffset?: number;\n}\n\nexport interface JobResult {\n name: string;\n size: number;\n md5: string;\n started: boolean;\n transport: \"lan\";\n duid: string;\n ip: string;\n}\n\nexport interface LanDiscoverOptions {\n retries?: number;\n timeoutMs?: number;\n store?: boolean;\n}\n\n/** Per-command default gcode timeouts by latency class (§6A). */\nexport function defaultTimeoutFor(command: string): number {\n const code = /^\\s*([GM]\\d+)/i.exec(command)?.[1]?.toUpperCase();\n switch (code) {\n case \"M109\": // heat-and-wait hotend\n case \"M190\": // heat-and-wait bed\n case \"M303\": // PID autotune\n case \"G29\": // auto bed leveling\n return 600000; // minutes\n case \"G28\": // homing\n case \"M400\": // motion barrier\n return 120000;\n default:\n return 10000; // instant/report commands\n }\n}\n\nexport class AnkerClient {\n private config: AnkerConfig;\n private readonly store?: ConfigStore;\n private readonly log: Logger;\n private readonly insecure: boolean;\n private printerRef?: string | number;\n private mqtt?: AnkerMqttClient;\n\n constructor(config: AnkerConfig, opts: AnkerClientOptions = {}) {\n this.config = config;\n this.store = opts.store;\n this.log = opts.log ?? (() => {});\n this.insecure = opts.insecure ?? false;\n this.printerRef = opts.printer;\n }\n\n // --- construction / auth (HTTPS) ---\n\n static async login(opts: LoginOptions & { save?: boolean }): Promise<AnkerClient> {\n const config = await loginAndBuildConfig(opts);\n const store = new ConfigStore();\n if (opts.save) store.save(config);\n return new AnkerClient(config, { store });\n }\n\n static fromStoredConfig(path?: string, opts: AnkerClientOptions = {}): AnkerClient {\n const store = opts.store ?? new ConfigStore(path);\n return new AnkerClient(store.load(), { ...opts, store });\n }\n\n getConfig(): AnkerConfig {\n return this.config;\n }\n\n // --- account / selection ---\n\n listPrinters(): AnkerPrinter[] {\n return this.config.printers;\n }\n\n selectPrinter(ref: string | number): AnkerPrinter {\n const printer = findPrinter(this.config, ref);\n if (!printer) throw this.notFound(ref);\n this.printerRef = printer.duid;\n this.config.selected = printer.duid;\n this.store?.update((c) => {\n c.selected = printer.duid;\n });\n return printer;\n }\n\n /** The currently selected printer (explicit ref → stored default → first). */\n currentPrinter(): AnkerPrinter {\n if (this.config.printers.length === 0) {\n throw new PrinterNotFoundError({\n message: \"No printers configured\",\n hint: \"Run `ankerts login --save` to populate the account's printer list.\",\n });\n }\n const ref = this.printerRef ?? this.config.selected;\n if (ref !== undefined) {\n const p = findPrinter(this.config, ref);\n if (!p) throw this.notFound(ref);\n return p;\n }\n return this.config.printers[0]!;\n }\n\n private notFound(ref: string | number): PrinterNotFoundError {\n return new PrinterNotFoundError({\n message: `Printer \"${ref}\" not found on the account`,\n hint: \"List printers with `ankerts printer list`.\",\n input: { printer: ref },\n });\n }\n\n private account() {\n if (!this.config.account) {\n throw new PrinterNotFoundError({\n code: \"not_logged_in\",\n message: \"Not logged in\",\n transport: \"https\",\n hint: \"Run `ankerts login --email … --password … --country … --save`.\",\n });\n }\n return this.config.account;\n }\n\n // --- MQTT lifecycle ---\n\n private async ensureMqtt(): Promise<AnkerMqttClient> {\n if (this.mqtt?.connected) return this.mqtt;\n const account = this.account();\n const printer = this.currentPrinter();\n this.mqtt = new AnkerMqttClient({\n sn: printer.sn,\n key: unhex(printer.mqtt_key),\n host: mqttHostFor(account, printer),\n username: mqttUsername(account),\n password: mqttPassword(account),\n insecure: this.insecure,\n log: this.log,\n });\n await this.mqtt.connect();\n return this.mqtt;\n }\n\n /** Close any open transports. */\n async close(): Promise<void> {\n await this.mqtt?.disconnect();\n this.mqtt = undefined;\n }\n\n // --- telemetry / status (MQTT) ---\n\n async getStatus(): Promise<PrinterStatus> {\n const mqtt = await this.ensureMqtt();\n return mqtt.getStatus();\n }\n\n async subscribeEvents(handler: (e: PrinterEvent) => void): Promise<Unsubscribe> {\n const mqtt = await this.ensureMqtt();\n return mqtt.onNotice(handler);\n }\n\n // --- gcode (MQTT, §6) ---\n\n async gcode(command: string, opts: GcodeOptions = {}): Promise<GcodeResult> {\n const mqtt = await this.ensureMqtt();\n const timeoutMs = opts.timeoutMs ?? defaultTimeoutFor(command);\n const result = await mqtt.gcode(command, { timeoutMs, quietMs: opts.quietMs, wait: opts.wait });\n\n if (opts.waitMotion && opts.wait !== false) {\n // M400 returns `ok` only when all queued moves have physically finished.\n const motion = await mqtt.gcode(\"M400\", { timeoutMs: defaultTimeoutFor(\"M400\") });\n return {\n ...result,\n raw: `${result.raw}\\n${motion.raw}`,\n lines: [...result.lines, ...motion.lines],\n ok: motion.ok,\n timedOut: result.timedOut || motion.timedOut,\n frames: result.frames + motion.frames,\n durationMs: result.durationMs + motion.durationMs,\n };\n }\n return result;\n }\n\n /** Run many commands, yielding each result as it completes (NDJSON-friendly). */\n async *gcodeBatch(commands: string[], opts: GcodeOptions = {}): AsyncIterable<GcodeResult> {\n for (const command of commands) {\n if (command.trim() === \"\") continue;\n yield await this.gcode(command, opts);\n }\n }\n\n // --- state helpers ---\n\n async snapshotState(): Promise<MachineSettings> {\n const result = await this.gcode(\"M503\", { timeoutMs: 15000 });\n const reports = result.reports;\n const pid = reports.M301?.match(/P([\\d.]+)\\s+I([\\d.]+)\\s+D([\\d.]+)/);\n const settings: MachineSettings = { result, reports };\n if (reports.M900) {\n const k = reports.M900.match(/K([\\d.]+)/);\n if (k) settings.linearAdvanceK = Number(k[1]);\n }\n if (pid) settings.hotendPid = { p: Number(pid[1]), i: Number(pid[2]), d: Number(pid[3]) };\n if (reports.M92) settings.steps = reports.M92;\n const z = reports.M851?.match(/Z(-?[\\d.]+)/);\n if (z) settings.probeZOffset = Number(z[1]);\n return settings;\n }\n\n async restoreState(): Promise<GcodeResult> {\n return this.gcode(\"M501\", { timeoutMs: 15000 });\n }\n\n // --- job control (MQTT) ---\n //\n // PRINT_CONTROL (0x03f0) with the `value` codes in PrintControl, reverse-\n // engineered and confirmed live against an M5 (2026-06-07): cancel drops the\n // job to idle with heaters off; pause/resume toggle the print.\n\n async cancelJob(): Promise<void> {\n (await this.ensureMqtt()).command({\n commandType: MqttCommandType.PRINT_CONTROL,\n value: PrintControl.STOP,\n });\n }\n async pauseJob(): Promise<void> {\n (await this.ensureMqtt()).command({\n commandType: MqttCommandType.PRINT_CONTROL,\n value: PrintControl.PAUSE,\n });\n }\n async resumeJob(): Promise<void> {\n (await this.ensureMqtt()).command({\n commandType: MqttCommandType.PRINT_CONTROL,\n value: PrintControl.RESUME,\n });\n }\n\n // --- discovery + jobs (PPPP, LAN) ---\n\n async discoverLan(opts: LanDiscoverOptions = {}): Promise<{ duid: string; ip: string }[]> {\n const { retries = 3, timeoutMs = 1000, store = false } = opts;\n let found: { duid: string; ip: string }[] = [];\n for (let attempt = 1; attempt <= retries; attempt++) {\n this.log(`pppp: LAN discovery attempt ${attempt}/${retries}`);\n found = await ppppDiscover({ timeoutMs, log: this.log });\n if (found.length > 0) break;\n }\n if (store && found.length > 0) {\n this.config = this.persistDiscoveredIps(found);\n }\n return found;\n }\n\n private persistDiscoveredIps(found: { duid: string; ip: string }[]): AnkerConfig {\n const apply = (c: AnkerConfig): void => {\n for (const f of found) {\n const p = c.printers.find((x) => x.duid === f.duid);\n if (p) p.ip_addr = f.ip;\n }\n };\n if (this.store) return this.store.update(apply);\n apply(this.config);\n return this.config;\n }\n\n /**\n * Upload a gcode file over the LAN and (by default) start the print. Auto-runs\n * discovery if the printer's IP is unknown; if it's still unreachable, throws\n * {@link TransportUnavailableError} (exit 6) naming the transport and the fix.\n */\n async uploadAndPrint(\n file: string | Buffer,\n opts: {\n start?: boolean;\n transport?: \"lan\" | \"auto\";\n /**\n * Auto-fix the LCD ETA/filament display by transcoding a third-party\n * slicer's embedded estimate into the Anker `;TIME:` header. ON by default\n * (it auto-detects: a no-op on natively-sliced files, which already carry\n * `;TIME:`). Operates on a copy — never mutates the file. Set `false` to\n * upload the file byte-for-byte untouched.\n */\n fixMetadata?: boolean;\n filename?: string;\n onProgress?: (p: UploadProgress) => void;\n } = {},\n ): Promise<JobResult> {\n let printer = this.currentPrinter();\n const filename = opts.filename ?? (typeof file === \"string\" ? basename(file) : \"print.gcode\");\n let data: Buffer;\n if (typeof file === \"string\") {\n try {\n data = await readFile(file);\n } catch (cause) {\n throw new UsageError({\n code: \"file_not_found\",\n message: `Cannot read gcode file: ${file}`,\n hint: \"Check the path. Provide a sliced .gcode file to upload.\",\n input: { file },\n cause,\n });\n }\n } else {\n data = file;\n }\n\n // Auto-fix slicer metadata by default (no-op on native files; copy only).\n if (opts.fixMetadata !== false) {\n const transcoded = transcodeMetadata(data.toString(\"utf8\"));\n if (transcoded.changed) {\n this.log(`transcoder: injected Anker metadata (${JSON.stringify(transcoded.injected)})`);\n data = Buffer.from(transcoded.content, \"utf8\");\n }\n }\n\n // Ensure we have a LAN IP — upload is LAN-only (PPPP).\n if (!printer.ip_addr) {\n this.log(\"pppp: no stored IP — running discovery\");\n await this.discoverLan({ store: true });\n printer = this.currentPrinter();\n }\n if (!printer.ip_addr) {\n throw new TransportUnavailableError({\n code: \"lan_printer_unreachable\",\n message: `Printer ${printer.duid} not found on the local network`,\n transport: \"pppp\",\n hint: \"File upload is LAN-only (PPPP). Run `ankerts discover --store` while on the same LAN as the printer, then retry.\",\n input: { file: filename },\n });\n }\n\n const pppp = new AnkerPpppClient({ duid: printer.duid, host: printer.ip_addr, log: this.log });\n try {\n await pppp.connect();\n const account = this.config.account;\n const res = await pppp.uploadFile(filename, data, {\n start: opts.start,\n userName: account?.email ?? \"ankerts\",\n userId: account?.user_id ?? \"-\",\n onProgress: opts.onProgress,\n });\n return { ...res, transport: \"lan\", duid: printer.duid, ip: printer.ip_addr };\n } finally {\n pppp.stop();\n }\n }\n\n // --- waiting (§6A) ---\n\n /**\n * Block until `cond` holds, resolving the current status. Re-attachable: it\n * re-derives state from fresh snapshots, so a re-issued wait still resolves.\n * Rejects with a retriable {@link TimeoutError} (exit 5) on timeout.\n */\n async waitFor(cond: WaitCondition, opts: WaitOptions = {}): Promise<PrinterStatus> {\n const { pollMs = 2000, timeoutMs = 600000, onTick } = opts;\n const deadline = Date.now() + timeoutMs;\n\n // Transport-only conditions resolve without status polling.\n if (cond.kind === \"connected\") {\n await this.ensureMqtt();\n return this.getStatus();\n }\n if (cond.kind === \"lan\") {\n const found = await this.discoverLan({ store: true, retries: Math.ceil(timeoutMs / 1000) });\n if (found.length === 0) {\n throw new TimeoutError({\n message: \"Printer not found on the local network within the timeout\",\n transport: \"pppp\",\n hint: \"Ensure you are on the same LAN as the printer and it is powered on.\",\n });\n }\n return this.getStatus();\n }\n\n // hybrid/poll/events: poll server-authoritative status as the robust floor.\n for (;;) {\n const status = await this.getStatus();\n onTick?.(status);\n if (conditionHolds(cond, status) === true) return status;\n if (Date.now() >= deadline) {\n throw new TimeoutError({\n message: `Condition \"${cond.kind}\" not met within ${timeoutMs}ms`,\n transport: \"mqtt\",\n hint: \"Waits are re-attachable — re-run the same `printer wait` to continue.\",\n input: { condition: cond.kind },\n });\n }\n await new Promise((r) => setTimeout(r, pollMs));\n }\n }\n}\n","/**\n * Stored configuration — the SDK's source of truth (brief §8).\n *\n * Holds the account auth material and the per-printer records (crypto keys,\n * hostnames, and the discovered LAN IP). Persisted as JSON under an\n * OS-appropriate config directory. Secrets are redactable for `config show`.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport type Region = \"us\" | \"eu\";\n\nexport interface AnkerAccount {\n user_id: string;\n auth_token: string;\n email: string;\n region: Region;\n country?: string;\n}\n\nexport interface AnkerPrinter {\n id: string;\n sn: string;\n name: string;\n model: string;\n /** PPPP device id (`p2p_did`), e.g. `USPRAKM-000994-YYLLG`. */\n duid: string;\n /** LAN IP — populated by discovery, not login. Empty until discovered. */\n ip_addr: string;\n wifi_mac: string;\n /** Per-printer MQTT AES key, hex-encoded (`secret_key` from the cloud). */\n mqtt_key: string;\n /** PPPP device secret key (`dsk_key`). */\n p2p_key: string;\n api_hosts: string[];\n p2p_hosts: string[];\n /** MQTT broker host (region-derived if absent). */\n mqtt_host?: string;\n}\n\nexport interface AnkerConfig {\n account: AnkerAccount | null;\n printers: AnkerPrinter[];\n /** DUID of the default-selected printer (`printer select`). */\n selected?: string;\n}\n\n/** Region → cloud MQTT broker host. */\nexport const MQTT_HOSTS: Record<Region, string> = {\n us: \"make-mqtt.ankermake.com\",\n eu: \"make-mqtt-eu.ankermake.com\",\n};\n\n/** Region → cloud HTTPS app-API host. */\nexport const API_HOSTS: Record<Region, string> = {\n us: \"make-app.ankermake.com\",\n eu: \"make-app-eu.ankermake.com\",\n};\n\nexport const REDACTED = \"<redacted>\";\n\n/** MQTT username/password are derived from the account (reference `model.py`). */\nexport const mqttUsername = (acct: AnkerAccount): string => `eufy_${acct.user_id}`;\nexport const mqttPassword = (acct: AnkerAccount): string => acct.email;\n\n/** Resolve the per-printer MQTT broker host (explicit, else region default). */\nexport function mqttHostFor(acct: AnkerAccount, printer: AnkerPrinter): string {\n return printer.mqtt_host ?? MQTT_HOSTS[acct.region];\n}\n\n/** OS-appropriate config directory for the `ankerts` app. */\nexport function configDir(): string {\n const override = process.env.ANKER_CONFIG_DIR;\n if (override) return override;\n const home = homedir();\n switch (platform()) {\n case \"darwin\":\n return join(home, \"Library\", \"Application Support\", \"ankerts\");\n case \"win32\":\n return join(process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\"), \"ankerts\");\n default:\n return join(process.env.XDG_CONFIG_HOME ?? join(home, \".config\"), \"ankerts\");\n }\n}\n\nconst emptyConfig = (): AnkerConfig => ({ account: null, printers: [] });\n\n/** A JSON-file-backed configuration store. */\nexport class ConfigStore {\n constructor(readonly path: string = join(configDir(), \"config.json\")) {}\n\n exists(): boolean {\n return existsSync(this.path);\n }\n\n load(): AnkerConfig {\n if (!existsSync(this.path)) return emptyConfig();\n const parsed = JSON.parse(readFileSync(this.path, \"utf8\")) as Partial<AnkerConfig>;\n return {\n account: parsed.account ?? null,\n printers: parsed.printers ?? [],\n ...(parsed.selected ? { selected: parsed.selected } : {}),\n };\n }\n\n save(config: AnkerConfig): void {\n mkdirSync(dirname(this.path), { recursive: true });\n writeFileSync(this.path, `${JSON.stringify(config, null, 2)}\\n`);\n }\n\n /** Mutate-and-persist helper. */\n update(fn: (config: AnkerConfig) => void): AnkerConfig {\n const config = this.load();\n fn(config);\n this.save(config);\n return config;\n }\n}\n\n/** Return a copy of the config with secrets masked (unless `reveal`). */\nexport function redactConfig(config: AnkerConfig, reveal = false): AnkerConfig {\n if (reveal) return config;\n return {\n account: config.account\n ? { ...config.account, auth_token: config.account.auth_token ? REDACTED : \"\" }\n : null,\n printers: config.printers.map((p) => ({\n ...p,\n mqtt_key: p.mqtt_key ? REDACTED : \"\",\n p2p_key: p.p2p_key ? REDACTED : \"\",\n })),\n ...(config.selected ? { selected: config.selected } : {}),\n };\n}\n\n/**\n * Resolve a printer reference (DUID, serial, name, or numeric index) against the\n * configured list. Returns the printer or null.\n */\nexport function findPrinter(config: AnkerConfig, ref: string | number): AnkerPrinter | null {\n const { printers } = config;\n if (typeof ref === \"number\" || /^\\d+$/.test(String(ref))) {\n const idx = Number(ref);\n return printers[idx] ?? null;\n }\n const r = String(ref);\n return printers.find((p) => p.duid === r || p.sn === r || p.id === r || p.name === r) ?? null;\n}\n","/**\n * Low-level cryptography, ported faithfully from the AnkerMake reference\n * implementation (`libflagship/megajank.py`). None of this is our design — it\n * is the obfuscation/encryption the M5 firmware speaks, reproduced byte-for-byte\n * so the SDK can interoperate. See the project brief §5.\n *\n * Pure functions over Buffers; no I/O, no console.\n */\nimport { createCipheriv, createDecipheriv, createECDH, createHash, randomBytes } from \"node:crypto\";\n\n// ---------------------------------------------------------------------------\n// hex / base64 helpers (libflagship/util.py)\n// ---------------------------------------------------------------------------\n\nexport const unhex = (s: string): Buffer => Buffer.from(s, \"hex\");\nexport const enhex = (b: Buffer): string => b.toString(\"hex\");\nexport const b64e = (b: Buffer): string => b.toString(\"base64\");\nexport const b64d = (s: string): Buffer => Buffer.from(s, \"base64\");\n\n// ---------------------------------------------------------------------------\n// MQTT AES-CBC + XOR checksum (megajank.py: mqtt aes / checksum handling)\n// ---------------------------------------------------------------------------\n\n/** Default IV used by the firmware for MQTT payloads: ASCII \"3DPrintAnkerMake\". */\nexport const MQTT_AES_IV = Buffer.from(\"3DPrintAnkerMake\", \"ascii\");\n\n/** PKCS#7 pad to a 16-byte boundary (always adds 1..16 bytes). */\nfunction pkcs7Pad(data: Buffer, blockSize = 16): Buffer {\n const padLen = blockSize - (data.length % blockSize);\n return Buffer.concat([data, Buffer.alloc(padLen, padLen)]);\n}\n\nfunction pkcs7Unpad(data: Buffer): Buffer {\n if (data.length === 0) return data;\n const padLen = data[data.length - 1]!;\n if (padLen < 1 || padLen > 16 || padLen > data.length) {\n throw new Error(\"invalid PKCS#7 padding\");\n }\n return data.subarray(0, data.length - padLen);\n}\n\n/** Map a raw key length to the matching AES-CBC algorithm name. */\nfunction aesCbcAlgo(key: Buffer): string {\n switch (key.length) {\n case 16:\n return \"aes-128-cbc\";\n case 24:\n return \"aes-192-cbc\";\n case 32:\n return \"aes-256-cbc\";\n default:\n throw new Error(`unsupported AES key length: ${key.length} bytes`);\n }\n}\n\nexport function aesCbcEncrypt(msg: Buffer, key: Buffer, iv: Buffer): Buffer {\n const cipher = createCipheriv(aesCbcAlgo(key), key, iv);\n cipher.setAutoPadding(false);\n return Buffer.concat([cipher.update(pkcs7Pad(msg)), cipher.final()]);\n}\n\nexport function aesCbcDecrypt(cmsg: Buffer, key: Buffer, iv: Buffer): Buffer {\n const decipher = createDecipheriv(aesCbcAlgo(key), key, iv);\n decipher.setAutoPadding(false);\n return pkcs7Unpad(Buffer.concat([decipher.update(cmsg), decipher.final()]));\n}\n\nexport const mqttAesEncrypt = (msg: Buffer, key: Buffer, iv: Buffer = MQTT_AES_IV): Buffer =>\n aesCbcEncrypt(msg, key, iv);\n\nexport const mqttAesDecrypt = (cmsg: Buffer, key: Buffer, iv: Buffer = MQTT_AES_IV): Buffer =>\n aesCbcDecrypt(cmsg, key, iv);\n\n/** XOR of every byte. The firmware appends this as a trailing checksum byte. */\nexport function xorBytes(data: Buffer): number {\n let s = 0;\n for (const x of data) s ^= x;\n return s;\n}\n\nexport function mqttChecksumAdd(msg: Buffer): Buffer {\n return Buffer.concat([msg, Buffer.from([xorBytes(msg)])]);\n}\n\n/**\n * Verify and strip the trailing XOR checksum. A well-formed packet XORs to 0\n * across its whole length (payload + checksum byte).\n */\nexport function mqttChecksumRemove(payload: Buffer): Buffer {\n if (xorBytes(payload) !== 0) {\n throw new Error(\"malformed MQTT message: checksum mismatch\");\n }\n return payload.subarray(0, payload.length - 1);\n}\n\n// ---------------------------------------------------------------------------\n// ECDH login-password encryption (megajank.py: ecdh_encrypt_login_password)\n// ---------------------------------------------------------------------------\n\n/**\n * Anker's fixed server public key (secp256r1 / prime256v1), uncompressed form\n * 0x04 || X || Y. Used as the ECDH partner when encrypting the login password.\n */\nconst ANKER_EC_PUBKEY = Buffer.from(\n \"04\" +\n \"c5c00c4f8d1197cc7c3167c52bf7acb054d722f0ef08dcd7e0883236e0d72a38\" +\n \"68d9750cb47fa4619248f3d83f0f662671dadc6e2d31c2f41db0161651c7c076\",\n \"hex\",\n);\n\n/**\n * Encrypt a login password the way the passport API expects.\n *\n * Returns the freshly generated ephemeral public key (hex, uncompressed) and\n * the base64 AES-CBC ciphertext. The AES key is the X coordinate of the ECDH\n * shared secret; the IV is the first half of that key.\n */\nexport function ecdhEncryptLoginPassword(password: string): {\n publicKey: string;\n encryptedPassword: string;\n} {\n const ecdh = createECDH(\"prime256v1\");\n ecdh.generateKeys();\n // computeSecret() returns the shared point's X coordinate (32 bytes).\n const key = ecdh.computeSecret(ANKER_EC_PUBKEY);\n const iv = key.subarray(0, 16);\n const ciphertext = aesCbcEncrypt(Buffer.from(password, \"utf8\"), key, iv);\n return {\n publicKey: ecdh.getPublicKey(\"hex\", \"uncompressed\"),\n encryptedPassword: b64e(ciphertext),\n };\n}\n\n// ---------------------------------------------------------------------------\n// login cache key (logincache.py) — legacy AES-ECB decrypt of slicer login.json\n// ---------------------------------------------------------------------------\n\nconst LOGIN_CACHE_KEY = unhex(\"1b55f97793d58864571e1055838cac97\");\n\n/** Decrypt the legacy slicer `login.json` blob (AES-ECB, NUL-trimmed). */\nexport function decryptLoginCache(data: string, key: Buffer = LOGIN_CACHE_KEY): string {\n const raw = b64d(data);\n const decipher = createDecipheriv(\"aes-128-ecb\", key, null);\n decipher.setAutoPadding(false);\n const out = Buffer.concat([decipher.update(raw), decipher.final()]);\n // strip trailing NULs\n let end = out.length;\n while (end > 0 && out[end - 1] === 0) end--;\n return out.subarray(0, end).toString(\"utf8\");\n}\n\n// ---------------------------------------------------------------------------\n// MD5 (FileUploadInfo) + CRC16 (PPCS framing)\n// ---------------------------------------------------------------------------\n\nexport const md5Hex = (data: Buffer): string => createHash(\"md5\").update(data).digest(\"hex\");\n\n/**\n * CRC-16/CCITT-FALSE (poly 0x1021, init 0x0000, no reflection, xorout 0),\n * returned little-endian as a 2-byte Buffer — matching the reference's\n * `ppcs_crc16` (`crcmod.mkCrcFun(0x11021, rev=False, initCrc=0x0000)` +\n * `struct.pack(\"<H\", ...)`).\n */\nexport function ppcsCrc16(data: Buffer): Buffer {\n let crc = 0x0000;\n for (const byte of data) {\n crc ^= byte << 8;\n for (let i = 0; i < 8; i++) {\n crc = crc & 0x8000 ? ((crc << 1) ^ 0x1021) & 0xffff : (crc << 1) & 0xffff;\n }\n }\n const out = Buffer.alloc(2);\n out.writeUInt16LE(crc, 0);\n return out;\n}\n\nexport const randomGuidBytes = (n: number): Buffer => randomBytes(n);\n\n// ---------------------------------------------------------------------------\n// PPPP \"curse\" obfuscation (megajank.py: crypto_curse / crypto_decurse)\n// ---------------------------------------------------------------------------\n\nconst PPPP_SEED = \"EUPRAKM\";\n\nconst PPPP_SHUFFLE: number[][] = [\n [0x95, 0xe5, 0x61, 0x97, 0x83, 0x0d, 0xa7, 0xf1],\n [0xd3, 0x05, 0x95, 0x8b, 0xdf, 0x13, 0x6d, 0xef],\n [0x07, 0x61, 0x0d, 0x6d, 0x7f, 0x67, 0x17, 0x2b],\n [0xc1, 0xb5, 0x13, 0x0b, 0xdf, 0x8b, 0x49, 0x3b],\n [0x7f, 0x07, 0xd3, 0x02, 0x6d, 0x2f, 0x13, 0xc5],\n [0x6d, 0x3d, 0xfb, 0x0d, 0x0b, 0x29, 0xe9, 0x4f],\n [0x89, 0x2f, 0xe3, 0xe9, 0x0d, 0x83, 0x6d, 0xe5],\n [0x07, 0x53, 0x8b, 0x25, 0x95, 0x47, 0x1f, 0x29],\n];\n\n/** Advance the (a,b,c,d) state by one byte `q`, per the reference. */\nfunction curseStep(\n a: number,\n b: number,\n c: number,\n d: number,\n q: number,\n shuffle: number[][],\n): [number, number, number, number] {\n return [\n shuffle[(b + (q % a)) & 7]![(q + (c % d)) & 7]!,\n shuffle[(c + (q % b)) & 7]![(q + (d % a)) & 7]!,\n shuffle[(d + (q % c)) & 7]![(q + (a % b)) & 7]!,\n shuffle[(a + (q % d)) & 7]![(q + (b % c)) & 7]!,\n ];\n}\n\nfunction curseInit(key: string, shuffle: number[][]): [number, number, number, number] {\n let [a, b, c, d] = [1, 3, 5, 7];\n for (const ch of key) {\n [a, b, c, d] = curseStep(a, b, c, d, ch.charCodeAt(0), shuffle);\n }\n return [a, b, c, d];\n}\n\nfunction cryptoDecurse(input: Buffer, key: string, shuffle: number[][]): number[] {\n let [a, b, c, d] = curseInit(key, shuffle);\n const output: number[] = new Array(input.length).fill(0);\n for (let p = 0; p < input.length; p++) {\n const x = input[p]!;\n output[p] = x ^ (a ^ b ^ c ^ d);\n [a, b, c, d] = curseStep(a, b, c, d, x, shuffle);\n }\n return output;\n}\n\nfunction cryptoCurse(input: Buffer, key: string, shuffle: number[][]): number[] {\n let [a, b, c, d] = curseInit(key, shuffle);\n const output: number[] = new Array(input.length + 4).fill(0);\n for (let p = 0; p < input.length; p++) {\n const x = (output[p] = input[p]! ^ (a ^ b ^ c ^ d));\n [a, b, c, d] = curseStep(a, b, c, d, x, shuffle);\n }\n for (let p = input.length; p < input.length + 4; p++) {\n const x = (output[p] = a ^ b ^ c ^ d ^ 0x43);\n [a, b, c, d] = curseStep(a, b, c, d, x, shuffle);\n }\n return output;\n}\n\nexport function cryptoCurseString(input: Buffer): Buffer {\n return Buffer.from(cryptoCurse(input, PPPP_SEED, PPPP_SHUFFLE));\n}\n\nexport function cryptoDecurseString(input: Buffer): Buffer {\n const output = cryptoDecurse(input, PPPP_SEED, PPPP_SHUFFLE);\n const tail = output.slice(-4);\n if (tail[0] !== 0x43 || tail[1] !== 0x43 || tail[2] !== 0x43 || tail[3] !== 0x43) {\n throw new Error(\"invalid PPPP decurse (missing trailer)\");\n }\n return Buffer.from(output.slice(0, -4));\n}\n\n// ---------------------------------------------------------------------------\n// PPPP \"simple\" cipher (megajank.py: simple_encrypt / simple_decrypt)\n// adapted from https://github.com/fbertone/lib32100/issues/7\n// ---------------------------------------------------------------------------\n\nconst PPPP_SIMPLE_SEED = Buffer.from(\"SSD@cs2-network.\", \"ascii\");\n\n// prettier-ignore\nconst PPPP_SIMPLE_SHUFFLE: number[] = [\n 0x7C, 0x9C, 0xE8, 0x4A, 0x13, 0xDE, 0xDC, 0xB2, 0x2F, 0x21, 0x23, 0xE4, 0x30, 0x7B, 0x3D, 0x8C,\n 0xBC, 0x0B, 0x27, 0x0C, 0x3C, 0xF7, 0x9A, 0xE7, 0x08, 0x71, 0x96, 0x00, 0x97, 0x85, 0xEF, 0xC1,\n 0x1F, 0xC4, 0xDB, 0xA1, 0xC2, 0xEB, 0xD9, 0x01, 0xFA, 0xBA, 0x3B, 0x05, 0xB8, 0x15, 0x87, 0x83,\n 0x28, 0x72, 0xD1, 0x8B, 0x5A, 0xD6, 0xDA, 0x93, 0x58, 0xFE, 0xAA, 0xCC, 0x6E, 0x1B, 0xF0, 0xA3,\n 0x88, 0xAB, 0x43, 0xC0, 0x0D, 0xB5, 0x45, 0x38, 0x4F, 0x50, 0x22, 0x66, 0x20, 0x7F, 0x07, 0x5B,\n 0x14, 0x98, 0x1D, 0x9B, 0xA7, 0x2A, 0xB9, 0xA8, 0xCB, 0xF1, 0xFC, 0x49, 0x47, 0x06, 0x3E, 0xB1,\n 0x0E, 0x04, 0x3A, 0x94, 0x5E, 0xEE, 0x54, 0x11, 0x34, 0xDD, 0x4D, 0xF9, 0xEC, 0xC7, 0xC9, 0xE3,\n 0x78, 0x1A, 0x6F, 0x70, 0x6B, 0xA4, 0xBD, 0xA9, 0x5D, 0xD5, 0xF8, 0xE5, 0xBB, 0x26, 0xAF, 0x42,\n 0x37, 0xD8, 0xE1, 0x02, 0x0A, 0xAE, 0x5F, 0x1C, 0xC5, 0x73, 0x09, 0x4E, 0x69, 0x24, 0x90, 0x6D,\n 0x12, 0xB3, 0x19, 0xAD, 0x74, 0x8A, 0x29, 0x40, 0xF5, 0x2D, 0xBE, 0xA5, 0x59, 0xE0, 0xF4, 0x79,\n 0xD2, 0x4B, 0xCE, 0x89, 0x82, 0x48, 0x84, 0x25, 0xC6, 0x91, 0x2B, 0xA2, 0xFB, 0x8F, 0xE9, 0xA6,\n 0xB0, 0x9E, 0x3F, 0x65, 0xF6, 0x03, 0x31, 0x2E, 0xAC, 0x0F, 0x95, 0x2C, 0x5C, 0xED, 0x39, 0xB7,\n 0x33, 0x6C, 0x56, 0x7E, 0xB4, 0xA0, 0xFD, 0x7A, 0x81, 0x53, 0x51, 0x86, 0x8D, 0x9F, 0x77, 0xFF,\n 0x6A, 0x80, 0xDF, 0xE2, 0xBF, 0x10, 0xD7, 0x75, 0x64, 0x57, 0x76, 0xF3, 0x55, 0xCD, 0xD0, 0xC8,\n 0x18, 0xE6, 0x36, 0x41, 0x62, 0xCF, 0x99, 0xF2, 0x32, 0x4C, 0x67, 0x60, 0x61, 0x92, 0xCA, 0xD3,\n 0xEA, 0x63, 0x7D, 0x16, 0xB6, 0x8E, 0xD4, 0x68, 0x35, 0xC3, 0x52, 0x9D, 0x46, 0x44, 0x1E, 0x17,\n];\n\nfunction simpleHash(seed: Buffer): number[] {\n const hash = [0, 0, 0, 0];\n for (const byte of seed) {\n hash[0] = (hash[0]! ^ byte) & 0xff;\n hash[1] = (hash[1]! + Math.floor(byte / 3)) & 0xff;\n hash[2] = (hash[2]! - byte) & 0xff;\n hash[3] = (hash[3]! + byte) & 0xff;\n }\n return hash.reverse();\n}\n\nfunction simpleLookup(hash: number[], b: number): number {\n const index = (hash[b & 0x3]! + b) & 0xffffffff;\n return PPPP_SIMPLE_SHUFFLE[\n ((index % PPPP_SIMPLE_SHUFFLE.length) + PPPP_SIMPLE_SHUFFLE.length) % PPPP_SIMPLE_SHUFFLE.length\n ]!;\n}\n\nexport function simpleDecrypt(input: Buffer, seed: Buffer = PPPP_SIMPLE_SEED): Buffer {\n const hash = simpleHash(seed);\n const output = Buffer.alloc(input.length);\n if (input.length === 0) return output;\n output[0] = input[0]! ^ simpleLookup(hash, 0);\n for (let i = 1; i < input.length; i++) {\n output[i] = input[i]! ^ simpleLookup(hash, input[i - 1]!);\n }\n return output;\n}\n\nexport function simpleEncrypt(input: Buffer, seed: Buffer = PPPP_SIMPLE_SEED): Buffer {\n const hash = simpleHash(seed);\n const output = Buffer.alloc(input.length);\n if (input.length === 0) return output;\n output[0] = input[0]! ^ simpleLookup(hash, 0);\n for (let i = 1; i < input.length; i++) {\n output[i] = input[i]! ^ simpleLookup(hash, output[i - 1]!);\n }\n return output;\n}\n\n// ---------------------------------------------------------------------------\n// PPPP init-string decoder (megajank.py: pppp_decode_initstring)\n// Decodes the `app_conn` / `p2p_conn` host lists from the cloud printer record.\n// ---------------------------------------------------------------------------\n\n// prettier-ignore\nconst PPPP_INITSTRING_SHUFFLE: number[] = [\n 0x49, 0x59, 0x43, 0x3d, 0xb5, 0xbf, 0x6d, 0xa3, 0x47, 0x53,\n 0x4f, 0x61, 0x65, 0xe3, 0x71, 0xe9, 0x67, 0x7f, 0x02, 0x03,\n 0x0b, 0xad, 0xb3, 0x89, 0x2b, 0x2f, 0x35, 0xc1, 0x6b, 0x8b,\n 0x95, 0x97, 0x11, 0xe5, 0xa7, 0x0d, 0xef, 0xf1, 0x05, 0x07,\n 0x83, 0xfb, 0x9d, 0x3b, 0xc5, 0xc7, 0x13, 0x17, 0x1d, 0x1f,\n 0x25, 0x29, 0xd3, 0xdf,\n];\n\nfunction ppppDecodeInitstringRaw(input: Buffer): Buffer {\n const olen = input.length >> 1;\n const output: number[] = new Array(olen).fill(0);\n for (let q = 0; q < olen; q++) {\n let xor = 0x39 ^ PPPP_INITSTRING_SHUFFLE[q % 0x36]!;\n for (let p = 0; p <= q; p++) xor ^= output[p]!;\n const l = input[q * 2 + 1]! - 0x41;\n const h = input[q * 2 + 0]! - 0x41;\n output[q] = (xor ^ (l + (h << 4))) & 0xff;\n }\n return Buffer.from(output);\n}\n\n/** Decode a PPPP init string into its comma-separated host list. */\nexport function ppppDecodeInitstring(input: string): string[] {\n const res = ppppDecodeInitstringRaw(Buffer.from(input, \"ascii\"));\n return res.toString(\"utf8\").replace(/,+$/, \"\").split(\",\");\n}\n","/**\n * Typed errors (brief §3, §7). Every error the SDK throws carries a structured,\n * machine-parseable shape — code, transport, retriability, an actionable hint,\n * and the echoed input — and maps to a documented CLI exit code. The SDK never\n * writes to stderr or exits; it throws these and lets the CLI format + map them.\n */\n\nexport type Transport = \"mqtt\" | \"pppp\" | \"https\";\n\n/** The structured body emitted as the JSON error and on stderr. */\nexport interface AnkerErrorBody {\n code: string;\n message: string;\n transport?: Transport;\n retriable: boolean;\n hint?: string;\n input?: Record<string, unknown>;\n}\n\nexport interface AnkerErrorOptions {\n code: string;\n message: string;\n transport?: Transport;\n retriable?: boolean;\n hint?: string;\n input?: Record<string, unknown>;\n cause?: unknown;\n}\n\n/**\n * Base class for all SDK errors. `exitCode` is the documented CLI contract:\n *\n * ```\n * 1 generic / unexpected 4 printer not found / not selected\n * 2 usage error 5 connectivity / timeout (RETRIABLE)\n * 3 auth error 6 transport unavailable for this op\n * 7 printer-side error (gcode rejected, job refused)\n * ```\n */\nexport class AnkerError extends Error {\n readonly exitCode: number = 1;\n readonly code: string;\n readonly transport?: Transport;\n readonly retriable: boolean;\n readonly hint?: string;\n input?: Record<string, unknown>;\n\n constructor(opts: AnkerErrorOptions) {\n super(opts.message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = new.target.name;\n this.code = opts.code;\n this.transport = opts.transport;\n this.retriable = opts.retriable ?? false;\n this.hint = opts.hint;\n this.input = opts.input;\n }\n\n /** Attach/merge the failing input (the CLI echoes this back). */\n withInput(input: Record<string, unknown>): this {\n this.input = { ...this.input, ...input };\n return this;\n }\n\n /** The structured body for JSON output and stderr. */\n body(): AnkerErrorBody {\n return {\n code: this.code,\n message: this.message,\n ...(this.transport ? { transport: this.transport } : {}),\n retriable: this.retriable,\n ...(this.hint ? { hint: this.hint } : {}),\n ...(this.input ? { input: this.input } : {}),\n };\n }\n\n toJSON(): { error: AnkerErrorBody } {\n return { error: this.body() };\n }\n}\n\n/** Exit 2 — bad/missing arguments. Thrown mostly at the CLI boundary. */\nexport class UsageError extends AnkerError {\n override readonly exitCode = 2;\n constructor(opts: Omit<AnkerErrorOptions, \"code\"> & { code?: string }) {\n super({ code: \"usage\", retriable: false, ...opts });\n }\n}\n\n/** Exit 3 — login required/expired/captcha. */\nexport class AuthError extends AnkerError {\n override readonly exitCode = 3;\n constructor(opts: Omit<AnkerErrorOptions, \"code\"> & { code?: string }) {\n super({ code: \"auth_required\", transport: \"https\", retriable: false, ...opts });\n }\n}\n\n/** Exit 4 — printer not found on the account / none selected. */\nexport class PrinterNotFoundError extends AnkerError {\n override readonly exitCode = 4;\n constructor(opts: Omit<AnkerErrorOptions, \"code\"> & { code?: string }) {\n super({ code: \"printer_not_found\", retriable: false, ...opts });\n }\n}\n\n/** Exit 5 — connectivity/timeout. Always retriable (transient). */\nexport class TimeoutError extends AnkerError {\n override readonly exitCode = 5;\n constructor(opts: Omit<AnkerErrorOptions, \"code\" | \"retriable\"> & { code?: string }) {\n super({ code: \"timeout\", retriable: true, ...opts });\n }\n}\n\n/** Exit 6 — the chosen op needs a transport the printer isn't reachable on. */\nexport class TransportUnavailableError extends AnkerError {\n override readonly exitCode = 6;\n constructor(opts: Omit<AnkerErrorOptions, \"code\"> & { code?: string }) {\n super({ code: \"transport_unavailable\", retriable: false, ...opts });\n }\n}\n\n/** Exit 7 — the printer rejected the request (gcode/job refused). */\nexport class PrinterRejectedError extends AnkerError {\n override readonly exitCode = 7;\n constructor(opts: Omit<AnkerErrorOptions, \"code\"> & { code?: string }) {\n super({ code: \"printer_rejected\", retriable: false, ...opts });\n }\n}\n\n/** Map any thrown value to an {@link AnkerError} (wrapping unknowns as exit 1). */\nexport function toAnkerError(err: unknown): AnkerError {\n if (err instanceof AnkerError) return err;\n if (err instanceof Error) {\n return new AnkerError({ code: \"internal_error\", message: err.message, cause: err });\n }\n return new AnkerError({ code: \"internal_error\", message: String(err) });\n}\n","/**\n * MQTT `commandType` enum and notice identifiers, extracted from the reference\n * `libflagship/mqtt.py` and the live captures documented in the brief (§5).\n *\n * The firmware speaks numeric command types; we name the ones that matter and\n * leave the rest reachable through the raw `send`/numeric value.\n */\n\n/** MQTT command/notice types (`commandType` field). Values are decimal. */\nexport enum MqttCommandType {\n EVENT_NOTIFY = 0x03e8, // 1000\n PRINT_SCHEDULE = 0x03e9, // 1001\n FIRMWARE_VERSION = 0x03ea, // 1002\n NOZZLE_TEMP = 0x03eb, // 1003 — 1/100 °C\n HOTBED_TEMP = 0x03ec, // 1004 — 1/100 °C\n FAN_SPEED = 0x03ed, // 1005\n PRINT_SPEED = 0x03ee, // 1006\n AUTO_LEVELING = 0x03ef, // 1007\n PRINT_CONTROL = 0x03f0, // 1008\n FILE_LIST_REQUEST = 0x03f1, // 1009\n APP_QUERY_STATUS = 0x0403, // 1027\n ONLINE_NOTIFY = 0x0404, // 1028\n RECOVER_FACTORY = 0x0405, // 1029\n BREAK_POINT = 0x040f, // 1039\n MODEL_LAYER = 0x041c, // 1052\n GCODE_COMMAND = 0x0413, // 1043 — raw gcode in/out\n}\n\n/** Notice `commandType` values seen streaming on `.../notice` (decimal §5). */\nexport enum NoticeType {\n EVENT_NOTIFY = 1000,\n PRINT_SCHEDULE = 1001,\n NOZZLE_TEMP = 1003,\n HOTBED_TEMP = 1004,\n PRINT_SPEED = 1006,\n MODEL_LAYER = 1052,\n}\n\n/**\n * `PRINT_CONTROL` (0x03f0) `value` for job control. Reverse-engineered live\n * against an M5 (2026-06-07): each value was sent and confirmed by watching the\n * printer's authoritative job state and heater targets.\n */\nexport enum PrintControl {\n PAUSE = 2, // → state 2 (paused), progress freezes\n RESUME = 3, // → state 1 (printing)\n STOP = 4, // → state 0 (idle), nozzle + bed targets drop to 0\n}\n\n/** Job state reported by `EVENT_NOTIFY` (0x03e8) with `subType: 1`. */\nexport enum PrintState {\n IDLE = 0,\n PRINTING = 1,\n PAUSED = 2,\n}\n","/**\n * Gcode metadata transcoder (brief §4A, optional `print --fix-metadata`).\n *\n * Third-party slicers (OrcaSlicer/PrusaSlicer) print correctly on the M5, but\n * the firmware's headline ETA/filament display reads from Anker-proprietary\n * header comments those slicers don't emit. AnkerMake Studio is Cura-based and\n * writes a `;TIME:<seconds>s` header; OrcaSlicer writes a human-readable\n * `; estimated printing time (normal mode) = 1h 39m 52s` instead.\n *\n * This is a TRANSCODER, not a calculator: the slicer already embedded its own\n * estimate; we only rewrite it into the keys/units Anker firmware reads. Motion,\n * temps, and `M73` progress are left untouched (they already work cross-slicer).\n * We never mutate the user's file — callers transcode a copy in the upload path.\n */\n\nexport interface TranscodeResult {\n /** The (possibly) rewritten gcode. */\n content: string;\n /** True if any Anker header line was injected. */\n changed: boolean;\n /** What was transcoded (for logging/reporting). */\n injected: {\n timeSeconds?: number;\n filamentMm?: number;\n filamentGrams?: number;\n };\n}\n\n/** Parse a duration like `1d 2h 39m 52s` / `1h 39m 52s` / `99m` into seconds. */\nexport function parseDurationToSeconds(text: string): number | undefined {\n let total = 0;\n let any = false;\n for (const [, value, unit] of text.matchAll(/(\\d+(?:\\.\\d+)?)\\s*([dhms])/gi)) {\n const n = Number(value);\n if (Number.isNaN(n)) continue;\n any = true;\n total += n * { d: 86400, h: 3600, m: 60, s: 1 }[unit!.toLowerCase() as \"d\" | \"h\" | \"m\" | \"s\"];\n }\n return any ? Math.round(total) : undefined;\n}\n\n/** Heuristic slicer detection from header comments. */\nexport function detectSlicer(gcode: string): \"ankermake\" | \"orca\" | \"prusa\" | \"unknown\" {\n const head = gcode.slice(0, 4096).toLowerCase();\n if (/;time:\\s*\\d/.test(head) || head.includes(\"ankermake\") || head.includes(\"eufymake\")) {\n return \"ankermake\";\n }\n if (head.includes(\"orcaslicer\") || head.includes(\"orca_slicer\")) return \"orca\";\n if (head.includes(\"prusaslicer\")) return \"prusa\";\n // Fall back to the Orca/Prusa-style estimate comment.\n if (/estimated printing time/.test(head)) return \"orca\";\n return \"unknown\";\n}\n\n/** True when the firmware would already read a correct headline ETA. */\nexport function hasAnkerTimeHeader(gcode: string): boolean {\n return /^;TIME:\\s*\\d+/m.test(gcode);\n}\n\nfunction findNumber(gcode: string, re: RegExp): number | undefined {\n const m = re.exec(gcode);\n if (!m) return undefined;\n const n = Number(m[1]);\n return Number.isNaN(n) ? undefined : n;\n}\n\n/**\n * Transcode a third-party slicer's embedded estimates into Anker/Cura header\n * comments. No-op (returns the input unchanged) when the file already carries a\n * `;TIME:` header — i.e. it was sliced natively.\n */\nexport function transcodeMetadata(gcode: string): TranscodeResult {\n if (hasAnkerTimeHeader(gcode)) {\n return { content: gcode, changed: false, injected: {} };\n }\n\n const injected: TranscodeResult[\"injected\"] = {};\n const headerLines: string[] = [];\n\n const timeMatch = /;\\s*estimated printing time \\(normal mode\\)\\s*=\\s*(.+)/i.exec(gcode);\n if (timeMatch) {\n const seconds = parseDurationToSeconds(timeMatch[1]!);\n if (seconds !== undefined) {\n injected.timeSeconds = seconds;\n headerLines.push(`;TIME:${seconds}s`);\n }\n }\n\n const mm = findNumber(gcode, /;\\s*filament used \\[mm\\]\\s*=\\s*([\\d.]+)/i);\n if (mm !== undefined) {\n injected.filamentMm = mm;\n headerLines.push(`;Filament used: ${mm}mm`);\n }\n\n const grams = findNumber(gcode, /;\\s*total filament used \\[g\\]\\s*=\\s*([\\d.]+)/i);\n if (grams !== undefined) {\n injected.filamentGrams = grams;\n headerLines.push(`;Filament weight: ${grams}g`);\n }\n\n if (headerLines.length === 0) {\n return { content: gcode, changed: false, injected };\n }\n\n // Cura/Anker writes these at the very top of the file.\n const content = `${headerLines.join(\"\\n\")}\\n${gcode}`;\n return { content, changed: true, injected };\n}\n","/**\n * Live MQTT transport (brief §5, §6, §6A).\n *\n * Connects to the Anker cloud broker over TLS (pinned CA), subscribes to the\n * printer's notice/reply topics, and drives the gcode request/response cycle.\n * The M5 returns each reply as a single ~512-byte serial-buffer snapshot (not a\n * multi-frame stream — see protocol/gcode.ts), so we collect the reply, settle\n * past the firmware's leading double-`ok`, and hand it to the parser, which\n * flags `truncated` when the snapshot is partial.\n *\n * The transport never writes to stdout/stderr; diagnostics go through an\n * injected `log` callback so the SDK stays I/O-free.\n */\nimport mqtt, { type MqttClient } from \"mqtt\";\nimport { MqttCommandType } from \"../protocol/commands.js\";\nimport {\n gcodeHasTerminalOk,\n parseGcodeResult,\n reassembleRaw,\n type GcodeResult,\n} from \"../protocol/gcode.js\";\nimport { normalizeStatus, type PrinterStatus, type RawNotice } from \"../protocol/status.js\";\nimport { TimeoutError } from \"../errors.js\";\nimport { ANKERMAKE_MQTT_CA } from \"./certs.js\";\nimport { packMqttMessage, parseMqttMessage } from \"./mqttframe.js\";\n\nexport interface MqttClientOptions {\n sn: string;\n /** Per-printer AES key (decoded from the hex `mqtt_key`). */\n key: Buffer;\n host: string;\n port?: number;\n username: string;\n password: string;\n guid?: string;\n /** Override the pinned CA (advanced). */\n ca?: string;\n /** Disable TLS verification (testing only). */\n insecure?: boolean;\n /** Diagnostic sink (defaults to no-op; the CLI routes this to stderr). */\n log?: (msg: string) => void;\n}\n\nexport interface GcodeWaitOptions {\n /** Hard ceiling before giving up (latency-class aware; default 10s). */\n timeoutMs?: number;\n /** Quiet period with no new frame that signals completion (default 600ms). */\n quietMs?: number;\n /** When false, fire-and-forget: publish and return without collecting. */\n wait?: boolean;\n}\n\nconst COMMAND_REPLY = (sn: string): string => `/phone/maker/${sn}/command/reply`;\nconst QUERY_REPLY = (sn: string): string => `/phone/maker/${sn}/query/reply`;\nconst NOTICE = (sn: string): string => `/phone/maker/${sn}/notice`;\nconst COMMAND_TOPIC = (sn: string): string => `/device/maker/${sn}/command`;\nconst QUERY_TOPIC = (sn: string): string => `/device/maker/${sn}/query`;\n\ntype NoticeHandler = (notice: RawNotice) => void;\n\nfunction randomGuid(): string {\n // RFC4122-ish; uniqueness is all that matters for the device GUID field.\n const h = (n: number): string =>\n Math.floor(Math.random() * 16 ** n)\n .toString(16)\n .padStart(n, \"0\");\n return `${h(8)}-${h(4)}-4${h(3)}-${h(4)}-${h(8)}${h(4)}`;\n}\n\nexport class AnkerMqttClient {\n private client?: MqttClient;\n private readonly guid: string;\n private readonly log: (msg: string) => void;\n private readonly noticeHandlers = new Set<NoticeHandler>();\n private readonly replyHandlers = new Set<(obj: RawNotice) => void>();\n private readonly latestNotices = new Map<number, RawNotice>();\n private gcodeLock: Promise<unknown> = Promise.resolve();\n\n constructor(private readonly opts: MqttClientOptions) {\n this.guid = opts.guid ?? randomGuid();\n this.log = opts.log ?? (() => {});\n }\n\n get connected(): boolean {\n return this.client?.connected ?? false;\n }\n\n async connect(timeoutMs = 30000): Promise<void> {\n const { host, port = 8789, username, password, sn } = this.opts;\n this.log(`mqtt: connecting to ${host}:${port} as ${username}`);\n const client = await mqtt.connectAsync(`mqtts://${host}:${port}`, {\n username,\n password,\n ca: this.opts.ca ?? ANKERMAKE_MQTT_CA,\n rejectUnauthorized: !this.opts.insecure,\n connectTimeout: timeoutMs,\n reconnectPeriod: 0,\n });\n this.client = client;\n client.on(\"message\", (topic, payload) => this.onMessage(topic, payload));\n await client.subscribeAsync([COMMAND_REPLY(sn), QUERY_REPLY(sn), NOTICE(sn)]);\n this.log(\"mqtt: connected and subscribed\");\n }\n\n async disconnect(): Promise<void> {\n await this.client?.endAsync();\n this.client = undefined;\n }\n\n private onMessage(_topic: string, payload: Buffer): void {\n let msg;\n try {\n msg = parseMqttMessage(payload, this.opts.key);\n } catch (err) {\n this.log(`mqtt: failed to decode message: ${(err as Error).message}`);\n return;\n }\n const objects = Array.isArray(msg.payload) ? msg.payload : [msg.payload];\n for (const obj of objects) {\n if (obj && typeof obj === \"object\") {\n const notice = obj as RawNotice;\n if (typeof notice.commandType === \"number\")\n this.latestNotices.set(notice.commandType, notice);\n for (const h of this.replyHandlers) h(notice);\n for (const h of this.noticeHandlers) h(notice);\n }\n }\n }\n\n private publish(topic: string, payload: unknown): void {\n if (!this.client) throw new Error(\"MQTT client not connected\");\n const packed = packMqttMessage({ guid: this.guid, payload, key: this.opts.key });\n this.client.publish(topic, packed);\n }\n\n /** Subscribe to streaming notices. Returns an unsubscribe function. */\n onNotice(handler: NoticeHandler): () => void {\n this.noticeHandlers.add(handler);\n return () => this.noticeHandlers.delete(handler);\n }\n\n /** Publish a raw command payload (escape hatch for un-modeled commands). */\n command(payload: Record<string, unknown>): void {\n this.publish(COMMAND_TOPIC(this.opts.sn), payload);\n }\n\n /** Publish a raw query payload. */\n query(payload: Record<string, unknown>): void {\n this.publish(QUERY_TOPIC(this.opts.sn), payload);\n }\n\n /**\n * Send a single gcode command and return the parsed response (§6). The result\n * carries `truncated` when the firmware's snapshot was partial. Serialized:\n * only one gcode is in flight at a time.\n */\n async gcode(command: string, opts: GcodeWaitOptions = {}): Promise<GcodeResult> {\n const run = this.gcodeLock.then(() => this.gcodeOnce(command, opts));\n // Keep the lock chain alive even if this call rejects.\n this.gcodeLock = run.catch(() => undefined);\n return run;\n }\n\n private gcodeOnce(command: string, opts: GcodeWaitOptions): Promise<GcodeResult> {\n const { timeoutMs = 10000, quietMs = 600, wait = true } = opts;\n const started = Date.now();\n\n const payload = {\n commandType: MqttCommandType.GCODE_COMMAND,\n cmdData: command,\n cmdLen: Buffer.byteLength(command, \"utf8\"),\n };\n\n if (!wait) {\n this.command(payload);\n return Promise.resolve(\n parseGcodeResult(command, [], { durationMs: Date.now() - started, timedOut: false }),\n );\n }\n\n return new Promise<GcodeResult>((resolve) => {\n const chunks: string[] = [];\n let lastFrameAt = Date.now();\n let settled = false;\n\n const finish = (timedOut: boolean): void => {\n if (settled) return;\n settled = true;\n clearInterval(ticker);\n this.replyHandlers.delete(collector);\n resolve(parseGcodeResult(command, chunks, { durationMs: Date.now() - started, timedOut }));\n };\n\n const collector = (obj: RawNotice): void => {\n if (obj.commandType !== MqttCommandType.GCODE_COMMAND) return;\n const chunk = obj.resData;\n if (typeof chunk === \"string\") {\n chunks.push(chunk);\n lastFrameAt = Date.now();\n }\n };\n\n this.replyHandlers.add(collector);\n this.command(payload);\n\n // The M5 emits a leading/double `ok` before the real echo output, so we\n // never stop on the *first* `ok` (that truncates to `echo:Ad`). Instead:\n // a TRAILING `ok` lets us finish after a short grace for any straggler\n // frame; otherwise the longer quiet period is the floor; the hard timeout\n // is the ceiling.\n const okGraceMs = Math.min(quietMs, 250);\n const ticker = setInterval(\n () => {\n const now = Date.now();\n const idle = now - lastFrameAt;\n if (now - started >= timeoutMs) {\n finish(true); // hard timeout → timedOut = true\n } else if (chunks.length > 0) {\n const trailingOk = gcodeHasTerminalOk(reassembleRaw(chunks));\n if (trailingOk && idle >= okGraceMs)\n finish(false); // settled after a terminal ok\n else if (idle >= quietMs) finish(false); // quiet-period completion\n }\n },\n Math.max(25, Math.floor(okGraceMs / 2)),\n );\n });\n }\n\n /**\n * Snapshot current printer status by normalizing the latest notice of each\n * type. Optionally nudges the printer with an APP_QUERY_STATUS query and waits\n * briefly for fresh telemetry.\n */\n async getStatus(opts: { refresh?: boolean; waitMs?: number } = {}): Promise<PrinterStatus> {\n const { refresh = true, waitMs = 1200 } = opts;\n if (refresh && this.client) {\n this.query({ commandType: MqttCommandType.APP_QUERY_STATUS });\n await new Promise((r) => setTimeout(r, waitMs));\n }\n return normalizeStatus([...this.latestNotices.values()]);\n }\n\n /** Throw a structured timeout error (used by waiters). */\n static timeout(message: string, hint?: string): never {\n throw new TimeoutError({ message, transport: \"mqtt\", hint });\n }\n}\n","/**\n * Gcode request/response handling (brief §6).\n *\n * IMPORTANT — corrected against real M5 hardware (see the project memory). The\n * brief assumed a gcode reply arrives across multiple `0x0413` frames to be\n * reassembled. It does not: each send yields exactly ONE reply whose `resData`\n * is a point-in-time snapshot of the firmware's ~512-byte serial ring buffer,\n * and the snapshot is RACY. Short replies (M105) come back whole; replies that\n * exceed the window are capped at 512 bytes; and a reply caught mid-write\n * truncates early with a `+ringbuf:<a>,512,<b>` marker (the buffer reporting its\n * own state) — this is the real `echo:Ad` bug.\n *\n * So we still concatenate whatever chunks the transport collected, strip ANSI,\n * and parse — but the key honesty fix is `truncated`: rather than silently\n * handing back a partial line (as the reference did), we DETECT truncation and\n * flag it, so a caller never mistakes `echo:Ad` for a complete reply.\n *\n * This module is pure: the transport collects the reply and decides completion;\n * these functions turn the collected chunks into a result, keeping the parser\n * fully unit-testable against the observed captures.\n */\n\nexport interface GcodeResult {\n /** Echoed input command. */\n command: string;\n /** FULL reassembled text, ANSI-free, all frames concatenated in order. */\n raw: string;\n /** `raw` split on newlines, trimmed, with empty lines removed. */\n lines: string[];\n /** A terminal `ok` line was seen. */\n ok: boolean;\n /** False iff an `echo:Unknown command` line was seen. */\n recognized: boolean;\n /** Parsed `echo:Key=Value` / `Key:Value` pairs, e.g. `{ \"Advance K\": \"0.00\" }`. */\n fields: Record<string, string>;\n /** Parsed Marlin report lines keyed by code, e.g. `{ \"M900\": \"K0.00\" }`. */\n reports: Record<string, string>;\n /** Wall-clock time spent collecting the response. */\n durationMs: number;\n /** Completion signal never arrived within the timeout. */\n timedOut: boolean;\n /**\n * The reply is incomplete: it hit the firmware's ~512-byte snapshot window, or\n * carries a `+ringbuf:` marker showing the serial buffer was mid-write. When\n * true, `raw`/`fields`/`reports` may be partial — do not trust them as the\n * full command output. (The reference silently returned these as if complete.)\n */\n truncated: boolean;\n /** How many reply chunks were collected (diagnostic; usually 1 — see header). */\n frames: number;\n}\n\n// The firmware's gcode reply is a snapshot of a ~512-byte serial ring buffer.\nconst GCODE_WINDOW_BYTES = 512;\nconst RINGBUF_RE = /\\+ringbuf:\\s*\\d+,\\s*\\d+,\\s*\\d+/;\n\n// ANSI escape sequences (CSI/SGR etc.). The ESC (0x1b) and single-byte CSI\n// (0x9b) introducers are assembled via fromCharCode so no control byte lives in\n// source (and no-control-regex stays quiet).\nconst ESC = String.fromCharCode(0x1b);\nconst CSI = String.fromCharCode(0x9b);\nconst ANSI_RE = new RegExp(\n `[${ESC}${CSI}][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PR-TZcf-ntqry=><~]`,\n \"g\",\n);\n\nexport function stripAnsi(text: string): string {\n return text.replace(ANSI_RE, \"\");\n}\n\n/** Concatenate reply chunks in arrival order; strip ANSI; normalize newlines. */\nexport function reassembleRaw(chunks: readonly string[]): string {\n return stripAnsi(chunks.join(\"\")).replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n}\n\n/** Split reassembled text into trimmed, non-empty lines. */\nexport function splitLines(raw: string): string[] {\n return raw\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n}\n\n/**\n * Does the reassembled text END with a terminal `ok`? Marlin terminates a\n * command's output with an `ok` line (sometimes carrying data, e.g. M105's\n * `ok T:...`). The check is on the LAST non-empty line, not any line: the M5\n * firmware emits a leading `ok` (and a double-`ok`) BEFORE the real output (e.g.\n * `ok\\n\\nok\\n\\n+ringbuf:...\\necho:Advance K=0.00\\nok`), so matching any `ok`\n * would stop reassembly early and truncate the reply — the `echo:Ad` bug. Used\n * by the transport's completion detection.\n */\nexport function gcodeHasTerminalOk(raw: string): boolean {\n // Ignore trailing firmware buffer-state noise (`+ringbuf:...` / a lone `+`)\n // that the M5 appends AFTER the terminal `ok`, which would otherwise hide it.\n const lines = splitLines(raw).filter((l) => !/^\\+/.test(l));\n const last = lines[lines.length - 1];\n return last !== undefined && (/^ok\\b/i.test(last) || last.toLowerCase() === \"ok\");\n}\n\n/** Was an \"unknown command\" rejection seen? */\nfunction hasUnknownCommand(lines: readonly string[]): boolean {\n return lines.some((l) => /echo:\\s*Unknown command/i.test(l));\n}\n\n// A Marlin report line: a leading G/M code followed by its parameters.\nconst REPORT_RE = /^([GM]\\d+)\\b\\s*(.*)$/;\n\n/**\n * Extract `KEY:VALUE` tokens from an M115-style line where several pairs share\n * one line and values may contain spaces (e.g. `FIRMWARE_NAME:Marlin V8111 ...\n * EXTRUDER_COUNT:1`). Boundaries are a space immediately preceding an uppercase\n * token + colon.\n */\nfunction parseKeyColonTokens(line: string, out: Record<string, string>): boolean {\n if (!/^[A-Za-z_][A-Za-z0-9_]*:/.test(line)) return false;\n // Split before ` UPPER_TOKEN:` boundaries, keeping each `KEY:VALUE` together.\n const parts = line.split(/\\s+(?=[A-Z][A-Z0-9_]*:)/);\n let matched = false;\n for (const part of parts) {\n const idx = part.indexOf(\":\");\n if (idx <= 0) continue;\n const key = part.slice(0, idx).trim();\n const value = part.slice(idx + 1).trim();\n if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {\n out[key] = value;\n matched = true;\n }\n }\n return matched;\n}\n\n/**\n * Parse the collected reply chunks for one command into a complete GcodeResult.\n *\n * @param command the gcode that was sent (echoed back)\n * @param chunks `resData` strings from each reply frame, in arrival order\n * @param meta timing/diagnostic info supplied by the transport\n */\nexport function parseGcodeResult(\n command: string,\n chunks: readonly string[],\n meta: { durationMs: number; timedOut: boolean },\n): GcodeResult {\n const raw = reassembleRaw(chunks);\n const lines = splitLines(raw);\n const fields: Record<string, string> = {};\n const reports: Record<string, string> = {};\n\n // Truncation: a `+ringbuf:` marker (snapshot taken mid-write) or hitting the\n // ~512-byte window (more output existed than the snapshot could hold).\n const truncated = RINGBUF_RE.test(raw) || chunks.join(\"\").length >= GCODE_WINDOW_BYTES;\n\n for (const line of lines) {\n // Strip a leading `echo:` and any indentation Marlin adds inside reports.\n const body = line.replace(/^echo:\\s*/i, \"\").trim();\n\n // 1. Report line: starts with a G/M code (e.g. `M900 K0.00`, M503 dumps).\n const report = REPORT_RE.exec(body);\n if (report && !body.includes(\"=\")) {\n reports[report[1]!] = report[2]!.trim();\n continue;\n }\n\n // 2. `Key=Value` field (e.g. `Advance K=0.00`).\n if (body.includes(\"=\") && !/^[A-Za-z_][A-Za-z0-9_]*:/.test(body)) {\n const eq = body.indexOf(\"=\");\n const key = body.slice(0, eq).trim();\n const value = body.slice(eq + 1).trim();\n if (key) {\n fields[key] = value;\n continue;\n }\n }\n\n // 3. `KEY:VALUE` token(s) (e.g. M115's FIRMWARE_NAME / EXTRUDER_COUNT).\n parseKeyColonTokens(body, fields);\n }\n\n return {\n command,\n raw,\n lines,\n ok: gcodeHasTerminalOk(raw),\n recognized: !hasUnknownCommand(lines),\n fields,\n reports,\n durationMs: meta.durationMs,\n timedOut: meta.timedOut,\n truncated,\n frames: chunks.length,\n };\n}\n","/**\n * Printer status/telemetry normalization (brief §5 + §4A).\n *\n * Notices stream continuously over MQTT `.../notice`. They carry raw firmware\n * units — temperatures in 1/100 °C, progress in 1/100 % — which we normalize so\n * callers never touch raw values. Crucially, for third-party-sliced gcode the\n * firmware's headline ETA is garbage (§4A); we detect that and mark the ETA\n * unreliable rather than surfacing a bogus 20,000-hour estimate.\n */\n\nimport { NoticeType } from \"./commands.js\";\n\nexport type JobState = \"idle\" | \"printing\" | \"paused\" | \"complete\" | \"failed\" | \"cancelled\";\n\nexport interface PrinterStatus {\n nozzle: { current: number; target: number }; // °C\n bed: { current: number; target: number }; // °C\n job?: {\n name: string;\n state: JobState;\n progressPct: number; // 0..100\n layer: number;\n totalLayers: number;\n etaSeconds?: number; // omitted when unreliable (§4A)\n etaReliable?: boolean;\n filamentUsed?: number;\n filamentUnit?: string;\n speedMmS?: number;\n speedFactorPct?: number;\n };\n raw: Record<string, unknown>; // original notice payloads — escape hatch\n}\n\n/** A raw notice payload: at minimum a `commandType`, plus arbitrary fields. */\nexport type RawNotice = Record<string, unknown> & { commandType?: number };\n\nconst centiToC = (v: number): number => Math.round((v / 100) * 100) / 100;\nconst num = (v: unknown): number | undefined =>\n typeof v === \"number\" ? v : typeof v === \"string\" && v.trim() !== \"\" ? Number(v) : undefined;\n\n// A remaining/total time this large almost certainly comes from un-populated\n// Anker metadata in third-party gcode (the observed bug showed ~20,000 hours).\nconst SANE_ETA_SECONDS = 60 * 60 * 24 * 30; // 30 days\n\n/**\n * Decide whether a `print_schedule` notice's ETA is trustworthy. Native\n * AnkerMake/eufyMake gcode carries the proprietary time metadata the firmware\n * needs; third-party slicers (OrcaSlicer/PrusaSlicer) do not, so the firmware\n * emits an inconsistent `time` vs `totalTime` and an absurd remaining time.\n */\nexport function isEtaReliable(schedule: RawNotice): boolean {\n const totalTime = num(schedule.totalTime);\n const time = num(schedule.time);\n const left = num(schedule.startLeftTime);\n if (totalTime === undefined || totalTime <= 0) return false;\n // `time` should never exceed the total for a real print; if it does, the\n // field was never populated correctly (the §4A tell: time≫totalTime).\n if (time !== undefined && time > totalTime) return false;\n if (left !== undefined && (left < 0 || left > SANE_ETA_SECONDS)) return false;\n return true;\n}\n\nfunction deriveState(\n schedule: RawNotice | undefined,\n progressPct: number,\n eventState?: number,\n): JobState {\n // Authoritative: EVENT_NOTIFY subType:1 carries the firmware job state\n // (0=idle, 1=printing, 2=paused), confirmed live. Prefer it when present.\n if (eventState === 0) return progressPct >= 100 ? \"complete\" : \"idle\";\n if (eventState === 1) return progressPct >= 100 ? \"complete\" : \"printing\";\n if (eventState === 2) return \"paused\";\n\n const hint = schedule?.state;\n if (typeof hint === \"string\") {\n const h = hint.toLowerCase();\n if ([\"idle\", \"printing\", \"paused\", \"complete\", \"failed\", \"cancelled\"].includes(h)) {\n return h as JobState;\n }\n }\n if (!schedule || !schedule.name) return \"idle\";\n if (progressPct >= 100) return \"complete\";\n return \"printing\";\n}\n\n/**\n * Fold a set of notice payloads into a normalized {@link PrinterStatus}.\n * The latest notice of each type wins. `etaReliableOverride` lets a caller that\n * already knows the file is third-party force the ETA to be marked unreliable.\n */\nexport function normalizeStatus(\n notices: readonly RawNotice[],\n opts: { etaReliableOverride?: boolean } = {},\n): PrinterStatus {\n const latest = new Map<number, RawNotice>();\n for (const n of notices) {\n if (typeof n.commandType === \"number\") latest.set(n.commandType, n);\n }\n\n const nozzleN = latest.get(NoticeType.NOZZLE_TEMP);\n const bedN = latest.get(NoticeType.HOTBED_TEMP);\n const layerN = latest.get(NoticeType.MODEL_LAYER);\n const speedN = latest.get(NoticeType.PRINT_SPEED);\n const schedule = latest.get(NoticeType.PRINT_SCHEDULE);\n // EVENT_NOTIFY subType:1 → authoritative job state (0=idle,1=printing,2=paused).\n const eventN = latest.get(NoticeType.EVENT_NOTIFY);\n const eventState = num(eventN?.subType) === 1 ? num(eventN?.value) : undefined;\n\n const raw: Record<string, unknown> = {};\n for (const [type, payload] of latest) raw[String(type)] = payload;\n\n const status: PrinterStatus = {\n nozzle: {\n current: centiToC(num(nozzleN?.currentTemp) ?? 0),\n target: centiToC(num(nozzleN?.targetTemp) ?? 0),\n },\n bed: {\n current: centiToC(num(bedN?.currentTemp) ?? 0),\n target: centiToC(num(bedN?.targetTemp) ?? 0),\n },\n raw,\n };\n\n if (schedule) {\n const progressPct = Math.round(((num(schedule.progress) ?? 0) / 100) * 100) / 100;\n const reliable = opts.etaReliableOverride ?? isEtaReliable(schedule);\n const left = num(schedule.startLeftTime);\n\n status.job = {\n name: typeof schedule.name === \"string\" ? schedule.name : \"\",\n state: deriveState(schedule, progressPct, eventState),\n progressPct,\n layer: num(layerN?.real_print_layer) ?? 0,\n totalLayers: num(layerN?.total_layer) ?? 0,\n etaReliable: reliable,\n ...(reliable && left !== undefined ? { etaSeconds: left } : {}),\n ...(num(schedule.filamentUsed) !== undefined\n ? { filamentUsed: num(schedule.filamentUsed) }\n : {}),\n ...(typeof schedule.filamentUnit === \"string\" ? { filamentUnit: schedule.filamentUnit } : {}),\n ...(num(speedN?.value) !== undefined ? { speedMmS: num(speedN?.value) } : {}),\n ...(num(schedule.realSpeed) !== undefined ? { speedFactorPct: num(schedule.realSpeed) } : {}),\n };\n }\n\n return status;\n}\n","/**\n * The AnkerMake MQTT broker presents a self-signed wildcard certificate for\n * `*.ankermake.com` (issued 2022, valid to 2122). It is not chained to a public\n * root, so TLS verification requires pinning this CA. Embedded verbatim from the\n * reference's `ssl/ankermake-mqtt.crt` so the SDK can verify the broker without\n * shipping a separate file.\n */\nexport const ANKERMAKE_MQTT_CA = `-----BEGIN CERTIFICATE-----\nMIIDwTCCAqmgAwIBAgIJAKrbZvWARI3BMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNV\nBAYTAkNOMREwDwYDVQQIDAhTaGVuemhlbjERMA8GA1UEBwwIU2hlbnpoZW4xEjAQ\nBgNVBAoMCWFua2VybWFrZTESMBAGA1UECwwJYW5rZXJtYWtlMRgwFgYDVQQDDA8q\nLmFua2VybWFrZS5jb20wIBcNMjIwNjE3MDMwNzU5WhgPMjEyMjA1MjQwMzA3NTla\nMHUxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGVuemhlbjERMA8GA1UEBwwIU2hl\nbnpoZW4xEjAQBgNVBAoMCWFua2VybWFrZTESMBAGA1UECwwJYW5rZXJtYWtlMRgw\nFgYDVQQDDA8qLmFua2VybWFrZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQC8JWJzdVJFqrarK5oMCF8nI5QZ2nebs9df6CQHuSZCOmGCav5sDDFt\n5IGhQ6G44++YNexC10kwxy10fOzIT6cZWnQrYQPBfS0y7G+yu/GPe9vXMWwkIcWv\nhg8xAO+/m5C/QAj4BOVTXVl5spuBGX644P3eErV+tUDwb1U2K6mMzmaJ7SZqkmiw\nQKfTK1KxH7oczcxjDtdbNdtpa1Rm3IUCCI2eAOQTlDHlKGGM2T+e6qQRCUQYqkiY\njG+3ugTzHMe6FMzOB1EjG0bZDemQwgUdBJexLgxrJe4jsVcuP75DfrV0NL/Drrmt\nuJax3V4tu5Yx1RQCWqGTNPOahpS+qD+NAgMBAAGjUjBQMA4GA1UdDwEB/wQEAwID\niDATBgNVHSUEDDAKBggrBgEFBQcDATApBgNVHREEIjAggg1hbmtlcm1ha2UuY29t\ngg8qLmFua2VybWFrZS5jb20wDQYJKoZIhvcNAQELBQADggEBALF/VDyZ21IdFejE\nawLriK+Xo78k1yqf2YKWYSDMEJPXXHfbkHZTU0IL+K9kToN19sObuWPA1oE2iyKp\nh4nKVDjy56Ntgt5lXeSTN08jlD0PzuuGfzPVxMrky8sp14pFT+Kw2HOEMLU6Hxj0\nWjpprKRbl1oI8JoksYNzCSelIItokA8CI3/p1j5FyWxok99sVvNUfjG9iaV74Nuh\nkY/1nm0T0aMPZKpcS0xS0JwA0tsySdDJP5t1KgmDa5D0hIhXuAJWGwUvg15vSyme\nbk3IO48Nh8QOG8PwGebPus1nnvKCbG6+iJaWp/PqSqNCzx/Nht+Tfi413dIc3exF\nLX0ZR20=\n-----END CERTIFICATE-----`;\n","/**\n * Tiny binary reader/writer, mirroring the struct helpers in the reference's\n * `libflagship/amtypes.py`. Big-endian is the default for the bare `u8/u16/u32`\n * helpers (matching `u8 = u8be` etc.); little-endian variants are explicit.\n *\n * Pure data manipulation — no I/O.\n */\n\nexport class BufReader {\n private offset = 0;\n\n constructor(private readonly buf: Buffer) {}\n\n get remaining(): number {\n return this.buf.length - this.offset;\n }\n\n private take(n: number): Buffer {\n if (this.offset + n > this.buf.length) {\n throw new RangeError(`buffer underrun: need ${n}, have ${this.remaining}`);\n }\n const slice = this.buf.subarray(this.offset, this.offset + n);\n this.offset += n;\n return slice;\n }\n\n u8(): number {\n return this.take(1).readUInt8(0);\n }\n u16be(): number {\n return this.take(2).readUInt16BE(0);\n }\n u16le(): number {\n return this.take(2).readUInt16LE(0);\n }\n u32be(): number {\n return this.take(4).readUInt32BE(0);\n }\n u32le(): number {\n return this.take(4).readUInt32LE(0);\n }\n i32be(): number {\n return this.take(4).readInt32BE(0);\n }\n i32le(): number {\n return this.take(4).readInt32LE(0);\n }\n\n bytes(n: number): Buffer {\n return Buffer.from(this.take(n));\n }\n\n tail(): Buffer {\n return this.bytes(this.remaining);\n }\n\n /** Read `n` zero bytes, asserting they are all zero. */\n zeroes(n: number): Buffer {\n const b = this.take(n);\n for (const x of b) if (x !== 0) throw new Error(\"expected zero padding\");\n return Buffer.from(b);\n }\n\n /** Read `expected.length` bytes and assert they match (a magic signature). */\n magic(expected: Buffer): Buffer {\n const b = this.take(expected.length);\n if (!b.equals(expected)) {\n throw new Error(`bad magic: expected ${expected.toString(\"hex\")}, got ${b.toString(\"hex\")}`);\n }\n return Buffer.from(b);\n }\n\n /** Fixed-width, NUL-terminated string field of `size` bytes (last byte NUL). */\n string(size: number): string {\n const b = this.take(size);\n if (b[size - 1] !== 0) throw new Error(\"expected NUL-terminated fixed string\");\n let end = size - 1;\n // Trim at the first NUL so embedded padding is dropped.\n for (let i = 0; i < size; i++) {\n if (b[i] === 0) {\n end = i;\n break;\n }\n }\n return b.subarray(0, end).toString(\"utf8\");\n }\n\n /** IPv4 address stored as 4 little-endian bytes (reversed dotted quad). */\n ipv4(): string {\n const b = this.take(4);\n return `${b[3]}.${b[2]}.${b[1]}.${b[0]}`;\n }\n}\n\nexport class BufWriter {\n private chunks: Buffer[] = [];\n\n u8(v: number): this {\n const b = Buffer.alloc(1);\n b.writeUInt8(v & 0xff, 0);\n this.chunks.push(b);\n return this;\n }\n u16be(v: number): this {\n const b = Buffer.alloc(2);\n b.writeUInt16BE(v & 0xffff, 0);\n this.chunks.push(b);\n return this;\n }\n u16le(v: number): this {\n const b = Buffer.alloc(2);\n b.writeUInt16LE(v & 0xffff, 0);\n this.chunks.push(b);\n return this;\n }\n u32be(v: number): this {\n const b = Buffer.alloc(4);\n b.writeUInt32BE(v >>> 0, 0);\n this.chunks.push(b);\n return this;\n }\n u32le(v: number): this {\n const b = Buffer.alloc(4);\n b.writeUInt32LE(v >>> 0, 0);\n this.chunks.push(b);\n return this;\n }\n i32be(v: number): this {\n const b = Buffer.alloc(4);\n b.writeInt32BE(v | 0, 0);\n this.chunks.push(b);\n return this;\n }\n i32le(v: number): this {\n const b = Buffer.alloc(4);\n b.writeInt32LE(v | 0, 0);\n this.chunks.push(b);\n return this;\n }\n\n bytes(b: Buffer): this {\n this.chunks.push(Buffer.from(b));\n return this;\n }\n\n zeroes(n: number): this {\n this.chunks.push(Buffer.alloc(n));\n return this;\n }\n\n /** Write a fixed-width string, NUL-padded to `size` bytes. */\n string(s: string, size: number): this {\n const b = Buffer.alloc(size);\n Buffer.from(s, \"utf8\").copy(b, 0, 0, Math.min(size - 1, Buffer.byteLength(s, \"utf8\")));\n this.chunks.push(b);\n return this;\n }\n\n ipv4(addr: string): this {\n const parts = addr.split(\".\").map((x) => parseInt(x, 10) & 0xff);\n if (parts.length !== 4) throw new Error(`invalid IPv4 address: ${addr}`);\n this.chunks.push(Buffer.from([parts[3]!, parts[2]!, parts[1]!, parts[0]!]));\n return this;\n }\n\n build(): Buffer {\n return Buffer.concat(this.chunks);\n }\n}\n","/**\n * MQTT message framing, ported from the reference `libflagship/mqtt.py`.\n *\n * Each MQTT message is an `MA`-signed header followed by an AES-CBC-encrypted\n * JSON payload and a trailing XOR checksum byte. The header carries a packet\n * type (Single / MultiBegin / MultiAppend / MultiFinish), a per-printer device\n * GUID, and a timestamp. In practice each MQTT message carries one complete JSON\n * object, and a gcode reply is a single such message whose `resData` is one\n * ~512-byte serial-buffer snapshot (see protocol/gcode.ts) — not fragmentation.\n *\n * Pure framing — pack/parse round-trips are unit-tested without a broker.\n */\nimport { BufReader, BufWriter } from \"../binary.js\";\nimport { mqttAesDecrypt, mqttAesEncrypt, mqttChecksumAdd, mqttChecksumRemove } from \"../crypto.js\";\n\nexport enum MqttPktType {\n Single = 0xc0,\n MultiBegin = 0xc1,\n MultiAppend = 0xc2,\n MultiFinish = 0xc3,\n}\n\nconst SIGNATURE = Buffer.from(\"MA\", \"ascii\");\nconst M7_F = 0x46; // 'F'\n\n// Header length by `m5` magic: 2 = AnkerMake M5 (64), 1 = M5C (24).\nconst BODY_LEN: Record<number, number> = { 1: 24, 2: 64 };\n\nexport interface MqttMessage {\n packetType: MqttPktType;\n packetNum: number;\n time: number;\n deviceGuid: string;\n /** Decoded JSON payload (object). */\n payload: unknown;\n}\n\nexport interface PackOptions {\n guid: string;\n payload: unknown;\n key: Buffer;\n packetType?: MqttPktType;\n packetNum?: number;\n time?: number;\n}\n\n/** Build a wire-ready, encrypted+checksummed MQTT packet for an M5. */\nexport function packMqttMessage(opts: PackOptions): Buffer {\n const data = mqttAesEncrypt(Buffer.from(JSON.stringify(opts.payload), \"utf8\"), opts.key);\n const bodyLen = 64;\n const size = bodyLen + data.length + 1; // +1 for the checksum byte\n\n const header = new BufWriter()\n .bytes(SIGNATURE)\n .u16le(size)\n .u8(5) // m3\n .u8(1) // m4\n .u8(2) // m5 (M5)\n .u8(5) // m6\n .u8(M7_F) // m7 = 'F'\n .u8(opts.packetType ?? MqttPktType.Single)\n .u16le(opts.packetNum ?? 0)\n .u32le(opts.time ?? 0)\n .string(opts.guid, 37)\n .zeroes(11)\n .build();\n\n // header is exactly 64 bytes by construction.\n return mqttChecksumAdd(Buffer.concat([header.subarray(0, bodyLen), data]));\n}\n\n/** Parse and decrypt an inbound MQTT packet. */\nexport function parseMqttMessage(payload: Buffer, key: Buffer): MqttMessage {\n const stripped = mqttChecksumRemove(payload);\n const m5 = stripped[6];\n const bodyLen = m5 !== undefined ? BODY_LEN[m5] : undefined;\n if (bodyLen === undefined) {\n throw new Error(`unsupported MQTT message format (m5=${m5})`);\n }\n const body = stripped.subarray(0, bodyLen);\n const data = mqttAesDecrypt(stripped.subarray(bodyLen), key);\n\n const r = new BufReader(body);\n r.magic(SIGNATURE);\n r.u16le(); // size\n r.u8(); // m3\n r.u8(); // m4\n r.u8(); // m5\n r.u8(); // m6\n r.u8(); // m7\n const packetType = r.u8() as MqttPktType;\n const packetNum = r.u16le();\n let time = 0;\n let deviceGuid = \"none\";\n if (m5 === 2) {\n time = r.u32le();\n deviceGuid = r.string(37);\n }\n // remaining header bytes are padding (ignored)\n\n return { packetType, packetNum, time, deviceGuid, payload: JSON.parse(data.toString(\"utf8\")) };\n}\n","/**\n * HTTPS cloud API transport (brief §5, §8) — ported from the reference\n * `libflagship/httpapi.py` and the `anselor` `config login` flow.\n *\n * Handles email/password login (ECDH-encrypted password), profile + printer\n * list retrieval, and PPPP key fetch — assembling a full {@link AnkerConfig}.\n * Region-coded endpoints; the login captcha branch surfaces as a structured\n * {@link AuthError} (never a hang).\n */\nimport { API_HOSTS, type AnkerConfig, type AnkerPrinter, type Region } from \"../config.js\";\nimport { ppppDecodeInitstring } from \"../crypto.js\";\nimport { ecdhEncryptLoginPassword } from \"../crypto.js\";\nimport { AuthError } from \"../errors.js\";\n\ntype Json = Record<string, unknown>;\n\n/** Country codes that route to the US region (reference `guess_region`). */\nconst US_REGIONS = new Set([\"US\", \"CA\", \"MX\", \"BR\", \"AR\", \"CU\", \"BS\", \"AU\", \"NZ\"]);\n\nexport function guessRegion(countryCode: string): Region {\n return US_REGIONS.has(countryCode.toUpperCase()) ? \"us\" : \"eu\";\n}\n\nconst LOGIN_HEADERS: Record<string, string> = {\n App_name: \"anker_make\",\n App_version: \"\",\n Model_type: \"PC\",\n Os_type: \"windows\",\n Os_version: \"10sp1\",\n};\n\nexport interface LoginResult {\n auth_token: string;\n user_id: string;\n email: string;\n region: Region;\n ab_code?: string;\n}\n\nexport interface LoginOptions {\n email: string;\n password: string;\n /** 2-letter country code selecting the API region. */\n country: string;\n captchaId?: string;\n captchaAnswer?: string;\n}\n\nfunction num(v: unknown): number | undefined {\n return typeof v === \"number\" ? v : undefined;\n}\nfunction str(v: unknown, fallback = \"\"): string {\n return typeof v === \"string\" ? v : fallback;\n}\n\n/** Low-level cloud HTTPS client for a single region. */\nexport class AnkerHttpApi {\n constructor(\n readonly region: Region,\n private readonly authToken?: string,\n ) {}\n\n private base(): string {\n return `https://${API_HOSTS[this.region]}`;\n }\n\n private async request(scope: string, path: string, body?: Json, auth = false): Promise<Json> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...LOGIN_HEADERS,\n };\n if (auth) {\n if (!this.authToken) throw new AuthError({ message: \"Missing auth token\" });\n headers[\"X-Auth-Token\"] = this.authToken;\n }\n let resp: Response;\n try {\n resp = await fetch(`${this.base()}${scope}${path}`, {\n method: body ? \"POST\" : \"GET\",\n headers,\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n } catch (cause) {\n throw new AuthError({\n code: \"https_unreachable\",\n message: `Could not reach Anker cloud API (${API_HOSTS[this.region]})`,\n retriable: true,\n hint: \"Check your internet connection and try again.\",\n cause,\n });\n }\n if (!resp.ok) {\n throw new AuthError({\n code: \"https_error\",\n message: `API request failed: ${resp.status} ${resp.statusText}`,\n retriable: resp.status >= 500,\n });\n }\n const jsn = (await resp.json()) as Json;\n if (num(jsn.code) !== 0) {\n throw this.apiError(jsn);\n }\n return (jsn.data as Json) ?? {};\n }\n\n /** Translate a non-zero API response into a structured error (captcha-aware). */\n private apiError(jsn: Json): AuthError {\n const data = (jsn.data as Json) ?? {};\n const message = str(jsn.msg, \"API error\");\n if (typeof data.captcha_id === \"string\") {\n return new AuthError({\n code: \"captcha_required\",\n message: \"Login requires solving a captcha challenge\",\n hint:\n \"Re-run login with `--captcha-answer <text>` (and the captcha_id below). \" +\n \"The captcha image URL is in `input`.\",\n input: {\n captcha_id: data.captcha_id,\n ...(typeof data.item === \"string\" ? { captcha_image: data.item } : {}),\n },\n });\n }\n return new AuthError({\n code: \"login_rejected\",\n message,\n input: { api_code: num(jsn.code) },\n });\n }\n\n // --- /v2/passport/login ---\n async login(opts: LoginOptions): Promise<LoginResult> {\n const { publicKey, encryptedPassword } = ecdhEncryptLoginPassword(opts.password);\n const body: Json = {\n client_secret_info: { public_key: publicKey },\n email: opts.email,\n password: encryptedPassword,\n };\n if (opts.captchaId) body.captcha_id = opts.captchaId;\n if (opts.captchaAnswer) body.answer = opts.captchaAnswer;\n\n const data = await this.request(\"/v2/passport/login\", \"\", body);\n const authToken = str(data.auth_token);\n if (!authToken) {\n throw new AuthError({ code: \"login_failed\", message: \"Login returned no auth token\" });\n }\n return {\n auth_token: authToken,\n user_id: str(data.user_id),\n email: str(data.email, opts.email),\n region: this.region,\n ab_code: str(data.ab_code) || undefined,\n };\n }\n\n // --- /v1/passport/profile ---\n async profile(): Promise<{ user_id: string; email: string; country: string }> {\n const data = await this.request(\"/v1/passport\", \"/profile\", undefined, true);\n const country = (data.country as Json | undefined)?.code;\n return {\n user_id: str(data.user_id),\n email: str(data.email),\n country: str(country),\n };\n }\n\n // --- /v1/app/query_fdm_list ---\n async queryFdmList(): Promise<Json[]> {\n const data = await this.request(\"/v1/app\", \"/query_fdm_list\", {}, true);\n return Array.isArray(data) ? (data as Json[]) : ((data.data as Json[] | undefined) ?? []);\n }\n\n // --- /v1/app/equipment/get_dsk_keys ---\n async getDskKeys(stationSns: string[]): Promise<Record<string, string>> {\n const data = await this.request(\n \"/v1/app\",\n \"/equipment/get_dsk_keys\",\n { invalid_dsks: {}, station_sns: stationSns },\n true,\n );\n const out: Record<string, string> = {};\n for (const dsk of (data.dsk_keys as Json[] | undefined) ?? []) {\n const sn = str(dsk.station_sn);\n if (sn) out[sn] = str(dsk.dsk_key);\n }\n return out;\n }\n}\n\n/** Map a raw cloud printer record + dsk keys into our {@link AnkerPrinter}. */\nfunction toPrinter(pr: Json, dskKeys: Record<string, string>): AnkerPrinter {\n const sn = str(pr.station_sn);\n return {\n id: str(pr.station_id),\n sn,\n name: str(pr.station_name),\n model: str(pr.station_model),\n duid: str(pr.p2p_did),\n ip_addr: str(pr.ip_addr),\n wifi_mac: str(pr.wifi_mac),\n mqtt_key: str(pr.secret_key),\n p2p_key: dskKeys[sn] ?? \"\",\n api_hosts: pr.app_conn ? ppppDecodeInitstring(str(pr.app_conn)) : [],\n p2p_hosts: pr.p2p_conn ? ppppDecodeInitstring(str(pr.p2p_conn)) : [],\n };\n}\n\n/**\n * Full login → config bootstrap (reference `fetch_config_by_login` +\n * `load_config_from_api`). Logs in, then pulls profile, printer list, and PPPP\n * keys, returning a ready-to-store {@link AnkerConfig}.\n */\nexport async function loginAndBuildConfig(opts: LoginOptions): Promise<AnkerConfig> {\n const region = guessRegion(opts.country);\n const api = new AnkerHttpApi(region);\n const login = await api.login(opts);\n\n const authed = new AnkerHttpApi(region, login.auth_token);\n const profile = await authed.profile();\n\n const printersRaw = await authed.queryFdmList();\n const sns = printersRaw.map((p) => str(p.station_sn)).filter(Boolean);\n const dskKeys = sns.length ? await authed.getDskKeys(sns) : {};\n\n const printers = printersRaw\n .map((p) => toPrinter(p, dskKeys))\n .sort((a, b) => Number(a.id) - Number(b.id));\n\n return {\n account: {\n user_id: profile.user_id || login.user_id,\n auth_token: login.auth_token,\n email: profile.email || login.email,\n region,\n country: profile.country || opts.country.toUpperCase(),\n },\n printers,\n };\n}\n","/**\n * PPPP LAN client (brief §5, §6A) — ported from the reference\n * `libflagship/ppppapi.py` + `cli/pppp.py` + the web file-transfer service.\n *\n * PPPP is LAN-only here (matching the reference): UDP broadcast discovery, a\n * punch/ready handshake, then reliable file upload over DRW channels. Upload is\n * `XZYH(P2P_SEND_FILE)` then `AABB` frames — BEGIN (metadata), DATA (chunks),\n * END (which starts the print). The SDK does no console I/O; progress and\n * diagnostics flow through callbacks.\n */\nimport { createSocket, type Socket } from \"node:dgram\";\nimport { md5Hex } from \"../../crypto.js\";\nimport { PrinterRejectedError, TimeoutError, TransportUnavailableError } from \"../../errors.js\";\nimport { Channel } from \"./channel.js\";\nimport {\n duidToString,\n FileTransfer,\n FileTransferReply,\n P2PCmdType,\n packAabb,\n packFileUploadInfo,\n packXzyh,\n parseAabbWithCrc,\n parseDuidString,\n parseMessage,\n pktAliveAck,\n pktClose,\n pktDevLgnAckCrc,\n pktDrwAck,\n pktHelloAck,\n pktLanSearch,\n pktP2pRdy,\n pktP2pRdyAck,\n PpppType,\n sanitizeFilename,\n type Duid,\n type Host,\n} from \"./packets.js\";\n\nexport const PPPP_LAN_PORT = 32108;\n\nexport enum PpppState {\n Idle = 1,\n Connecting = 2,\n Connected = 3,\n Disconnected = 4,\n}\n\nexport interface LanPrinter {\n duid: string;\n ip: string;\n}\n\nexport interface UploadProgress {\n sent: number;\n total: number;\n pct: number;\n}\n\nexport interface PpppClientOptions {\n duid: string;\n host: string;\n port?: number;\n log?: (msg: string) => void;\n}\n\n/**\n * One-shot LAN discovery: broadcast a `LAN_SEARCH` and collect `PUNCH_PKT`\n * replies (each yields a DUID + source IP). Retried by the caller — UDP\n * broadcast is flaky (the §4 lesson).\n */\nexport function discoverLan(\n opts: { timeoutMs?: number; bindAddr?: string; log?: (m: string) => void } = {},\n): Promise<LanPrinter[]> {\n const { timeoutMs = 1000, bindAddr, log = () => {} } = opts;\n return new Promise((resolve, reject) => {\n const sock = createSocket({ type: \"udp4\", reuseAddr: true });\n const found = new Map<string, string>();\n\n const done = (): void => {\n try {\n sock.close();\n } catch {\n /* ignore */\n }\n resolve([...found].map(([duid, ip]) => ({ duid, ip })));\n };\n\n sock.on(\"error\", (err) => {\n try {\n sock.close();\n } catch {\n /* ignore */\n }\n reject(err);\n });\n\n sock.on(\"message\", (data, rinfo) => {\n try {\n const msg = parseMessage(data);\n if (msg.type === PpppType.PUNCH_PKT && msg.duid) {\n found.set(duidToString(msg.duid), rinfo.address);\n }\n } catch {\n /* ignore malformed datagrams */\n }\n });\n\n sock.bind(bindAddr ? { address: bindAddr, port: 0 } : { port: 0 }, () => {\n sock.setBroadcast(true);\n log(`pppp: broadcasting LAN_SEARCH (timeout ${timeoutMs}ms)`);\n sock.send(pktLanSearch(), PPPP_LAN_PORT, \"255.255.255.255\");\n setTimeout(done, timeoutMs);\n });\n });\n}\n\nexport class AnkerPpppClient {\n private sock?: Socket;\n private addr: { host: string; port: number };\n private readonly duid: Duid;\n private readonly chans: Channel[] = Array.from({ length: 8 }, (_, i) => new Channel(i));\n private pollTimer?: ReturnType<typeof setInterval>;\n private readonly log: (msg: string) => void;\n state: PpppState = PpppState.Idle;\n private connectResolve?: () => void;\n private connectReject?: (err: Error) => void;\n\n constructor(opts: PpppClientOptions) {\n this.duid = parseDuidString(opts.duid);\n this.addr = { host: opts.host, port: opts.port ?? PPPP_LAN_PORT };\n this.log = opts.log ?? (() => {});\n }\n\n private get host(): Host {\n return { afam: 2, port: this.addr.port, addr: this.addr.host };\n }\n\n private send(buf: Buffer): void {\n this.sock?.send(buf, this.addr.port, this.addr.host);\n }\n\n /** Connect over the LAN via the punch/ready handshake. */\n connect(timeoutMs = 10000): Promise<void> {\n this.sock = createSocket(\"udp4\");\n this.sock.on(\"message\", (data, rinfo) => {\n this.addr = { host: rinfo.address, port: rinfo.port };\n this.process(data);\n });\n this.sock.on(\"error\", (err) => this.failConnect(err));\n this.state = PpppState.Connecting;\n\n const ready = new Promise<void>((resolve, reject) => {\n this.connectResolve = resolve;\n this.connectReject = reject;\n });\n\n this.send(pktLanSearch());\n this.pollTimer = setInterval(() => this.pollChannels(), 20);\n\n const timer = setTimeout(() => {\n if (this.state !== PpppState.Connected) {\n this.failConnect(\n new TimeoutError({\n message: `PPPP handshake to ${this.addr.host} timed out`,\n transport: \"pppp\",\n hint: \"The printer may be off-LAN or asleep. Re-run `ankerts discover --store` and retry.\",\n }),\n );\n }\n }, timeoutMs);\n\n return ready.finally(() => clearTimeout(timer));\n }\n\n private failConnect(err: Error): void {\n this.state = PpppState.Disconnected;\n this.stop();\n this.connectReject?.(err);\n this.connectReject = undefined;\n this.connectResolve = undefined;\n }\n\n private pollChannels(): void {\n const now = Date.now();\n for (const ch of this.chans) {\n for (const pkt of ch.poll(now)) this.send(pkt);\n }\n }\n\n private process(data: Buffer): void {\n let msg;\n try {\n msg = parseMessage(data);\n } catch {\n return;\n }\n switch (msg.type) {\n case PpppType.CLOSE:\n this.failConnect(new Error(\"PPPP connection closed by device\"));\n break;\n case PpppType.ALIVE:\n this.send(pktAliveAck());\n break;\n case PpppType.DRW:\n if (msg.chan !== undefined && msg.index !== undefined && msg.data) {\n this.send(pktDrwAck(msg.chan, [msg.index]));\n this.chans[msg.chan]?.rxDrw(msg.index, msg.data);\n }\n break;\n case PpppType.DRW_ACK:\n if (msg.chan !== undefined && msg.acks) this.chans[msg.chan]?.rxAck(msg.acks);\n break;\n case PpppType.DEV_LGN_CRC:\n this.send(pktDevLgnAckCrc());\n break;\n case PpppType.HELLO:\n this.send(pktHelloAck(this.host));\n break;\n case PpppType.P2P_RDY:\n this.send(pktP2pRdyAck(this.duid, this.host));\n this.state = PpppState.Connected;\n this.connectResolve?.();\n this.connectResolve = undefined;\n this.connectReject = undefined;\n this.log(\"pppp: connected\");\n break;\n case PpppType.PUNCH_PKT:\n if (this.state === PpppState.Connecting) {\n this.send(pktClose());\n this.send(pktP2pRdy(this.duid));\n }\n break;\n default:\n break;\n }\n }\n\n stop(): void {\n if (this.pollTimer) clearInterval(this.pollTimer);\n this.pollTimer = undefined;\n if (this.sock) {\n try {\n if (this.state === PpppState.Connected) this.send(pktClose());\n this.sock.close();\n } catch {\n /* ignore */\n }\n this.sock = undefined;\n }\n }\n\n /** Send one channel-1 AABB request and await the printer's reply byte. */\n private async aabbRequest(\n frametype: FileTransfer,\n data: Buffer,\n opts: { pos?: number; replyTimeoutMs?: number } = {},\n ): Promise<FileTransferReply> {\n const ch = this.chans[1]!;\n const { done } = ch.write(packAabb(frametype, data, { pos: opts.pos ?? 0 }));\n await ch.ackUpTo(done);\n\n const replyTimeout = opts.replyTimeoutMs ?? 30000;\n const head = await ch.read(12, replyTimeout);\n const len = head.readUInt32LE(8);\n const rest = await ch.read(len + 2, replyTimeout);\n const { data: payload } = parseAabbWithCrc(Buffer.concat([head, rest]));\n const reply = (payload[0] ?? FileTransferReply.ERR_BUSY) as FileTransferReply;\n if (reply !== FileTransferReply.OK) {\n throw new PrinterRejectedError({\n code: \"upload_rejected\",\n message: `Printer rejected file transfer: ${FileTransferReply[reply] ?? reply}`,\n transport: \"pppp\",\n });\n }\n return reply;\n }\n\n /**\n * Upload a gcode file and (by default) start the print. Mirrors the reference\n * web service: XZYH(P2P_SEND_FILE) → AABB BEGIN (metadata) → AABB DATA chunks\n * → AABB END (starts printing).\n */\n async uploadFile(\n filename: string,\n data: Buffer,\n opts: {\n userName?: string;\n userId?: string;\n machineId?: string;\n start?: boolean;\n onProgress?: (p: UploadProgress) => void;\n } = {},\n ): Promise<{ name: string; size: number; md5: string; started: boolean }> {\n if (this.state !== PpppState.Connected) {\n throw new TransportUnavailableError({\n message: \"PPPP not connected — cannot upload\",\n transport: \"pppp\",\n hint: \"Run discovery and connect to the printer on the LAN first.\",\n });\n }\n const name = sanitizeFilename(filename);\n const md5 = md5Hex(data);\n const fui = packFileUploadInfo({\n name,\n size: data.length,\n md5,\n userName: opts.userName ?? \"ankerts\",\n userId: opts.userId ?? \"-\",\n machineId: opts.machineId ?? \"-\",\n });\n\n // 1. request file transfer (16-byte token on channel 0).\n const token = Buffer.from(\n globalThis.crypto.randomUUID().replace(/-/g, \"\").slice(0, 16),\n \"utf8\",\n );\n const ch0 = this.chans[0]!;\n const { done: tokenDone } = ch0.write(packXzyh({ cmd: P2PCmdType.P2P_SEND_FILE, data: token }));\n await ch0.ackUpTo(tokenDone);\n\n // 2. metadata (BEGIN). The reference appends an extra NUL after the struct.\n this.log(`pppp: uploading ${data.length} bytes as ${name}`);\n await this.aabbRequest(FileTransfer.BEGIN, Buffer.concat([fui, Buffer.from([0])]));\n\n // 3. file contents in 32KB chunks.\n const blockSize = 1024 * 32;\n let sent = 0;\n for (let pos = 0; pos < data.length; pos += blockSize) {\n const chunk = data.subarray(pos, pos + blockSize);\n await this.aabbRequest(FileTransfer.DATA, chunk, { pos });\n sent += chunk.length;\n opts.onProgress?.({ sent, total: data.length, pct: (sent / data.length) * 100 });\n }\n\n // 4. END starts the print (unless start === false → abort/leave staged).\n const started = opts.start !== false;\n if (started) {\n await this.aabbRequest(FileTransfer.END, Buffer.alloc(0));\n }\n\n return { name, size: data.length, md5, started };\n }\n}\n","/**\n * PPPP packet framing, ported from the reference `libflagship/pppp.py`.\n *\n * PPPP is the custom P2P-over-UDP protocol the M5 uses for LAN file upload and\n * camera streaming. Every datagram is a `0xF1`-magic message: `F1 <type> <size>`\n * then a type-specific body. Reliable bulk transfer rides on DRW/DRW_ACK packets\n * (see channel.ts); file payloads are wrapped in `XZYH` and `AABB` frames.\n *\n * Pure framing — pack/parse round-trips are unit-tested without a printer.\n */\nimport { BufReader, BufWriter } from \"../../binary.js\";\nimport { cryptoCurseString, ppcsCrc16, simpleDecrypt } from \"../../crypto.js\";\n\nconst MSG_MAGIC = 0xf1;\n\nexport enum PpppType {\n HELLO = 0x00,\n HELLO_ACK = 0x01,\n DEV_LGN_CRC = 0x12,\n DEV_LGN_ACK_CRC = 0x13,\n LAN_SEARCH = 0x30,\n PUNCH_TO = 0x40,\n PUNCH_PKT = 0x41,\n P2P_RDY = 0x42,\n P2P_RDY_ACK = 0x43,\n DRW = 0xd0,\n DRW_ACK = 0xd1,\n ALIVE = 0xe0,\n ALIVE_ACK = 0xe1,\n CLOSE = 0xf0,\n REPORT_SESSION_READY = 0xf9,\n}\n\nexport enum P2PCmdType {\n P2P_SEND_FILE = 0x3a98,\n}\n\nexport enum FileTransfer {\n BEGIN = 0x00, // sent with metadata\n DATA = 0x01, // file content\n END = 0x02, // complete transfer → start printing\n ABORT = 0x03, // abort + delete file\n REPLY = 0x80, // reply from printer\n}\n\nexport enum FileTransferReply {\n OK = 0x00,\n ERR_TIMEOUT = 0xfc,\n ERR_FRAME_TYPE = 0xfd,\n ERR_WRONG_MD5 = 0xfe,\n ERR_BUSY = 0xff,\n}\n\n// ---------------------------------------------------------------------------\n// Duid (device id, e.g. \"USPRAKM-000994-YYLLG\")\n// ---------------------------------------------------------------------------\n\nexport interface Duid {\n prefix: string;\n serial: number;\n check: string;\n}\n\nexport function parseDuidString(s: string): Duid {\n const [prefix, serial, check] = s.split(\"-\");\n return { prefix: prefix ?? \"\", serial: Number(serial ?? 0), check: check ?? \"\" };\n}\n\nexport function duidToString(d: Duid): string {\n return `${d.prefix}-${String(d.serial).padStart(6, \"0\")}-${d.check}`;\n}\n\nfunction writeDuid(w: BufWriter, d: Duid): void {\n w.string(d.prefix, 8).u32be(d.serial).string(d.check, 6).zeroes(2);\n}\n\nfunction readDuid(r: BufReader): Duid {\n const prefix = r.string(8);\n const serial = r.u32be();\n const check = r.string(6);\n r.zeroes(2);\n return { prefix, serial, check };\n}\n\n// ---------------------------------------------------------------------------\n// Host (address record)\n// ---------------------------------------------------------------------------\n\nexport interface Host {\n afam: number;\n port: number;\n addr: string;\n}\n\nfunction writeHost(w: BufWriter, h: Host): void {\n w.zeroes(1).u8(h.afam).u16le(h.port).ipv4(h.addr).zeroes(8);\n}\n\nconst AF_INET = 2;\nconst ZERO_HOST: Host = { afam: AF_INET, port: 0, addr: \"0.0.0.0\" };\n\n// ---------------------------------------------------------------------------\n// Message wrapper: F1 <type> <size:u16be> <body>\n// ---------------------------------------------------------------------------\n\nfunction wrapMessage(type: PpppType, body: Buffer): Buffer {\n return new BufWriter().u8(MSG_MAGIC).u8(type).u16be(body.length).bytes(body).build();\n}\n\nexport interface ParsedMessage {\n type: PpppType;\n body: Buffer;\n duid?: Duid;\n /** DRW fields. */\n chan?: number;\n index?: number;\n data?: Buffer;\n /** DRW_ACK fields. */\n acks?: number[];\n}\n\n/** Parse an inbound datagram into a typed message (subset we act on). */\nexport function parseMessage(buf: Buffer): ParsedMessage {\n const r = new BufReader(buf);\n const magic = r.u8();\n if (magic !== MSG_MAGIC) throw new Error(`bad PPPP magic: 0x${magic.toString(16)}`);\n const type = r.u8() as PpppType;\n const size = r.u16be();\n const body = r.bytes(size);\n const out: ParsedMessage = { type, body };\n\n switch (type) {\n case PpppType.PUNCH_PKT:\n case PpppType.P2P_RDY: {\n out.duid = readDuid(new BufReader(body));\n break;\n }\n case PpppType.DRW: {\n const b = new BufReader(body);\n b.magic(Buffer.from([0xd1]));\n out.chan = b.u8();\n out.index = b.u16be();\n out.data = b.tail();\n break;\n }\n case PpppType.DRW_ACK: {\n const b = new BufReader(body);\n b.magic(Buffer.from([0xd1]));\n out.chan = b.u8();\n const count = b.u16be();\n out.acks = Array.from({ length: count }, () => b.u16be());\n break;\n }\n case PpppType.REPORT_SESSION_READY: {\n // body is `simple`-encrypted; decode best-effort for diagnostics.\n try {\n out.duid = readDuid(new BufReader(simpleDecrypt(body)));\n } catch {\n /* ignore */\n }\n break;\n }\n default:\n break;\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Outbound packet builders\n// ---------------------------------------------------------------------------\n\nexport const pktLanSearch = (): Buffer => wrapMessage(PpppType.LAN_SEARCH, Buffer.alloc(0));\nexport const pktClose = (): Buffer => wrapMessage(PpppType.CLOSE, Buffer.alloc(0));\nexport const pktAlive = (): Buffer => wrapMessage(PpppType.ALIVE, Buffer.alloc(0));\nexport const pktAliveAck = (): Buffer => wrapMessage(PpppType.ALIVE_ACK, Buffer.alloc(0));\n\nexport function pktP2pRdy(duid: Duid): Buffer {\n const w = new BufWriter();\n writeDuid(w, duid);\n return wrapMessage(PpppType.P2P_RDY, w.build());\n}\n\nexport function pktP2pRdyAck(duid: Duid, host: Host): Buffer {\n const w = new BufWriter();\n writeDuid(w, duid);\n writeHost(w, host);\n w.zeroes(8);\n return wrapMessage(PpppType.P2P_RDY_ACK, w.build());\n}\n\nexport function pktHelloAck(host: Host): Buffer {\n const w = new BufWriter();\n writeHost(w, host);\n return wrapMessage(PpppType.HELLO_ACK, w.build());\n}\n\nexport function pktDevLgnAckCrc(): Buffer {\n return wrapMessage(PpppType.DEV_LGN_ACK_CRC, cryptoCurseString(Buffer.alloc(4)));\n}\n\nexport function pktDrw(chan: number, index: number, data: Buffer): Buffer {\n const body = new BufWriter().u8(0xd1).u8(chan).u16be(index).bytes(data).build();\n return wrapMessage(PpppType.DRW, body);\n}\n\nexport function pktDrwAck(chan: number, acks: number[]): Buffer {\n const w = new BufWriter().u8(0xd1).u8(chan).u16be(acks.length);\n for (const a of acks) w.u16be(a);\n return wrapMessage(PpppType.DRW_ACK, w.build());\n}\n\nexport { ZERO_HOST, AF_INET };\n\n// ---------------------------------------------------------------------------\n// XZYH frame (file-transfer command envelope)\n// ---------------------------------------------------------------------------\n\nexport interface XzyhOptions {\n cmd: P2PCmdType;\n data: Buffer;\n chan?: number;\n signCode?: number;\n devType?: number;\n}\n\nexport function packXzyh(opts: XzyhOptions): Buffer {\n const { cmd, data, chan = 0, signCode = 0, devType = 0 } = opts;\n return new BufWriter()\n .bytes(Buffer.from(\"XZYH\", \"ascii\"))\n .u16le(cmd)\n .u32le(data.length)\n .u8(0) // unk0\n .u8(0) // unk1\n .u8(chan)\n .u8(signCode)\n .u8(0) // unk3\n .u8(devType)\n .bytes(data)\n .build();\n}\n\n// ---------------------------------------------------------------------------\n// AABB frame (file-transfer data, CRC-protected)\n// ---------------------------------------------------------------------------\n\nexport interface AabbHeader {\n frametype: FileTransfer;\n sn: number;\n pos: number;\n len: number;\n}\n\nconst AABB_SIG = Buffer.from([0xaa, 0xbb]);\n\nfunction packAabbHeader(h: AabbHeader): Buffer {\n return new BufWriter().bytes(AABB_SIG).u8(h.frametype).u8(h.sn).u32le(h.pos).u32le(h.len).build();\n}\n\n/** Build an AABB frame with trailing CRC16 over `header[2:] + data`. */\nexport function packAabb(\n frametype: FileTransfer,\n data: Buffer,\n opts: { sn?: number; pos?: number } = {},\n): Buffer {\n const header = packAabbHeader({\n frametype,\n sn: opts.sn ?? 0,\n pos: opts.pos ?? 0,\n len: data.length,\n });\n const crc = ppcsCrc16(Buffer.concat([header.subarray(2), data]));\n return Buffer.concat([header, data, crc]);\n}\n\nexport interface AabbParsed {\n header: AabbHeader;\n data: Buffer;\n}\n\n/** Parse an AABB frame (12-byte header + len bytes + 2-byte CRC), verifying CRC. */\nexport function parseAabbWithCrc(buf: Buffer): AabbParsed {\n const head = buf.subarray(0, 12);\n const r = new BufReader(head);\n r.magic(AABB_SIG);\n const frametype = r.u8() as FileTransfer;\n const sn = r.u8();\n const pos = r.u32le();\n const len = r.u32le();\n const data = buf.subarray(12, 12 + len);\n const crc1 = buf.subarray(12 + len, 12 + len + 2);\n const crc2 = ppcsCrc16(Buffer.concat([head.subarray(2), data]));\n if (!crc1.equals(crc2)) throw new Error(\"AABB CRC mismatch\");\n return { header: { frametype, sn, pos, len }, data: Buffer.from(data) };\n}\n\n// ---------------------------------------------------------------------------\n// FileUploadInfo (upload metadata string)\n// ---------------------------------------------------------------------------\n\nexport interface FileUploadInfoFields {\n name: string;\n size: number;\n md5: string;\n userName: string;\n userId: string;\n machineId: string;\n type?: number;\n}\n\n/** Sanitize a filename to the upload whitelist (reference `sanitize_filename`). */\nexport function sanitizeFilename(name: string): string {\n const base = name.split(/[\\\\/]/).pop() ?? name;\n const cleaned = [...base].map((c) => (/[A-Za-z0-9._-]/.test(c) ? c : \"_\")).join(\"\");\n return cleaned.replace(/^\\.+/, \"\").replace(/\\.\\./g, \".\");\n}\n\n/**\n * Serialize FileUploadInfo to its wire form:\n * `type,name,size,md5,user_name,user_id,machine_id` + a trailing NUL.\n */\nexport function packFileUploadInfo(f: FileUploadInfoFields): Buffer {\n const type = f.type ?? 0;\n const str = `${type},${f.name},${f.size},${f.md5},${f.userName},${f.userId},${f.machineId}`;\n return Buffer.concat([Buffer.from(str, \"utf8\"), Buffer.from([0])]);\n}\n","/**\n * PPPP reliable channel — ported from the `Channel`/`CyclicU16` classes in the\n * reference `libflagship/ppppapi.py` and `cyclic.py`.\n *\n * PPPP rides a reliable, ordered byte stream on top of unreliable UDP: outbound\n * payloads are chunked into ≤1KB DRW packets with cyclic 16-bit sequence\n * numbers, retransmitted until ACKed; inbound DRW packets are reordered into a\n * contiguous stream for readers. The 16-bit counters wrap, so comparisons use\n * the cyclic ordering from `CyclicU16`.\n */\nimport { pktDrw } from \"./packets.js\";\n\nconst U16 = 0xffff;\nconst trunc = (n: number): number => n & U16;\n\n/** Cyclic 16-bit `<` (wrap-aware), matching `CyclicU16.__lt__`. */\nexport function cyclicLt(a: number, b: number, wrap = 0x100): boolean {\n a = trunc(a);\n b = trunc(b);\n if ((a ^ b) & 0x8000) return trunc(a - wrap) < trunc(b - wrap);\n return a < b;\n}\n\n/** Cyclic 16-bit `>`. */\nexport function cyclicGt(a: number, b: number, wrap = 0x100): boolean {\n a = trunc(a);\n b = trunc(b);\n if ((a ^ b) & 0x8000) return trunc(a - wrap) > trunc(b - wrap);\n return a > b;\n}\n\n/** Cyclic 16-bit `>=`. */\nexport const cyclicGte = (a: number, b: number, wrap = 0x100): boolean => !cyclicLt(a, b, wrap);\n\nconst CHUNK = 1024;\n\ninterface TxEntry {\n deadline: number;\n data: Buffer;\n}\n\nexport class Channel {\n private txCtr = 0;\n private txAck = 0;\n private readonly backlog: { ctr: number; data: Buffer }[] = [];\n private readonly txqueue = new Map<number, TxEntry>();\n private readonly acks = new Set<number>();\n private readonly ackWaiters: { ctr: number; resolve: () => void }[] = [];\n\n private rxCtr = 0;\n private readonly rxqueue = new Map<number, Buffer>();\n private rxBuf: Buffer = Buffer.alloc(0);\n private readonly rxWaiters: (() => void)[] = [];\n\n constructor(\n readonly index: number,\n private readonly timeoutMs = 500,\n private readonly maxInFlight = 64,\n ) {}\n\n // --- transmit side ---\n\n /** Queue `payload` for reliable delivery; returns its sequence range. */\n write(payload: Buffer): { start: number; done: number } {\n const start = this.txCtr;\n let rest = payload;\n do {\n const data = rest.subarray(0, CHUNK);\n rest = rest.subarray(CHUNK);\n this.backlog.push({ ctr: this.txCtr, data });\n this.txCtr = trunc(this.txCtr + 1);\n } while (rest.length > 0);\n return { start, done: this.txCtr };\n }\n\n /** Resolve once every chunk up to (but excluding) `done` has been ACKed. */\n ackUpTo(done: number): Promise<void> {\n if (cyclicGte(this.txAck, done)) return Promise.resolve();\n return new Promise<void>((resolve) => this.ackWaiters.push({ ctr: done, resolve }));\n }\n\n /** Apply received ACKs, advancing `txAck` and releasing waiters. */\n rxAck(acks: readonly number[]): void {\n for (const a of acks) {\n this.txqueue.delete(a);\n if (cyclicGte(a, this.txAck)) this.acks.add(a);\n }\n while (this.acks.has(this.txAck)) {\n this.acks.delete(this.txAck);\n this.txAck = trunc(this.txAck + 1);\n }\n for (let i = this.ackWaiters.length - 1; i >= 0; i--) {\n if (cyclicGte(this.txAck, this.ackWaiters[i]!.ctr)) {\n this.ackWaiters.splice(i, 1)[0]!.resolve();\n }\n }\n }\n\n /** Produce DRW packets to (re)transmit; called on each poll tick. */\n poll(now: number): Buffer[] {\n while (this.backlog.length && this.txqueue.size < this.maxInFlight) {\n const item = this.backlog.shift()!;\n this.txqueue.set(item.ctr, { deadline: now, data: item.data });\n }\n const out: Buffer[] = [];\n for (const [ctr, entry] of this.txqueue) {\n if (entry.deadline <= now) {\n out.push(pktDrw(this.index, ctr, entry.data));\n entry.deadline = now + this.timeoutMs;\n }\n }\n return out;\n }\n\n /** True once all queued writes have been fully ACKed. */\n get drained(): boolean {\n return this.backlog.length === 0 && this.txqueue.size === 0;\n }\n\n // --- receive side ---\n\n /** Ingest a received DRW chunk, reordering into the contiguous stream. */\n rxDrw(index: number, data: Buffer): void {\n if (cyclicGt(this.rxCtr, index)) return; // already have it\n this.rxqueue.set(trunc(index), data);\n let advanced = false;\n while (this.rxqueue.has(this.rxCtr)) {\n const chunk = this.rxqueue.get(this.rxCtr)!;\n this.rxqueue.delete(this.rxCtr);\n this.rxCtr = trunc(this.rxCtr + 1);\n this.rxBuf = Buffer.concat([this.rxBuf, chunk]);\n advanced = true;\n }\n if (advanced) {\n for (const w of this.rxWaiters.splice(0)) w();\n }\n }\n\n private waitForData(timeoutMs?: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const timer =\n timeoutMs !== undefined\n ? setTimeout(() => {\n const i = this.rxWaiters.indexOf(onData);\n if (i >= 0) this.rxWaiters.splice(i, 1);\n reject(new Error(\"channel read timeout\"));\n }, timeoutMs)\n : undefined;\n const onData = (): void => {\n if (timer) clearTimeout(timer);\n resolve();\n };\n this.rxWaiters.push(onData);\n });\n }\n\n /** Read exactly `n` bytes from the reassembled stream (awaiting more). */\n async read(n: number, timeoutMs?: number): Promise<Buffer> {\n while (this.rxBuf.length < n) {\n await this.waitForData(timeoutMs);\n }\n const out = this.rxBuf.subarray(0, n);\n this.rxBuf = this.rxBuf.subarray(n);\n return Buffer.from(out);\n }\n}\n","/**\n * Waitable conditions over server-authoritative printer state (brief §6A).\n *\n * Every wait is re-derivable from a fresh status snapshot, which is what makes\n * waits re-attachable: an agent that crashed or timed out mid-wait can re-issue\n * the same wait and it resolves against current state — no dependence on a live\n * subscription. This module is the pure predicate layer; `AnkerClient.waitFor`\n * supplies the polling/event loop.\n */\nimport type { PrinterStatus } from \"./protocol/status.js\";\n\nexport type WaitCondition =\n | { kind: \"connected\" }\n | { kind: \"lan\" }\n | { kind: \"nozzle\"; atLeast: number }\n | { kind: \"bed\"; atLeast: number }\n | { kind: \"temp-stable\" }\n | { kind: \"printing\" }\n | { kind: \"idle\" }\n | { kind: \"progress\"; atLeast: number }\n | { kind: \"layer\"; atLeast: number }\n | { kind: \"complete\" }\n | { kind: \"failed\" }\n | { kind: \"cancelled\" }\n | { kind: \"runout\" };\n\n/** Temperatures within this band of target count as \"stable\". */\nconst TEMP_STABLE_DELTA = 2.0;\n\n/**\n * Parse a CLI condition string such as `nozzle>=210`, `progress>=50`, or\n * `complete` into a {@link WaitCondition}.\n */\nexport function parseWaitCondition(input: string): WaitCondition {\n const s = input.trim();\n const cmp = /^(nozzle|bed|progress|layer)\\s*>=\\s*(-?\\d+(?:\\.\\d+)?)$/.exec(s);\n if (cmp) {\n const value = Number(cmp[2]);\n switch (cmp[1]) {\n case \"nozzle\":\n return { kind: \"nozzle\", atLeast: value };\n case \"bed\":\n return { kind: \"bed\", atLeast: value };\n case \"progress\":\n return { kind: \"progress\", atLeast: value };\n case \"layer\":\n return { kind: \"layer\", atLeast: value };\n }\n }\n switch (s) {\n case \"connected\":\n case \"lan\":\n case \"temp-stable\":\n case \"printing\":\n case \"idle\":\n case \"complete\":\n case \"failed\":\n case \"cancelled\":\n case \"runout\":\n return { kind: s };\n default:\n throw new Error(\n `unknown wait condition: \"${input}\" (try connected|lan|nozzle>=C|bed>=C|` +\n `temp-stable|printing|idle|progress>=pct|layer>=n|complete|failed|cancelled|runout)`,\n );\n }\n}\n\n/** Render a condition back to its canonical string form. */\nexport function describeWaitCondition(cond: WaitCondition): string {\n switch (cond.kind) {\n case \"nozzle\":\n return `nozzle>=${cond.atLeast}`;\n case \"bed\":\n return `bed>=${cond.atLeast}`;\n case \"progress\":\n return `progress>=${cond.atLeast}`;\n case \"layer\":\n return `layer>=${cond.atLeast}`;\n default:\n return cond.kind;\n }\n}\n\n/**\n * Evaluate a status-derived condition against a snapshot. Transport-only\n * conditions (`connected`, `lan`) return `null` — the caller resolves those from\n * transport state, not status.\n */\nexport function conditionHolds(cond: WaitCondition, status: PrinterStatus): boolean | null {\n const job = status.job;\n switch (cond.kind) {\n case \"connected\":\n case \"lan\":\n return null;\n case \"nozzle\":\n return status.nozzle.current >= cond.atLeast;\n case \"bed\":\n return status.bed.current >= cond.atLeast;\n case \"temp-stable\": {\n const nozzleOk =\n status.nozzle.target > 0 &&\n Math.abs(status.nozzle.current - status.nozzle.target) <= TEMP_STABLE_DELTA;\n const bedOk =\n status.bed.target <= 0 ||\n Math.abs(status.bed.current - status.bed.target) <= TEMP_STABLE_DELTA;\n return nozzleOk && bedOk;\n }\n case \"printing\":\n return job?.state === \"printing\";\n case \"idle\":\n return !job || job.state === \"idle\" || job.state === \"complete\";\n case \"progress\":\n return (job?.progressPct ?? -1) >= cond.atLeast;\n case \"layer\":\n return (job?.layer ?? -1) >= cond.atLeast;\n case \"complete\":\n return job?.state === \"complete\";\n case \"failed\":\n return job?.state === \"failed\";\n case \"cancelled\":\n return job?.state === \"cancelled\";\n case \"runout\":\n // Best-effort: a runout surfaces as a failed job or a runout event in raw.\n return job?.state === \"failed\" || JSON.stringify(status.raw).toLowerCase().includes(\"runout\");\n }\n}\n","/**\n * State-mutating gcode detection (brief §4, lesson #5).\n *\n * Many gcode commands change persistent or volatile machine state. The reference\n * silently let us set Linear Advance K in RAM, contaminating the next print. The\n * SDK flags these so the CLI can warn — noting that the change is volatile, that\n * `M500` persists it to EEPROM, and that `M501` reloads EEPROM to revert.\n */\n\n/** Commands that mutate machine settings (parameter tuning, offsets, currents). */\nconst VOLATILE_SETTERS = new Set([\n \"M92\", // steps/mm\n \"M201\", // max acceleration\n \"M203\", // max feedrate\n \"M204\", // accel for print/retract/travel\n \"M301\", // hotend PID\n \"M304\", // bed PID\n \"M851\", // probe Z offset\n \"M900\", // linear advance K\n \"M906\", // stepper current (TMC)\n \"M913\", // hybrid threshold (TMC)\n]);\n\nexport interface GcodeInspection {\n /** The leading G/M code, uppercased (e.g. `M900`), or null if unparseable. */\n code: string | null;\n /** True if the command writes machine state (volatile or persistent). */\n mutatesState: boolean;\n /** True if the effect lives in volatile RAM until power-cycle or `M501`. */\n volatile: boolean;\n /** True if the command persists settings to EEPROM (`M500`). */\n persists: boolean;\n /** Human-readable note about side effects (empty when none). */\n note: string;\n}\n\n/** Extract the leading gcode word (e.g. `M900` from `M900 K0.5 ; comment`). */\nexport function gcodeCode(command: string): string | null {\n const m = /^\\s*([GM]\\d+)/i.exec(command);\n return m ? m[1]!.toUpperCase() : null;\n}\n\n/** Inspect a gcode command for state-mutation side effects. */\nexport function inspectGcode(command: string): GcodeInspection {\n const code = gcodeCode(command);\n const base: GcodeInspection = {\n code,\n mutatesState: false,\n volatile: false,\n persists: false,\n note: \"\",\n };\n if (!code) return base;\n\n switch (code) {\n case \"M500\":\n return {\n ...base,\n mutatesState: true,\n persists: true,\n note: \"M500 writes current settings to EEPROM — the change persists across power cycles.\",\n };\n case \"M501\":\n return {\n ...base,\n mutatesState: true,\n note: \"M501 reloads settings from EEPROM, discarding volatile (RAM) changes.\",\n };\n case \"M502\":\n return {\n ...base,\n mutatesState: true,\n volatile: true,\n note: \"M502 resets settings to firmware defaults in RAM (not saved until M500).\",\n };\n default:\n if (VOLATILE_SETTERS.has(code)) {\n return {\n ...base,\n mutatesState: true,\n volatile: true,\n note: `${code} changes a machine setting in volatile RAM — it persists until power-cycle and can contaminate the next print. M500 persists it; M501 reverts to the EEPROM value.`,\n };\n }\n return base;\n }\n}\n"],"mappings":"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,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F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","names":["MqttCommandType","NoticeType","PrintControl","num","FileTransferReply","str","PpppState","mqtt"]}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@aubron/ankerts",
3
+ "version": "0.1.0",
4
+ "description": "Agent-first TypeScript SDK for AnkerMake / eufyMake M5 printers (MQTT gcode, PPPP upload, HTTPS login).",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "dependencies": {
17
+ "mqtt": "^5.10.1"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^25.9.2",
21
+ "eslint": "^10.4.1",
22
+ "tsup": "^8.5.1",
23
+ "typescript": "^6.0.3",
24
+ "vitest": "^4.1.8",
25
+ "@aubron/prettier-config": "0.1.0",
26
+ "@aubron/tsconfig": "0.1.0",
27
+ "@aubron/eslint-config": "0.1.0",
28
+ "@aubron/tsup-config": "0.1.0"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "provenance": true
33
+ },
34
+ "engines": {
35
+ "node": ">=22"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "dev": "tsup --watch",
40
+ "test": "vitest run",
41
+ "lint": "eslint .",
42
+ "typecheck": "tsc --noEmit"
43
+ }
44
+ }