@ikalogic/at1000 0.2.2 → 0.4.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.
Files changed (122) hide show
  1. package/README.md +21 -0
  2. package/build/at1000-B3HgANSi.d.mts +109 -0
  3. package/build/at1000-B3HgANSi.d.mts.map +1 -0
  4. package/build/at1000-B_udjy0I.cjs +8 -0
  5. package/build/at1000-B_udjy0I.cjs.map +1 -0
  6. package/build/at1000-BjVj0Ujk.mjs +8 -0
  7. package/build/at1000-BjVj0Ujk.mjs.map +1 -0
  8. package/build/at1000-C6w5G8L4.d.cts +109 -0
  9. package/build/at1000-C6w5G8L4.d.cts.map +1 -0
  10. package/build/browser.cjs +1 -1
  11. package/build/browser.cjs.map +1 -1
  12. package/build/browser.d.cts +9 -2
  13. package/build/browser.d.cts.map +1 -1
  14. package/build/browser.d.mts +9 -2
  15. package/build/browser.d.mts.map +1 -1
  16. package/build/browser.mjs +1 -1
  17. package/build/browser.mjs.map +1 -1
  18. package/build/{com-CBhjFJPB.d.cts → com-BLw4CGid.d.cts} +61 -61
  19. package/build/{com-CBhjFJPB.d.cts.map → com-BLw4CGid.d.cts.map} +1 -1
  20. package/build/{com-CBhjFJPB.d.mts → com-BLw4CGid.d.mts} +61 -61
  21. package/build/{com-CBhjFJPB.d.mts.map → com-BLw4CGid.d.mts.map} +1 -1
  22. package/build/com.cjs +1 -1
  23. package/build/com.cjs.map +1 -1
  24. package/build/com.d.cts +2 -2
  25. package/build/com.d.mts +2 -2
  26. package/build/com.mjs +1 -1
  27. package/build/com.mjs.map +1 -1
  28. package/build/events-CBVQfojK.d.cts +359 -0
  29. package/build/events-CBVQfojK.d.cts.map +1 -0
  30. package/build/events-CBVQfojK.d.mts +359 -0
  31. package/build/events-CBVQfojK.d.mts.map +1 -0
  32. package/build/events.cjs +1 -0
  33. package/build/events.d.cts +2 -0
  34. package/build/events.d.mts +2 -0
  35. package/build/events.mjs +1 -0
  36. package/build/{gpio-DNrdX-QV.d.cts → gpio-BuvDysYa.d.cts} +65 -16
  37. package/build/gpio-BuvDysYa.d.cts.map +1 -0
  38. package/build/{gpio-DchxrXWK.d.mts → gpio-DgQHE5fr.d.mts} +65 -16
  39. package/build/gpio-DgQHE5fr.d.mts.map +1 -0
  40. package/build/gpio.cjs +1 -1
  41. package/build/gpio.cjs.map +1 -1
  42. package/build/gpio.d.cts +2 -2
  43. package/build/gpio.d.mts +2 -2
  44. package/build/gpio.mjs +1 -1
  45. package/build/gpio.mjs.map +1 -1
  46. package/build/{hmi-DszM-Hfp.d.cts → hmi-BrdqKB3o.d.cts} +17 -5
  47. package/build/hmi-BrdqKB3o.d.cts.map +1 -0
  48. package/build/{hmi-DszM-Hfp.d.mts → hmi-BrdqKB3o.d.mts} +17 -5
  49. package/build/hmi-BrdqKB3o.d.mts.map +1 -0
  50. package/build/hmi.cjs +1 -1
  51. package/build/hmi.cjs.map +1 -1
  52. package/build/hmi.d.cts +2 -2
  53. package/build/hmi.d.mts +2 -2
  54. package/build/hmi.mjs +1 -1
  55. package/build/hmi.mjs.map +1 -1
  56. package/build/management.cjs +1 -2
  57. package/build/management.d.cts +816 -2
  58. package/build/management.d.cts.map +1 -0
  59. package/build/management.d.mts +816 -2
  60. package/build/management.d.mts.map +1 -0
  61. package/build/management.mjs +1 -2
  62. package/build/node.cjs +1 -1
  63. package/build/node.cjs.map +1 -1
  64. package/build/node.d.cts +16 -3
  65. package/build/node.d.cts.map +1 -1
  66. package/build/node.d.mts +16 -3
  67. package/build/node.d.mts.map +1 -1
  68. package/build/node.mjs +1 -1
  69. package/build/node.mjs.map +1 -1
  70. package/build/package-Bcvmz2Lx.cjs +2 -0
  71. package/build/package-Bcvmz2Lx.cjs.map +1 -0
  72. package/build/package-CN49oc-F.mjs +2 -0
  73. package/build/package-CN49oc-F.mjs.map +1 -0
  74. package/build/{power-o75Gjpwf.d.cts → power-FmknBUz2.d.cts} +30 -30
  75. package/build/{power-CN_9o_NX.d.mts.map → power-FmknBUz2.d.cts.map} +1 -1
  76. package/build/{power-CN_9o_NX.d.mts → power-HWfK-Ybw.d.mts} +30 -30
  77. package/build/{power-o75Gjpwf.d.cts.map → power-HWfK-Ybw.d.mts.map} +1 -1
  78. package/build/power.cjs +1 -1
  79. package/build/power.cjs.map +1 -1
  80. package/build/power.d.cts +2 -2
  81. package/build/power.d.mts +2 -2
  82. package/build/power.mjs +1 -1
  83. package/build/power.mjs.map +1 -1
  84. package/build/{relays-BCn6rQN1.d.cts → relays-8EGNIK1T.d.cts} +5 -2
  85. package/build/relays-8EGNIK1T.d.cts.map +1 -0
  86. package/build/{relays-BCn6rQN1.d.mts → relays-8EGNIK1T.d.mts} +5 -2
  87. package/build/relays-8EGNIK1T.d.mts.map +1 -0
  88. package/build/relays.cjs +1 -1
  89. package/build/relays.cjs.map +1 -1
  90. package/build/relays.d.cts +2 -2
  91. package/build/relays.d.mts +2 -2
  92. package/build/relays.mjs +1 -1
  93. package/build/relays.mjs.map +1 -1
  94. package/build/session-D7TpUd6L.d.cts +44 -0
  95. package/build/session-D7TpUd6L.d.cts.map +1 -0
  96. package/build/session-D7TpUd6L.d.mts +44 -0
  97. package/build/session-D7TpUd6L.d.mts.map +1 -0
  98. package/build/session.cjs +1 -0
  99. package/build/session.d.cts +2 -0
  100. package/build/session.d.mts +2 -0
  101. package/build/session.mjs +1 -0
  102. package/package.json +22 -2
  103. package/build/at1000-BXtrf8Rt.d.cts +0 -50
  104. package/build/at1000-BXtrf8Rt.d.cts.map +0 -1
  105. package/build/at1000-BuI_g7jF.cjs +0 -3
  106. package/build/at1000-BuI_g7jF.cjs.map +0 -1
  107. package/build/at1000-DVDltAyk.mjs +0 -3
  108. package/build/at1000-DVDltAyk.mjs.map +0 -1
  109. package/build/at1000-D_rOUVFH.d.mts +0 -50
  110. package/build/at1000-D_rOUVFH.d.mts.map +0 -1
  111. package/build/gpio-DNrdX-QV.d.cts.map +0 -1
  112. package/build/gpio-DchxrXWK.d.mts.map +0 -1
  113. package/build/hmi-DszM-Hfp.d.cts.map +0 -1
  114. package/build/hmi-DszM-Hfp.d.mts.map +0 -1
  115. package/build/management-C0iQha23.d.cts +0 -512
  116. package/build/management-C0iQha23.d.cts.map +0 -1
  117. package/build/management-C0iQha23.d.mts +0 -512
  118. package/build/management-C0iQha23.d.mts.map +0 -1
  119. package/build/management.cjs.map +0 -1
  120. package/build/management.mjs.map +0 -1
  121. package/build/relays-BCn6rQN1.d.cts.map +0 -1
  122. package/build/relays-BCn6rQN1.d.mts.map +0 -1
package/README.md CHANGED
@@ -25,6 +25,27 @@ npm install @ikalogic/at1000
25
25
 
26
26
  For a complete guide on setting up and using the AT1000, please refer to the [Getting Started Guide](https://ikalogic.com/kb/at1000-api/100getting-started/).
27
27
 
28
+ ## Real-time events
29
+
30
+ Subscribe to live device state changes over a WebSocket. Fetch initial state via
31
+ REST, then listen for updates. Requires a runtime with a global `WebSocket`
32
+ (Node >= 22, Bun, or a browser).
33
+
34
+ ```ts
35
+ import { AT1000 } from '@ikalogic/at1000';
36
+
37
+ const device = await AT1000.open('at1000.local');
38
+
39
+ await device.events.connect();
40
+ const unsubscribe = device.events.on('gpio.state', (event) => {
41
+ console.log(`GPIO ${event.id} =`, event.data.value);
42
+ });
43
+
44
+ // later
45
+ unsubscribe();
46
+ device.events.close();
47
+ ```
48
+
28
49
  ## License
29
50
 
30
51
  This package is distributed under the [MIT License](LICENSE).
@@ -0,0 +1,109 @@
1
+ import { s as ComApi } from "./com-BLw4CGid.mjs";
2
+ import { i as EventsApi } from "./events-CBVQfojK.mjs";
3
+ import { Information, ManagementApi } from "./management.mjs";
4
+ import { s as GpioApi } from "./gpio-DgQHE5fr.mjs";
5
+ import { r as HmiApi } from "./hmi-BrdqKB3o.mjs";
6
+ import { i as PowerApi } from "./power-HWfK-Ybw.mjs";
7
+ import { n as RelaysApi } from "./relays-8EGNIK1T.mjs";
8
+ import { t as SessionApi } from "./session-D7TpUd6L.mjs";
9
+
10
+ //#region src/at1000.d.ts
11
+ /** Structured error thrown for AT1000 API responses carrying `{ code, message, suggestion, path }`. */
12
+ declare class ApiError extends Error {
13
+ readonly code: string;
14
+ /** Raw human-readable server message (without code/suggestion/path). */
15
+ readonly detail: string;
16
+ readonly suggestion: string;
17
+ readonly path: string;
18
+ constructor(body: {
19
+ code: string;
20
+ message: string;
21
+ suggestion: string;
22
+ path: string;
23
+ });
24
+ }
25
+ /**
26
+ * Formats a host into a URL authority. Returns `host` unchanged when it already
27
+ * carries an explicit port; otherwise appends `fallbackPort` (the device's
28
+ * mDNS-advertised port when known, else the default 8080). Bare IPv6 literals
29
+ * are bracketed. Idempotent: re-formatting an authority is a no-op.
30
+ */
31
+ declare function format_host_for_url(host: string, fallbackPort?: number): string;
32
+ /** Returns the bare host (IP or hostname) from a URL authority, dropping any port. */
33
+ declare function host_without_port(host: string): string;
34
+ /** A device discovered on the network but not yet opened. */
35
+ interface DiscoveredDevice {
36
+ host: string;
37
+ info: Information;
38
+ }
39
+ /** Options for opening a device. */
40
+ interface OpenOptions {
41
+ /** Open read-only: skip `session/open`, mint no token, send no session header. */
42
+ readonly?: boolean;
43
+ /** Human-readable holder label shown to other clients and on the front panel. */
44
+ label?: string;
45
+ }
46
+ /** @hideconstructor */
47
+ declare class AT1000Base {
48
+ /**
49
+ * IP address or hostname of the AT1000 device
50
+ */
51
+ readonly host: string;
52
+ /**
53
+ * Device information
54
+ */
55
+ readonly info: Information;
56
+ /**
57
+ * Session token minted by `open()`, or `undefined` for a read-only or legacy
58
+ * (pre-session) client. Sent as the `X-AT1000-Session` header on every request.
59
+ */
60
+ readonly token: string | undefined;
61
+ /**
62
+ * `false` only when a pre-session server was detected during an exclusive
63
+ * `open()` (no `/v0/session/*` routes). Read-only opens never probe and report `true`.
64
+ */
65
+ readonly sessionSupported: boolean;
66
+ private fetch;
67
+ /**
68
+ * COM API
69
+ */
70
+ readonly com: ComApi;
71
+ /**
72
+ * HMI API
73
+ */
74
+ readonly hmi: HmiApi;
75
+ /**
76
+ * GPIO API
77
+ */
78
+ readonly gpio: GpioApi;
79
+ /**
80
+ * Management API
81
+ */
82
+ readonly management: ManagementApi;
83
+ /**
84
+ * Power management API
85
+ */
86
+ readonly power: PowerApi;
87
+ /**
88
+ * Relays API
89
+ */
90
+ readonly relays: RelaysApi;
91
+ /**
92
+ * Real-time events API (WebSocket). Call `events.connect()` then `events.on(...)`.
93
+ */
94
+ readonly events: EventsApi;
95
+ /**
96
+ * Session API. Read the current holder via `session.state()`.
97
+ */
98
+ readonly session: SessionApi;
99
+ constructor(host: string, info: Information, token?: string, sessionSupported?: boolean);
100
+ reset(): Promise<void>;
101
+ protected static openBase(device: DiscoveredDevice | string, options?: OpenOptions, session?: {
102
+ kind: 'remote' | 'project';
103
+ project?: string;
104
+ defaultLabel?: string;
105
+ }): Promise<AT1000Base>;
106
+ }
107
+ //#endregion
108
+ export { format_host_for_url as a, OpenOptions as i, ApiError as n, host_without_port as o, DiscoveredDevice as r, AT1000Base as t };
109
+ //# sourceMappingURL=at1000-B3HgANSi.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"at1000-B3HgANSi.d.mts","names":[],"sources":["../src/at1000.ts"],"mappings":";;;;;;;;;;;cAYa,QAAA,SAAiB,KAAK;EAAA,SACxB,IAAA;EADW;EAAA,SAGX,MAAA;EAAA,SACA,UAAA;EAAA,SACA,IAAA;cAEG,IAAA;IAAQ,IAAA;IAAc,OAAA;IAAiB,UAAA;IAAoB,IAAA;EAAA;AAAA;;;;;AAAc;AAoBvF;iBAAgB,mBAAA,CAAoB,IAAA,UAAc,YAAqB;;iBAgBvD,iBAAA,CAAkB,IAAY;;UAoD7B,gBAAA;EACf,IAAA;EACA,IAAA,EAAM,WAAW;AAAA;;UAIF,WAAA;EAJE;EAMjB,QAAA;EAF0B;EAI1B,KAAK;AAAA;AAAA;AAAA,cAIM,UAAA;EAAU;;;EAAA,SAIL,IAAA;EAwBK;;;EAAA,SApBL,IAAA,EAAM,WAAA;EAoCE;;;;EAAA,SA/BR,KAAA;EA8EN;;;;EAAA,SAzEM,gBAAA;EAAA,QAER,KAAA;EAhBQ;;;EAAA,SAoBA,GAAA,EAAK,MAAA;EANL;;;EAAA,SAUA,GAAA,EAAK,MAAA;EAAL;;;EAAA,SAIA,IAAA,EAAM,OAAA;EAIN;;;EAAA,SAAA,UAAA,EAAY,aAAA;EAQZ;;;EAAA,SAJA,KAAA,EAAO,QAAA;EAYP;;;EAAA,SARA,MAAA,EAAQ,SAAA;EAUQ;;;EAAA,SANhB,MAAA,EAAQ,SAAA;EAkClB;;;EAAA,SA9BU,OAAA,EAAS,UAAA;cAEb,IAAA,UAAc,IAAA,EAAM,WAAA,EAAa,KAAA,WAAgB,gBAAA;EA4BvD,KAAA,IAAK,OAAA;EAAA,iBAQY,QAAA,CACrB,MAAA,EAAQ,gBAAA,WACR,OAAA,GAAU,WAAA,EACV,OAAA;IAAY,IAAA;IAA4B,OAAA;IAAkB,YAAA;EAAA,IACzD,OAAA,CAAQ,UAAA;AAAA"}
@@ -0,0 +1,8 @@
1
+ const e=require("./chunk-CMqjfN_6.cjs"),t=require("./com.cjs"),n=require("./gpio.cjs"),r=require("./hmi.cjs"),i=require("./power.cjs"),a=require("./relays.cjs");let o=require("up-fetch"),s=require("valibot");s=e.t(s,1);let c=function(e){return e.Can=`can`,e.Rs232=`rs232`,e.Rs485=`rs485`,e.I2c=`i2c`,e.Spi=`spi`,e.Uart=`uart`,e}({});const l=s.enum(c),u=s.object({min:s.pipe(s.number(),s.minValue(-24),s.maxValue(24)),max:s.pipe(s.number(),s.minValue(-24),s.maxValue(24))}),d=s.object({max:s.number()}),ee=s.object({count:s.pipe(s.number(),s.minValue(1)),input_voltage:u,output_voltage:u}),te=s.object({count:s.pipe(s.number(),s.minValue(0)),max_current:s.number(),max_voltage:s.number(),on_resistance:s.pipe(s.number(),s.minValue(0))}),f=s.object({type:s.picklist([`3.0`,`2.0`]),count:s.pipe(s.number(),s.minValue(1))}),ne=s.object({type:s.picklist([`1000BASE-T`,`100BASE-T`,`10BASE-T`]),count:s.pipe(s.number(),s.minValue(1))}),re=s.object({type:l,count:s.pipe(s.number(),s.minValue(1))}),ie=s.object({count:s.pipe(s.number(),s.minValue(1)),output_voltage:u,current:d}),ae=s.object({type:s.picklist([`low_precision`,`high_precision`]),source:s.string(),resolution:s.pipe(s.number(),s.minValue(0))}),oe=s.object({width:s.pipe(s.number(),s.minValue(1)),height:s.pipe(s.number(),s.minValue(1))}),se=s.object({type:s.string(),size:oe}),ce=s.object({count:s.pipe(s.number(),s.minValue(0)),type:s.string()}),le=s.object({count:s.pipe(s.number(),s.minValue(0)),type:s.string()}),p=s.object({display:se,input:ce,speaker:le}),m=s.object({io:ee,relays:te,usb_ports:s.array(f),ethernet:s.array(ne),communication:s.array(re),power_supply:ie,current_measurement:s.array(ae),hmi:p}),h=s.object({model:s.string(),hostname:s.string(),serial_number:s.string(),firmware_version:s.string(),firmware_commit:s.string(),hardware_version:s.optional(s.number()),disk_used:s.number(),disk_total:s.number(),docker_used:s.number(),capabilities:m}),g=s.picklist([`idle`,`running`,`error`]),_=s.picklist([`created`,`started`,`stopped`,`removed`,`error`]),v=s.picklist([`tcp`,`udp`]),y=s.pipe(s.number(),s.integer(),s.minValue(1),s.maxValue(65535)),b=s.object({host:y,container:y,proto:v}),ue=s.pipe(s.string(),s.trim(),s.regex(/^(ssh-|ecdsa-sha2-|sk-ssh-|sk-ecdsa-)/,`Not a recognized SSH public key`)),x=s.picklist([`normal`,`development`]),S=s.object({stream:s.picklist([`stdout`,`stderr`]),line:s.string()}),C=s.object({name:s.string(),path:s.string(),id:s.string(),image:s.string(),is_default:s.boolean(),ports:s.optional(s.array(b),[]),command:s.optional(s.string(),``),state:g,mode:s.optional(x)}),w=s.variant(`change`,[s.object({change:s.literal(`state_changed`),id:s.string(),state:_}),s.object({change:s.literal(`renamed`),id:s.string(),name:s.string()}),s.object({change:s.literal(`default_changed`),id:s.string(),is_default:s.boolean()})]),de=s.partial(s.pick(C,[`name`,`is_default`,`command`])),fe=s.picklist([`nodejs`,`python`]);s.object({name:s.string(),template:fe,command:s.optional(s.string())});const T=s.object({bundle:s.string(),version:s.string(),compatible:s.string(),description:s.string()}),pe=s.object({message:s.string(),bundle:s.string()}),E=s.object({state:s.picklist([`idle`,`installing`,`failed`]),progress:s.number(),message:s.string(),error:s.string()}),D=s.object({message:s.string()}),O=s.pipe(s.number(),s.finite()),k=s.pipe(s.number(),s.finite(),s.minValue(Number.MIN_VALUE)),A=s.pipe(s.number(),s.integer(),s.minValue(0),s.maxValue(31)),j=s.pipe(s.number(),s.integer(),s.minValue(0),s.maxValue(7)),M=s.pipe(s.number(),s.integer(),s.minValue(0),s.maxValue(1)),N=s.picklist([`pass`,`fail`]),P=s.object({set:O,measured:O,reading:s.optional(O)}),F=s.object({channel:A,points:s.array(P),max_error:O,avg_error:O}),I=s.object({voltage:s.object({points:s.array(P),max_error:O,avg_error:O}),current:s.object({load_ohms:k,points:s.array(P),max_error:O,avg_error:O})}),L=s.object({load_ohms:k,voltage_applied:O,measured_current:O,expected_current:O,error_percent:O}),R=s.object({relay:j,status:N,test_voltage:O,closed_current:O,open_current:O,calibration:s.optional(F)}),z=s.object({port:M,status:N,voltage:O,current_tests:s.optional(s.array(L))}),B=s.object({schema_version:s.literal(1),tool_version:s.string(),device_id:s.string(),timestamp:s.string(),firmware_version:s.string(),dac:s.array(F),adc_positive:s.array(F),adc_negative:s.optional(s.array(F)),dut_psu:I,relays:s.array(R),usb:s.array(z)}),V=s.object({enabled:s.boolean(),loaded:s.boolean(),last_error:s.optional(s.nullable(s.string())),calibrated_at:s.optional(s.nullable(s.string()))}),me=s.object({enabled:s.boolean()});var H=class{fetch;host;constructor(e,t){this.fetch=e,this.host=t}info(){return this.fetch(`/management/info`,{schema:h})}get_calibration(){return this.fetch(`/management/calibration`,{schema:B})}set_calibration(e){return this.fetch(`/management/calibration`,{method:`PUT`,body:s.parse(B,e),schema:B})}get_calibration_state(){return this.fetch(`/management/calibration/state`,{schema:V})}set_calibration_enabled(e){return this.fetch(`/management/calibration/state`,{method:`PATCH`,body:s.parse(me,{enabled:e}),schema:V})}enable_calibration(){return this.set_calibration_enabled(!0)}disable_calibration(){return this.set_calibration_enabled(!1)}async datetime(){let e=await this.fetch(`/management/datetime`,{schema:s.object({epoch_seconds:s.number()})});return new Date(e.epoch_seconds*1e3)}setDatetime(e){return this.fetch(`/management/datetime`,{method:`POST`,body:{epoch_seconds:Math.floor(e.getTime()/1e3)},parseResponse:()=>{}})}getProjects(){return this.fetch(`/management/projects`,{schema:s.array(C)})}listImages(){return this.fetch(`/management/images`,{schema:s.array(s.string())})}getProject(e){return this.fetch(`/management/projects/${e}`,{schema:C})}createProject(e){return this.fetch(`/management/projects`,{method:`POST`,body:e,timeout:3e4,schema:C})}importProject(e,t){return this.fetch(`/management/projects/import`,{method:`POST`,body:e,headers:{"Content-Type":`application/x-tar`,"X-Project-Name":encodeURIComponent(t)},serializeBody:e=>e,timeout:6e5,schema:C})}updateProject(e,t){return this.fetch(`/management/projects/${e}`,{method:`PATCH`,body:s.parse(de,t),schema:C})}runProject(e,t=`normal`){return this.fetch(`/management/projects/${e}/run`,{method:`POST`,body:{mode:t},timeout:3e4})}stopProject(e){return this.fetch(`/management/projects/${e}/stop`,{method:`POST`,timeout:3e4})}getProjectPorts(e){return this.fetch(`/management/projects/${e}/ports`,{schema:s.array(b)})}setProjectPorts(e,t){return this.fetch(`/management/projects/${e}/ports`,{method:`PUT`,body:s.parse(s.array(b),t),timeout:3e4,schema:s.array(b)})}addProjectPort(e,t){return this.fetch(`/management/projects/${e}/ports`,{method:`POST`,body:s.parse(b,t),timeout:3e4,schema:s.array(b)})}deleteProjectPort(e,t){return this.fetch(`/management/projects/${e}/ports/${t}`,{method:`DELETE`,parseResponse:()=>{},timeout:3e4})}setProjectPassword(e,t){return this.fetch(`/management/projects/${e}/password`,{method:`POST`,body:s.parse(s.object({password:s.pipe(s.string(),s.minLength(1))}),{password:t}),parseResponse:()=>{},timeout:3e4})}getProjectAuthorizedKeys(e){return this.fetch(`/management/projects/${e}/authorized_keys`,{schema:s.array(s.string())})}setProjectAuthorizedKeys(e,t){return this.fetch(`/management/projects/${e}/authorized_keys`,{method:`PUT`,body:s.parse(s.array(ue),t),timeout:3e4,schema:s.array(s.string())})}deleteProject(e){return this.fetch(`/management/projects/${e}`,{method:`DELETE`,timeout:3e4})}upload_bundle(e,t=`bundle.raucb`){return this.fetch(`/management/update/upload`,{method:`POST`,body:e,headers:{"Content-Type":`application/octet-stream`,"X-Bundle-Filename":t},serializeBody:e=>e,timeout:6e5,schema:T})}bundle_info(){return this.fetch(`/management/update/bundle-info`,{schema:T})}install_update(){return this.fetch(`/management/update/install`,{method:`POST`,schema:pe})}update_status(){return this.fetch(`/management/update/status`,{schema:E})}reboot(){return this.fetch(`/management/reboot`,{method:`POST`,schema:D})}getProjectLogs(e,t){let n=t?.tail??1e3;return this.fetch(`/management/projects/${e}/logs?tail=${n}`,{schema:s.array(S)})}streamProjectLogs(e,t,n){let r=n?.tail??1e3,i=new AbortController,a=`http://${Q(this.host)}/v0/management/projects/${e}/logs/stream?tail=${r}`;return(async()=>{try{let e=await fetch(a,{signal:i.signal});if(!e.ok||e.body===null)throw Error(`Log stream failed: HTTP ${e.status}`);let n=e.body.getReader(),r=new TextDecoder,o=``,c=e=>{let n=`message`,r=``;for(let t of e.split(`
2
+ `))t.startsWith(`event:`)?n=t.slice(6).trim():t.startsWith(`data:`)&&(r+=t.slice(5).trim());if(n===`end`)return t.onEnd?.(),!0;if(n===`log`&&r.length>0){let e=s.safeParse(S,JSON.parse(r));e.success&&t.onLine(e.output)}return!1};for(;;){let{done:e,value:t}=await n.read();if(e)break;o+=r.decode(t,{stream:!0});let a=o.indexOf(`
3
+
4
+ `);for(;a!==-1;){let e=o.slice(0,a);if(o=o.slice(a+2),c(e)){i.abort();return}a=o.indexOf(`
5
+
6
+ `)}}}catch(e){if(i.signal.aborted)return;t.onError?.(e)}})(),{close:()=>i.abort()}}};const U=s.object({active:s.boolean(),kind:s.optional(s.picklist([`remote`,`project`])),label:s.optional(s.string()),project:s.optional(s.string()),since:s.optional(s.number())}),he=s.object({token:s.string()});async function W(e,t){let n=await fetch(`${e}/session/open`,{method:`POST`,body:JSON.stringify(t),headers:{"content-type":`application/json`},signal:AbortSignal.timeout(3e4)});if(!(n.status===404||n.status===405))return n.ok||await $(n,`POST`),s.parse(he,await n.json()).token}var G=class{fetch;constructor(e){this.fetch=e}state(){return this.fetch(`/session/state`,{schema:U})}};const K=s.array(s.pipe(s.number(),s.integer(),s.minValue(0),s.maxValue(255))),q={"com.can_config":t.canConfig,"com.rs232_config":t.rs232Config,"com.rs485_config":t.rs485Config,"com.spi_config":t.spiConfig,"com.i2c_config":t.i2cConfig,"com.uart_config":t.uartConfig,"com.can_rx":s.array(t.canFrame),"com.uart_rx":K,"com.rs232_rx":K,"com.rs485_rx":K,"gpio.state":s.union([n.digitalState,n.analogState]),"gpio.config":s.union([n.analogInputConfiguration,n.analogOutputConfiguration,n.digitalInputConfiguration,n.digitalOutputConfiguration,n.openDrainConfiguration]),"gpio.sync":n.syncState,"hmi.text":r.text,"hmi.colors":r.color,"hmi.progress":s.optional(r.progress),"hmi.play":r.playbackConfiguration,"hmi.knob":r.internalKnobEvent,"management.datetime":s.object({epoch_seconds:s.number()}),"management.calibration":B,"management.calibration_state":V,"management.project":w,"management.update_status":E,"power.dut":i.dutState,"power.usb":i.usbState,"relays.state":a.relayState,"relays.states":a.states,"session.state":U},ge=[`com.reset`,`gpio.reset`,`hmi.clear`,`hmi.reset`,`power.reset`,`relays.reset`],_e=s.object({type:s.string(),id:s.optional(s.pipe(s.number(),s.integer())),ts:s.number(),data:s.optional(s.unknown())}),J=5e3,Y=(...e)=>console.debug(`[at1000:events]`,...e);var X=class{host;socket;listeners=new Map;stateListeners=new Set;shouldReconnect=!1;reconnectDelayMs=500;reconnectTimer;_connected=!1;connectPromise;constructor(e){this.host=e}get connected(){return this._connected}async connect(e){if(typeof WebSocket>`u`)throw Error(`WebSocket is not available in this runtime (Node >= 22, Bun, or a browser is required)`);if(!this._connected){this.connectPromise??=this.openSocket(e?.timeout??J);try{await this.connectPromise}finally{this.connectPromise=void 0}}}close(){this.shouldReconnect=!1,this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0);let e=this.socket;this.socket=void 0,e!==void 0&&e.close(),this.setConnected(!1)}on(e,t){let n=this.listeners.get(e);return n===void 0&&(n=new Set,this.listeners.set(e,n)),n.add(t),()=>{let n=this.listeners.get(e);n!==void 0&&(n.delete(t),n.size===0&&this.listeners.delete(e))}}onStateChange(e){return this.stateListeners.add(e),()=>{this.stateListeners.delete(e)}}openSocket(e){let{promise:t,resolve:n,reject:r}=Promise.withResolvers(),i=`ws://${Q(this.host)}/v0/events`,a=new WebSocket(i);this.socket=a;let o=!1,s=setTimeout(()=>{o||(o=!0,a.close(),r(Error(`Timed out connecting to ${i}`)))},e);return a.onopen=()=>{this.socket!==a||o||(o=!0,clearTimeout(s),this.shouldReconnect=!0,this.reconnectDelayMs=500,this.setConnected(!0),Y(`open`,i),n())},a.onmessage=e=>{this.socket===a&&this.handleMessage(e.data)},a.onerror=()=>{Y(`error`,i),this.socket===a&&!o&&(o=!0,clearTimeout(s),r(Error(`Failed to connect to ${i}`)))},a.onclose=()=>{if(this.socket===a){if(Y(`close`,i),clearTimeout(s),this.setConnected(!1),!o){o=!0,r(Error(`Connection to ${i} closed before opening`));return}this.shouldReconnect&&this.scheduleReconnect()}},t}scheduleReconnect(){if(!this.shouldReconnect||this.reconnectTimer!==void 0)return;let e=this.reconnectDelayMs;Y(`reconnecting in`,e,`ms`),this.reconnectDelayMs=Math.min(this.reconnectDelayMs*2,8e3),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=void 0,this.shouldReconnect&&this.openSocket(J).catch(()=>{})},e)}handleMessage(e){if(typeof e!=`string`){Y(`drop: non-string frame`);return}let t;try{t=JSON.parse(e)}catch{Y(`drop: invalid JSON`,e);return}Y(`recv`,t);let n=s.safeParse(_e,t);if(!n.success){Y(`drop: malformed envelope`,n.issues);return}let{type:r,id:i,ts:a}=n.output;if(r in q){let e=q[r],t=s.safeParse(e,n.output.data);if(!t.success){Y(`drop: payload validation failed for`,r,t.issues);return}let o={type:r,id:i,ts:a,data:t.output};Y(`dispatch`,r),this.emitTo(r,o),this.emitTo(`*`,o);return}if(ge.includes(r)){let e={type:r,id:i,ts:a};Y(`dispatch (dataless)`,r),this.emitTo(r,e),this.emitTo(`*`,e);return}Y(`drop: unknown type`,r),this.emitTo(`*`,{type:r,id:i,ts:a,data:n.output.data})}emitTo(e,t){let n=this.listeners.get(e);if(n!==void 0)for(let e of n)try{e(t)}catch{}}setConnected(e){if(this._connected!==e){this._connected=e;for(let t of this.stateListeners)try{t(e)}catch{}}}},Z=class extends Error{code;detail;suggestion;path;constructor(e){let t=e.suggestion.replace(/\n/g,`
7
+ `);super(`${e.message} (${e.code})\n Suggestion: ${t}\n Path: ${e.path}`),this.code=e.code,this.detail=e.message,this.suggestion=e.suggestion,this.path=e.path}};function Q(e,t){let n=t||8080;if(e.startsWith(`[`))return e.includes(`]:`)?e:`${e}:${n}`;let r=e.indexOf(`:`);return r===-1?`${e}:${n}`:r===e.lastIndexOf(`:`)?e:`[${e}]:${n}`}function ve(e){if(e.startsWith(`[`)){let t=e.indexOf(`]`);return t===-1?e:e.slice(1,t)}let t=e.indexOf(`:`);return t!==-1&&t===e.lastIndexOf(`:`)?e.slice(0,t):e}function ye(e){if(typeof e!=`object`||!e)return!1;let t=e;return[`code`,`message`,`suggestion`,`path`].every(e=>typeof t[e]==`string`)}async function $(e,t){let n,r=e.headers.get(`content-type`);try{n=r?.includes(`application/json`)?await e.json():await e.text()}catch{n=`Failed to parse response body`}throw ye(n)?new Z(n):Error(`API request to ${t} ${e.url} failed with status ${e.status}: ${typeof n==`string`?n:JSON.stringify(n)}`)}var be=class e{host;info;token;sessionSupported;fetch;com;hmi;gpio;management;power;relays;events;session;constructor(e,s,c,l=!0){this.host=e,this.info=s,this.token=c,this.sessionSupported=l,this.fetch=(0,o.up)(fetch,()=>({baseUrl:`http://${Q(this.host)}/v0`,timeout:1e3,headers:this.token?{"X-AT1000-Session":this.token}:void 0,parseRejected:(e,t)=>$(e,t.method)})),this.com=new t.ComApi(this.fetch),this.hmi=new r.HmiApi(this.fetch),this.gpio=new n.GpioApi(this.fetch,this.info),this.management=new H(this.fetch,this.host),this.power=new i.PowerApi(this.fetch,this.info),this.relays=new a.RelaysApi(this.fetch),this.events=new X(this.host),this.session=new G(this.fetch)}async reset(){await this.com.reset(),await this.hmi.reset(),await this.gpio.reset(),await this.power.reset(),await this.relays.reset()}static async openBase(t,n,r){let i=typeof t==`string`?t:t.host,a=`http://${Q(i)}/v0`,s=await new H((0,o.up)(fetch,()=>({baseUrl:a,timeout:1e3,parseRejected:(e,t)=>$(e,t.method)})),i).info();if(n?.readonly)return new e(i,s);let c=await W(a,{kind:r?.kind??`remote`,label:n?.label??r?.defaultLabel,project:r?.project});return c===void 0?new e(i,s,void 0,!1):new e(i,s,c)}};Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return $}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return W}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return B}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return ve}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return U}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return Z}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return X}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return Q}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return G}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return be}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return E}});
8
+ //# sourceMappingURL=at1000-B_udjy0I.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"at1000-B_udjy0I.cjs","names":["v","v","v","canConfig","rs232Config","rs485Config","spiConfig","i2cConfig","uartConfig","canFrame","digitalState","analogState","analogInputConfiguration","analogOutputConfiguration","digitalInputConfiguration","digitalOutputConfiguration","openDrainConfiguration","syncState","text","color","progress","playbackConfiguration","internalKnobEvent","dutState","usbState","relayState","states","ComApi","HmiApi","GpioApi","PowerApi","RelaysApi"],"sources":["../src/api/management.ts","../src/api/session.ts","../src/api/events.ts","../src/at1000.ts"],"sourcesContent":["import { type UpFetch } from 'up-fetch';\nimport * as v from 'valibot';\n\nimport { format_host_for_url } from '../at1000';\n\nexport enum CommunicationInterfaceType {\n Can = 'can',\n Rs232 = 'rs232',\n Rs485 = 'rs485',\n I2c = 'i2c',\n Spi = 'spi',\n Uart = 'uart',\n}\nconst communicationInterface = v.enum(CommunicationInterfaceType);\n\nconst voltageRange = v.object({\n min: v.pipe(v.number(), v.minValue(-24), v.maxValue(24)),\n max: v.pipe(v.number(), v.minValue(-24), v.maxValue(24)),\n});\n\nconst currentRange = v.object({ max: v.number() });\n\nconst ioCapabilities = v.object({\n count: v.pipe(v.number(), v.minValue(1)),\n input_voltage: voltageRange,\n output_voltage: voltageRange,\n});\n\nconst relaysCapabilities = v.object({\n count: v.pipe(v.number(), v.minValue(0)),\n max_current: v.number(),\n max_voltage: v.number(),\n on_resistance: v.pipe(v.number(), v.minValue(0)),\n});\n\nconst usbCapability = v.object({\n type: v.picklist(['3.0', '2.0']),\n count: v.pipe(v.number(), v.minValue(1)),\n});\n\nconst ethernetCapability = v.object({\n type: v.picklist(['1000BASE-T', '100BASE-T', '10BASE-T']),\n count: v.pipe(v.number(), v.minValue(1)),\n});\n\nconst communicationCapability = v.object({\n type: communicationInterface,\n count: v.pipe(v.number(), v.minValue(1)),\n});\n\nconst powerSupplyCapabilities = v.object({\n count: v.pipe(v.number(), v.minValue(1)),\n output_voltage: voltageRange,\n current: currentRange,\n});\n\nconst currentMeasurementCapabilities = v.object({\n type: v.picklist(['low_precision', 'high_precision']),\n source: v.string(),\n resolution: v.pipe(v.number(), v.minValue(0)),\n});\n\nconst screenSize = v.object({\n width: v.pipe(v.number(), v.minValue(1)),\n height: v.pipe(v.number(), v.minValue(1)),\n});\n\nconst displayCapabilities = v.object({ type: v.string(), size: screenSize });\n\nconst inputCapabilities = v.object({ count: v.pipe(v.number(), v.minValue(0)), type: v.string() });\n\nconst audioCapabilities = v.object({ count: v.pipe(v.number(), v.minValue(0)), type: v.string() });\n\nconst hmiCapabilities = v.object({\n display: displayCapabilities,\n input: inputCapabilities,\n speaker: audioCapabilities,\n});\n\nconst capabilities = v.object({\n io: ioCapabilities,\n relays: relaysCapabilities,\n usb_ports: v.array(usbCapability),\n ethernet: v.array(ethernetCapability),\n communication: v.array(communicationCapability),\n power_supply: powerSupplyCapabilities,\n current_measurement: v.array(currentMeasurementCapabilities),\n hmi: hmiCapabilities,\n});\n\nconst info = v.object({\n model: v.string(),\n hostname: v.string(),\n serial_number: v.string(),\n firmware_version: v.string(),\n firmware_commit: v.string(),\n hardware_version: v.optional(v.number()),\n disk_used: v.number(),\n disk_total: v.number(),\n docker_used: v.number(),\n capabilities: capabilities,\n});\nexport type Information = v.InferOutput<typeof info>;\n\nconst projectStatus = v.picklist(['idle', 'running', 'error']);\nconst projectEventState = v.picklist(['created', 'started', 'stopped', 'removed', 'error']);\nconst portProtocol = v.picklist(['tcp', 'udp']);\nconst port = v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(65535));\nexport const portMapping = v.object({ host: port, container: port, proto: portProtocol });\nexport type PortMapping = v.InferOutput<typeof portMapping>;\nconst authorizedKey = v.pipe(\n v.string(),\n v.trim(),\n v.regex(/^(ssh-|ecdsa-sha2-|sk-ssh-|sk-ecdsa-)/, 'Not a recognized SSH public key'),\n);\nexport const runMode = v.picklist(['normal', 'development']);\nexport type RunMode = v.InferOutput<typeof runMode>;\n\nconst logLine = v.object({ stream: v.picklist(['stdout', 'stderr']), line: v.string() });\nexport type LogLine = v.InferOutput<typeof logLine>;\n\nexport const project = v.object({\n name: v.string(),\n path: v.string(),\n id: v.string(),\n image: v.string(),\n is_default: v.boolean(),\n ports: v.optional(v.array(portMapping), []),\n command: v.optional(v.string(), ''),\n state: projectStatus,\n mode: v.optional(runMode),\n});\nexport type Project = v.InferOutput<typeof project>;\n\n// Merged management.project realtime event, discriminated on `change`.\nexport const projectEvent = v.variant('change', [\n v.object({ change: v.literal('state_changed'), id: v.string(), state: projectEventState }),\n v.object({ change: v.literal('renamed'), id: v.string(), name: v.string() }),\n v.object({ change: v.literal('default_changed'), id: v.string(), is_default: v.boolean() }),\n]);\nexport type ProjectEvent = v.InferOutput<typeof projectEvent>;\n\nconst projectPatch = v.partial(v.pick(project, ['name', 'is_default', 'command']));\nexport type ProjectPatch = v.InferOutput<typeof projectPatch>;\n\nconst projectTemplate = v.picklist(['nodejs', 'python']);\nexport type ProjectTemplate = v.InferOutput<typeof projectTemplate>;\n\nconst createProject = v.object({\n name: v.string(),\n template: projectTemplate,\n command: v.optional(v.string()),\n});\nexport type CreateProject = v.InferOutput<typeof createProject>;\n\nconst bundleInfo = v.object({\n bundle: v.string(),\n version: v.string(),\n compatible: v.string(),\n description: v.string(),\n});\nexport type BundleInfo = v.InferOutput<typeof bundleInfo>;\n\nconst installResponse = v.object({ message: v.string(), bundle: v.string() });\nexport type InstallResponse = v.InferOutput<typeof installResponse>;\n\nexport const updateStatus = v.object({\n state: v.picklist(['idle', 'installing', 'failed']),\n progress: v.number(),\n message: v.string(),\n error: v.string(),\n});\nexport type UpdateStatus = v.InferOutput<typeof updateStatus>;\n\nconst rebootResponse = v.object({ message: v.string() });\nexport type RebootResponse = v.InferOutput<typeof rebootResponse>;\n\nconst finiteNumber = v.pipe(v.number(), v.finite());\nconst positiveFiniteNumber = v.pipe(v.number(), v.finite(), v.minValue(Number.MIN_VALUE));\nconst channelId = v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(31));\nconst relayId = v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(7));\nconst usbPortId = v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(1));\nconst calibrationStatus = v.picklist(['pass', 'fail']);\n\nconst calibrationPoint = v.object({\n set: finiteNumber,\n measured: finiteNumber,\n reading: v.optional(finiteNumber),\n});\nexport type CalibrationPoint = v.InferOutput<typeof calibrationPoint>;\n\nconst channelCalibration = v.object({\n channel: channelId,\n points: v.array(calibrationPoint),\n max_error: finiteNumber,\n avg_error: finiteNumber,\n});\nexport type ChannelCalibration = v.InferOutput<typeof channelCalibration>;\n\nconst dutPsuCalibration = v.object({\n voltage: v.object({\n points: v.array(calibrationPoint),\n max_error: finiteNumber,\n avg_error: finiteNumber,\n }),\n current: v.object({\n load_ohms: positiveFiniteNumber,\n points: v.array(calibrationPoint),\n max_error: finiteNumber,\n avg_error: finiteNumber,\n }),\n});\nexport type DutPsuCalibration = v.InferOutput<typeof dutPsuCalibration>;\n\nconst currentTestResult = v.object({\n load_ohms: positiveFiniteNumber,\n voltage_applied: finiteNumber,\n measured_current: finiteNumber,\n expected_current: finiteNumber,\n error_percent: finiteNumber,\n});\n/** @deprecated See {@link currentTestResult}. */\nexport type CurrentTestResult = v.InferOutput<typeof currentTestResult>;\n\nconst relayTestResult = v.object({\n relay: relayId,\n status: calibrationStatus,\n test_voltage: finiteNumber,\n closed_current: finiteNumber,\n open_current: finiteNumber,\n calibration: v.optional(channelCalibration),\n});\nexport type RelayTestResult = v.InferOutput<typeof relayTestResult>;\n\nconst usbTestResult = v.object({\n port: usbPortId,\n status: calibrationStatus,\n voltage: finiteNumber,\n /** @deprecated Legacy field; only present on calibrations from older devices. */\n current_tests: v.optional(v.array(currentTestResult)),\n});\nexport type UsbTestResult = v.InferOutput<typeof usbTestResult>;\n\nexport const calibrationData = v.object({\n schema_version: v.literal(1),\n tool_version: v.string(),\n device_id: v.string(),\n timestamp: v.string(),\n firmware_version: v.string(),\n dac: v.array(channelCalibration),\n adc_positive: v.array(channelCalibration),\n adc_negative: v.optional(v.array(channelCalibration)),\n dut_psu: dutPsuCalibration,\n relays: v.array(relayTestResult),\n usb: v.array(usbTestResult),\n});\nexport type CalibrationData = v.InferOutput<typeof calibrationData>;\n\nexport const calibrationState = v.object({\n enabled: v.boolean(),\n loaded: v.boolean(),\n last_error: v.optional(v.nullable(v.string())),\n calibrated_at: v.optional(v.nullable(v.string())),\n});\nexport type CalibrationState = v.InferOutput<typeof calibrationState>;\n\nconst calibrationStatePatch = v.object({ enabled: v.boolean() });\n\n/** @hideconstructor */\nexport class ManagementApi {\n constructor(private readonly fetch: UpFetch, private readonly host: string) {}\n\n /**\n * Gets device information and capabilities.\n * @returns The device information.\n */\n info() {\n return this.fetch('/management/info', { schema: info });\n }\n\n /**\n * Gets the loaded calibration data.\n * @returns The canonical calibration data stored on the device.\n */\n get_calibration(): Promise<CalibrationData> {\n return this.fetch('/management/calibration', { schema: calibrationData });\n }\n\n /**\n * Uploads and activates calibration data.\n * @param data The full calibration report to store on the device.\n */\n set_calibration(data: CalibrationData): Promise<CalibrationData> {\n return this.fetch('/management/calibration', {\n method: 'PUT',\n body: v.parse(calibrationData, data),\n schema: calibrationData,\n });\n }\n\n /**\n * Gets runtime calibration enable/load state.\n */\n get_calibration_state(): Promise<CalibrationState> {\n return this.fetch('/management/calibration/state', { schema: calibrationState });\n }\n\n /**\n * Enables or disables runtime calibration correction.\n * @param enabled Whether calibration correction should be applied.\n */\n set_calibration_enabled(enabled: boolean): Promise<CalibrationState> {\n return this.fetch('/management/calibration/state', {\n method: 'PATCH',\n body: v.parse(calibrationStatePatch, { enabled }),\n schema: calibrationState,\n });\n }\n\n /** Enables runtime calibration correction. */\n enable_calibration(): Promise<CalibrationState> {\n return this.set_calibration_enabled(true);\n }\n\n /** Disables runtime calibration correction. */\n disable_calibration(): Promise<CalibrationState> {\n return this.set_calibration_enabled(false);\n }\n\n /**\n * Gets the current date and time of the device.\n * @returns The current date and time.\n */\n async datetime(): Promise<Date> {\n const time = await this.fetch('/management/datetime', {\n schema: v.object({ epoch_seconds: v.number() }),\n });\n return new Date(time.epoch_seconds * 1000);\n }\n\n /**\n * Sets the date and time of the device.\n *\n * This will only work if the device is not connected to the internet.\n *\n * @param datetime The date and time to set.\n */\n setDatetime(datetime: Date): Promise<void> {\n return this.fetch('/management/datetime', {\n method: 'POST',\n body: { epoch_seconds: Math.floor(datetime.getTime() / 1000) },\n parseResponse: () => {},\n });\n }\n\n /**\n * Gets a list of all projects.\n * @returns An array of projects.\n */\n getProjects() {\n return this.fetch('/management/projects', { schema: v.array(project) });\n }\n\n /**\n * Lists the AT1000 base images present on the device.\n * @returns Repo tags of on-device base images (e.g. \"localhost/at1000-base:latest\").\n */\n listImages() {\n return this.fetch('/management/images', { schema: v.array(v.string()) });\n }\n\n /**\n * Gets a single project by id.\n * @param projectId The project slug id.\n * @returns The project info.\n */\n getProject(projectId: string) {\n return this.fetch(`/management/projects/${projectId}`, { schema: project });\n }\n\n /**\n * Creates a new project from a preinstalled template.\n * @param newProject The project creation data.\n */\n createProject(newProject: CreateProject) {\n return this.fetch('/management/projects', {\n method: 'POST',\n body: newProject,\n timeout: 30_000,\n schema: project,\n });\n }\n\n /**\n * Imports a project from a container image archive (.tar / .tar.gz).\n * @param file The image archive to import.\n * @param name The project name (sent percent-encoded; the server decodes it).\n * @returns The created project.\n */\n importProject(file: Blob, name: string) {\n return this.fetch('/management/projects/import', {\n method: 'POST',\n body: file,\n headers: { 'Content-Type': 'application/x-tar', 'X-Project-Name': encodeURIComponent(name) },\n serializeBody: (b) => b as any,\n timeout: 600_000,\n schema: project,\n });\n }\n\n /**\n * Patches an existing project (`name`, `is_default`, `command`). An empty `command` clears it (entrypoint autodetect).\n * @param projectId The project slug id.\n * @param data The data to patch the project with.\n */\n updateProject(projectId: string, data: ProjectPatch) {\n return this.fetch(`/management/projects/${projectId}`, {\n method: 'PATCH',\n body: v.parse(projectPatch, data),\n schema: project,\n });\n }\n\n /**\n * Runs a project.\n * @param projectId The project slug id.\n * @param mode The run mode: 'normal' (default) or 'development' (starts sshd).\n */\n runProject(projectId: string, mode: RunMode = 'normal') {\n return this.fetch(`/management/projects/${projectId}/run`, {\n method: 'POST',\n body: { mode },\n timeout: 30_000,\n });\n }\n\n /**\n * Stops a running project.\n * @param projectId The project slug id.\n */\n stopProject(projectId: string) {\n return this.fetch(`/management/projects/${projectId}/stop`, {\n method: 'POST',\n timeout: 30_000,\n });\n }\n\n /**\n * Gets the port mappings for a project.\n * @param projectId The project slug id.\n * @returns The project's port mappings.\n */\n getProjectPorts(projectId: string) {\n return this.fetch(`/management/projects/${projectId}/ports`, { schema: v.array(portMapping) });\n }\n\n /**\n * Replaces the full set of port mappings for a project.\n * @param projectId The project slug id.\n * @param ports The complete list of port mappings.\n * @returns The updated port mappings.\n */\n setProjectPorts(projectId: string, ports: PortMapping[]) {\n return this.fetch(`/management/projects/${projectId}/ports`, {\n method: 'PUT',\n body: v.parse(v.array(portMapping), ports),\n timeout: 30_000,\n schema: v.array(portMapping),\n });\n }\n\n /**\n * Adds a single port mapping to a project.\n * @param projectId The project slug id.\n * @param mapping The port mapping to add.\n * @returns The updated port mappings.\n */\n addProjectPort(projectId: string, mapping: PortMapping) {\n return this.fetch(`/management/projects/${projectId}/ports`, {\n method: 'POST',\n body: v.parse(portMapping, mapping),\n timeout: 30_000,\n schema: v.array(portMapping),\n });\n }\n\n /**\n * Removes the port mapping for a host port from a project.\n * @param projectId The project slug id.\n * @param hostPort The host port to unmap.\n */\n deleteProjectPort(projectId: string, hostPort: number) {\n return this.fetch(`/management/projects/${projectId}/ports/${hostPort}`, {\n method: 'DELETE',\n parseResponse: () => {},\n timeout: 30_000,\n });\n }\n\n /**\n * Sets the password of the project's dev user (used for SSH password login in\n * development mode).\n * @param projectId The project slug id.\n * @param password The new password.\n */\n setProjectPassword(projectId: string, password: string) {\n return this.fetch(`/management/projects/${projectId}/password`, {\n method: 'POST',\n body: v.parse(v.object({ password: v.pipe(v.string(), v.minLength(1)) }), { password }),\n parseResponse: () => {},\n timeout: 30_000,\n });\n }\n\n /**\n * Gets the SSH authorized_keys for the project's dev user.\n * @param projectId The project slug id.\n * @returns The list of authorized public keys.\n */\n getProjectAuthorizedKeys(projectId: string) {\n return this.fetch(`/management/projects/${projectId}/authorized_keys`, {\n schema: v.array(v.string()),\n });\n }\n\n /**\n * Replaces the SSH authorized_keys for the project's dev user.\n * @param projectId The project slug id.\n * @param keys The complete list of authorized public keys.\n * @returns The updated list of authorized public keys.\n */\n setProjectAuthorizedKeys(projectId: string, keys: string[]) {\n return this.fetch(`/management/projects/${projectId}/authorized_keys`, {\n method: 'PUT',\n body: v.parse(v.array(authorizedKey), keys),\n timeout: 30_000,\n schema: v.array(v.string()),\n });\n }\n\n /**\n * Deletes a project.\n * @param projectId The project slug id.\n */\n deleteProject(projectId: string) {\n return this.fetch(`/management/projects/${projectId}`, { method: 'DELETE', timeout: 30_000 });\n }\n\n /**\n * Uploads a firmware bundle to the device and inspects it.\n * The bundle is staged for installation but not installed yet.\n * @param file The firmware bundle file (.raucb).\n * @param filename Optional filename for the bundle. Defaults to 'bundle.raucb'.\n * @returns Bundle metadata including version information.\n */\n upload_bundle(file: Blob, filename = 'bundle.raucb') {\n return this.fetch('/management/update/upload', {\n method: 'POST',\n body: file,\n headers: { 'Content-Type': 'application/octet-stream', 'X-Bundle-Filename': filename },\n serializeBody: (b) => b as any,\n timeout: 600_000,\n schema: bundleInfo,\n });\n }\n\n /**\n * Gets metadata for the currently staged firmware bundle.\n * @returns Bundle metadata including version information.\n */\n bundle_info() {\n return this.fetch('/management/update/bundle-info', { schema: bundleInfo });\n }\n\n /**\n * Installs a previously uploaded firmware bundle.\n * @returns The install response with message and bundle name.\n */\n install_update() {\n return this.fetch('/management/update/install', { method: 'POST', schema: installResponse });\n }\n\n /**\n * Gets the current firmware update status.\n * @returns The update status including state, progress, message and error.\n */\n update_status() {\n return this.fetch('/management/update/status', { schema: updateStatus });\n }\n\n /**\n * Reboots the device.\n * @returns The reboot response message.\n */\n reboot() {\n return this.fetch('/management/reboot', { method: 'POST', schema: rebootResponse });\n }\n\n /**\n * Gets the current run's container logs for a project (one-shot).\n *\n * Lines are scoped server-side to the container's most recent start: a running\n * project returns the live run so far, an idle/stopped project the last run.\n * stdout and stderr are merged into one chronological array; each entry's\n * `stream` field says which it came from.\n *\n * @param projectId The project slug id.\n * @param options.tail Max number of trailing lines (default 1000, clamped 1..10000).\n * @returns The current run's log lines, in order.\n */\n getProjectLogs(projectId: string, options?: { tail?: number }) {\n const tail = options?.tail ?? 1000;\n return this.fetch(`/management/projects/${projectId}/logs?tail=${tail}`, {\n schema: v.array(logLine),\n });\n }\n\n /**\n * Streams the current run's container logs for a project over SSE.\n *\n * Replays the current run's recent lines (bounded by `tail`) then follows live\n * with zero gap. `onLine` fires once per line (stdout and stderr merged in\n * order); `onEnd` fires when the follow ends (the container stopped, or an\n * idle project's last-run replay completed). No auto-reconnect.\n *\n * @param projectId The project slug id.\n * @param handlers Line/end/error callbacks.\n * @param options.tail Max number of trailing lines (default 1000, clamped 1..10000).\n * @returns A handle whose `close()` aborts the stream.\n */\n streamProjectLogs(\n projectId: string,\n handlers: {\n onLine: (line: LogLine) => void;\n onEnd?: () => void;\n onError?: (err: unknown) => void;\n },\n options?: { tail?: number },\n ): { close: () => void } {\n const tail = options?.tail ?? 1000;\n const controller = new AbortController();\n const url = `http://${format_host_for_url(this.host)}`\n + `/v0/management/projects/${projectId}/logs/stream?tail=${tail}`;\n\n void (async () => {\n try {\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok || response.body === null) {\n throw new Error(`Log stream failed: HTTP ${response.status}`);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n const dispatch = (record: string): boolean => {\n let event = 'message';\n let data = '';\n for (const rawLine of record.split('\\n')) {\n if (rawLine.startsWith('event:')) {\n event = rawLine.slice('event:'.length).trim();\n } else if (rawLine.startsWith('data:')) {\n data += rawLine.slice('data:'.length).trim();\n }\n }\n if (event === 'end') {\n handlers.onEnd?.();\n return true; // terminal\n }\n if (event === 'log' && data.length > 0) {\n const parsed = v.safeParse(logLine, JSON.parse(data));\n if (parsed.success) {\n handlers.onLine(parsed.output);\n }\n }\n return false;\n };\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n buffer += decoder.decode(value, { stream: true });\n let separator = buffer.indexOf('\\n\\n');\n while (separator !== -1) {\n const record = buffer.slice(0, separator);\n buffer = buffer.slice(separator + 2);\n if (dispatch(record)) {\n controller.abort();\n return;\n }\n separator = buffer.indexOf('\\n\\n');\n }\n }\n } catch (err) {\n if (controller.signal.aborted) {\n return; // caller closed, or terminal end handled above\n }\n handlers.onError?.(err);\n }\n })();\n\n return { close: () => controller.abort() };\n }\n}\n","import type { UpFetch } from 'up-fetch';\nimport * as v from 'valibot';\n\nimport { parseApiError } from '../at1000';\n\nexport const sessionState = v.object({\n active: v.boolean(),\n kind: v.optional(v.picklist(['remote', 'project'])),\n label: v.optional(v.string()),\n project: v.optional(v.string()),\n since: v.optional(v.number()),\n});\nexport type SessionState = v.InferOutput<typeof sessionState>;\n\n/** Body for `POST /v0/session/open`. */\nexport type SessionOpenBody = { kind: 'remote' | 'project'; label?: string; project?: string };\n\nconst openResponse = v.object({ token: v.string() });\n\n/**\n * Opens an exclusive session on the device (last-open-wins). Returns the minted\n * session token, or `undefined` when the server predates the session model.\n *\n * Deliberately uses a direct `fetch` (not the shared up-fetch client) so the\n * response status is inspectable before any structured-error handler throws:\n * pre-session servers answer the unmatched route with 404/405, which we degrade\n * to `undefined` rather than surface as a failure. A 30 s timeout (not the SDK\n * default 1 s) covers a remote-kind open synchronously stopping any running\n * project container; the host is already known-alive from the preceding info\n * probe, so the long timeout never hangs on dead hosts.\n */\nexport async function open_session(\n baseUrl: string,\n body: SessionOpenBody,\n): Promise<string | undefined> {\n const response = await fetch(`${baseUrl}/session/open`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json' },\n signal: AbortSignal.timeout(30_000),\n });\n\n if (response.status === 404 || response.status === 405) {\n return undefined; // legacy server without the session model\n }\n if (!response.ok) {\n await parseApiError(response, 'POST');\n }\n\n return v.parse(openResponse, await response.json()).token;\n}\n\n/** @hideconstructor */\nexport class SessionApi {\n constructor(private readonly fetch: UpFetch) {}\n\n /**\n * Reads the current device session state.\n * @returns The active holder (kind/label/project/since) or `{ active: false }`.\n */\n state(): Promise<SessionState> {\n return this.fetch('/session/state', { schema: sessionState });\n }\n}\n","import * as v from 'valibot';\n\nimport { format_host_for_url } from '../at1000';\nimport {\n canConfig,\n canFrame,\n i2cConfig,\n rs232Config,\n rs485Config,\n spiConfig,\n uartConfig,\n} from './com';\nimport {\n analogInputConfiguration,\n analogOutputConfiguration,\n analogState,\n digitalInputConfiguration,\n digitalOutputConfiguration,\n digitalState,\n openDrainConfiguration,\n syncState,\n} from './gpio';\nimport { color, internalKnobEvent, playbackConfiguration, progress, text } from './hmi';\nimport { calibrationData, calibrationState, projectEvent, updateStatus } from './management';\nimport { dutState, usbState } from './power';\nimport { relayState, states } from './relays';\nimport { sessionState } from './session';\n\n// Receive-side serial bytes. Distinct from com's `serialData`, whose string->bytes\n// transform is for request bodies; events deliver raw byte arrays.\nconst rxBytes = v.array(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(255)));\n\n// Schema for the `data` payload of each event, keyed by the wire `type` literal.\nconst eventDataSchemas = {\n 'com.can_config': canConfig,\n 'com.rs232_config': rs232Config,\n 'com.rs485_config': rs485Config,\n 'com.spi_config': spiConfig,\n 'com.i2c_config': i2cConfig,\n 'com.uart_config': uartConfig,\n 'com.can_rx': v.array(canFrame),\n 'com.uart_rx': rxBytes,\n 'com.rs232_rx': rxBytes,\n 'com.rs485_rx': rxBytes,\n 'gpio.state': v.union([digitalState, analogState]),\n 'gpio.config': v.union([\n analogInputConfiguration,\n analogOutputConfiguration,\n digitalInputConfiguration,\n digitalOutputConfiguration,\n openDrainConfiguration,\n ]),\n // Mirrors the glz::meta of gpio::sync_hold_state_t (apps/server/src/api/resources/gpio.hpp).\n 'gpio.sync': syncState,\n 'hmi.text': text,\n 'hmi.colors': color,\n // `data` absent (progress hidden) is accepted by v.optional.\n 'hmi.progress': v.optional(progress),\n 'hmi.play': playbackConfiguration,\n 'hmi.knob': internalKnobEvent,\n 'management.datetime': v.object({ epoch_seconds: v.number() }),\n 'management.calibration': calibrationData,\n 'management.calibration_state': calibrationState,\n 'management.project': projectEvent,\n 'management.update_status': updateStatus,\n 'power.dut': dutState,\n 'power.usb': usbState,\n 'relays.state': relayState,\n 'relays.states': states,\n 'session.state': sessionState,\n} as const;\n\nconst dataLessEvents = [\n 'com.reset',\n 'gpio.reset',\n 'hmi.clear',\n 'hmi.reset',\n 'power.reset',\n 'relays.reset',\n] as const;\n\nconst envelope = v.object({\n type: v.string(),\n id: v.optional(v.pipe(v.number(), v.integer())),\n ts: v.number(),\n data: v.optional(v.unknown()),\n});\n\ntype EventDataSchemas = typeof eventDataSchemas;\n\n/** Every event `type` literal the server can emit. */\nexport type EventType = keyof EventDataSchemas | (typeof dataLessEvents)[number];\n\ntype PayloadEvent = {\n [K in keyof EventDataSchemas]: {\n type: K;\n id?: number;\n ts: number;\n data: v.InferOutput<EventDataSchemas[K]>;\n };\n}[keyof EventDataSchemas];\n\ntype DataLessEvent = { type: (typeof dataLessEvents)[number]; id?: number; ts: number };\n\n/** A fully-typed device event for a known `type`. */\nexport type DeviceEvent = PayloadEvent | DataLessEvent;\n\n/**\n * Shape delivered to `'*'` listeners. Forward-compatible: a newer server may emit\n * a `type` this SDK does not model, surfaced here with an untyped `data`.\n */\nexport type AnyDeviceEvent = DeviceEvent | {\n type: string;\n id?: number;\n ts: number;\n data?: unknown;\n};\n\ntype EventListener = (event: AnyDeviceEvent) => void;\ntype StateListener = (connected: boolean) => void;\n\nconst reconnectBaseDelayMs = 500;\nconst reconnectMaxDelayMs = 8_000;\nconst defaultConnectTimeoutMs = 5_000;\n\nconst debugLog = (...args: unknown[]): void => console.debug('[at1000:events]', ...args);\n\n/** @hideconstructor */\nexport class EventsApi {\n private socket: WebSocket | undefined;\n private readonly listeners = new Map<string, Set<EventListener>>();\n private readonly stateListeners = new Set<StateListener>();\n private shouldReconnect = false;\n private reconnectDelayMs = reconnectBaseDelayMs;\n private reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n private _connected = false;\n private connectPromise: Promise<void> | undefined;\n\n constructor(private readonly host: string) {}\n\n /** Whether the WebSocket is currently open. */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Open the event stream. Resolves once the socket is open, rejects on failure or\n * after `timeout` ms. A no-op while already connected. Auto-reconnect arms only\n * after the first successful open, so a failed initial `connect()` (e.g. old\n * firmware without `/v0/events`) rejects without scheduling retries.\n */\n async connect(options?: { timeout?: number }): Promise<void> {\n if (typeof WebSocket === 'undefined') {\n throw new Error(\n 'WebSocket is not available in this runtime (Node >= 22, Bun, or a browser is required)',\n );\n }\n if (this._connected) {\n return;\n }\n // Coalesce concurrent opens: the layout holder-banner effect and the\n // projects page both call connect() on the shared per-client socket. Without\n // this a second caller starts a second openSocket() that orphans the first\n // (leaked WS + a connect() that never settles). Reuse the in-flight open.\n this.connectPromise ??= this.openSocket(options?.timeout ?? defaultConnectTimeoutMs);\n try {\n await this.connectPromise;\n } finally {\n this.connectPromise = undefined;\n }\n }\n\n /** Close the stream and cancel any pending reconnect. */\n close(): void {\n this.shouldReconnect = false;\n if (this.reconnectTimer !== undefined) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n const socket = this.socket;\n this.socket = undefined;\n if (socket !== undefined) {\n socket.close();\n }\n this.setConnected(false);\n }\n\n /**\n * Subscribe to an event `type`, or `'*'` for every event. Returns an unsubscribe\n * function. Listeners survive reconnects and are isolated: a throwing listener\n * does not break dispatch to the others.\n */\n on<T extends EventType>(\n type: T,\n listener: (event: Extract<DeviceEvent, { type: T }>) => void,\n ): () => void;\n on(type: '*', listener: (event: AnyDeviceEvent) => void): () => void;\n on(type: string, listener: (event: any) => void): () => void {\n let set = this.listeners.get(type);\n if (set === undefined) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(listener);\n return () => {\n const current = this.listeners.get(type);\n if (current === undefined) {\n return;\n }\n current.delete(listener);\n if (current.size === 0) {\n this.listeners.delete(type);\n }\n };\n }\n\n /** Subscribe to connection state changes. Returns an unsubscribe function. */\n onStateChange(listener: StateListener): () => void {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n private openSocket(timeoutMs: number): Promise<void> {\n const { promise, resolve, reject } = Promise.withResolvers<void>();\n const url = `ws://${format_host_for_url(this.host)}/v0/events`;\n const socket = new WebSocket(url);\n this.socket = socket;\n\n let settled = false;\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n socket.close();\n reject(new Error(`Timed out connecting to ${url}`));\n }\n }, timeoutMs);\n\n socket.onopen = () => {\n if (this.socket !== socket || settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n this.shouldReconnect = true;\n this.reconnectDelayMs = reconnectBaseDelayMs;\n this.setConnected(true);\n debugLog('open', url);\n resolve();\n };\n\n socket.onmessage = (event) => {\n if (this.socket === socket) {\n this.handleMessage(event.data);\n }\n };\n\n socket.onerror = () => {\n debugLog('error', url);\n if (this.socket === socket && !settled) {\n settled = true;\n clearTimeout(timer);\n reject(new Error(`Failed to connect to ${url}`));\n }\n };\n\n socket.onclose = () => {\n if (this.socket !== socket) {\n return;\n }\n debugLog('close', url);\n clearTimeout(timer);\n this.setConnected(false);\n if (!settled) {\n settled = true;\n reject(new Error(`Connection to ${url} closed before opening`));\n return;\n }\n if (this.shouldReconnect) {\n this.scheduleReconnect();\n }\n };\n\n return promise;\n }\n\n private scheduleReconnect(): void {\n if (!this.shouldReconnect || this.reconnectTimer !== undefined) {\n return;\n }\n const delay = this.reconnectDelayMs;\n debugLog('reconnecting in', delay, 'ms');\n this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, reconnectMaxDelayMs);\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = undefined;\n if (!this.shouldReconnect) {\n return;\n }\n // A failed attempt rejects this promise; onclose then schedules the next retry.\n this.openSocket(defaultConnectTimeoutMs).catch(() => {});\n }, delay);\n }\n\n private handleMessage(data: unknown): void {\n if (typeof data !== 'string') {\n debugLog('drop: non-string frame');\n return;\n }\n let raw: unknown;\n try {\n raw = JSON.parse(data);\n } catch {\n debugLog('drop: invalid JSON', data);\n return;\n }\n\n debugLog('recv', raw);\n const parsed = v.safeParse(envelope, raw);\n if (!parsed.success) {\n debugLog('drop: malformed envelope', parsed.issues);\n return;\n }\n const { type, id, ts } = parsed.output;\n\n if (type in eventDataSchemas) {\n const schema = eventDataSchemas[type as keyof EventDataSchemas] as v.GenericSchema;\n const payload = v.safeParse(schema, parsed.output.data);\n if (!payload.success) {\n debugLog('drop: payload validation failed for', type, payload.issues);\n return;\n }\n const event: AnyDeviceEvent = { type, id, ts, data: payload.output };\n debugLog('dispatch', type);\n this.emitTo(type, event);\n this.emitTo('*', event);\n return;\n }\n\n if ((dataLessEvents as readonly string[]).includes(type)) {\n const event: AnyDeviceEvent = { type, id, ts };\n debugLog('dispatch (dataless)', type);\n this.emitTo(type, event);\n this.emitTo('*', event);\n return;\n }\n\n // Unknown type: deliver the raw envelope to '*' only (server may be newer).\n debugLog('drop: unknown type', type);\n this.emitTo('*', { type, id, ts, data: parsed.output.data });\n }\n\n private emitTo(key: string, event: AnyDeviceEvent): void {\n const set = this.listeners.get(key);\n if (set === undefined) {\n return;\n }\n for (const listener of set) {\n try {\n listener(event);\n } catch {\n // One throwing listener must not break dispatch to the others.\n }\n }\n }\n\n private setConnected(connected: boolean): void {\n if (this._connected === connected) {\n return;\n }\n this._connected = connected;\n for (const listener of this.stateListeners) {\n try {\n listener(connected);\n } catch {\n // Ignore throwing state listeners.\n }\n }\n }\n}\n","import { up, type UpFetch } from 'up-fetch';\n\nimport { ComApi } from './api/com';\nimport { EventsApi } from './api/events';\nimport { GpioApi } from './api/gpio';\nimport { HmiApi } from './api/hmi';\nimport { type Information, ManagementApi } from './api/management';\nimport { PowerApi } from './api/power';\nimport { RelaysApi } from './api/relays';\nimport { open_session, SessionApi } from './api/session';\n\n/** Structured error thrown for AT1000 API responses carrying `{ code, message, suggestion, path }`. */\nexport class ApiError extends Error {\n readonly code: string;\n /** Raw human-readable server message (without code/suggestion/path). */\n readonly detail: string;\n readonly suggestion: string;\n readonly path: string;\n\n constructor(body: { code: string; message: string; suggestion: string; path: string }) {\n const formattedSuggestion = body.suggestion.replace(/\\n/g, '\\n ');\n super(\n `${body.message} (${body.code})\\n Suggestion: ${formattedSuggestion}\\n Path: ${body.path}`,\n );\n this.code = body.code;\n this.detail = body.message;\n this.suggestion = body.suggestion;\n this.path = body.path;\n }\n}\n\nconst DEFAULT_PORT = 8080;\n\n/**\n * Formats a host into a URL authority. Returns `host` unchanged when it already\n * carries an explicit port; otherwise appends `fallbackPort` (the device's\n * mDNS-advertised port when known, else the default 8080). Bare IPv6 literals\n * are bracketed. Idempotent: re-formatting an authority is a no-op.\n */\nexport function format_host_for_url(host: string, fallbackPort?: number): string {\n const port = fallbackPort || DEFAULT_PORT;\n if (host.startsWith('[')) {\n return host.includes(']:') ? host : `${host}:${port}`;\n }\n const firstColon = host.indexOf(':');\n if (firstColon === -1) {\n return `${host}:${port}`;\n }\n if (firstColon === host.lastIndexOf(':')) {\n return host; // exactly one colon: host:port already specified\n }\n return `[${host}]:${port}`; // bare IPv6 literal, no port\n}\n\n/** Returns the bare host (IP or hostname) from a URL authority, dropping any port. */\nexport function host_without_port(host: string): string {\n if (host.startsWith('[')) {\n const end = host.indexOf(']');\n return end === -1 ? host : host.slice(1, end); // [ipv6] / [ipv6]:port -> ipv6\n }\n const firstColon = host.indexOf(':');\n if (firstColon !== -1 && firstColon === host.lastIndexOf(':')) {\n return host.slice(0, firstColon); // host:port -> host\n }\n return host; // bare hostname/IPv4 or bare IPv6 literal\n}\n\n/** Type guard for the AT1000 structured error body. */\nfunction isApiErrorBody(\n value: unknown,\n): value is { code: string; message: string; suggestion: string; path: string } {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const record = value as Record<string, unknown>;\n return ['code', 'message', 'suggestion', 'path'].every((key) => typeof record[key] === 'string');\n}\n\n/**\n * Shared rejection handler for every AT1000 up-fetch client. Parses a structured\n * `{ code, message, suggestion, path }` body into an {@link ApiError}, otherwise\n * throws a generic error carrying the HTTP status. Written once so the per-client\n * factory and `open_session` share one code path.\n */\nexport async function parseApiError(response: Response, method: string): Promise<never> {\n let errorData: unknown;\n const contentType = response.headers.get('content-type');\n try {\n errorData = contentType?.includes('application/json')\n ? await response.json()\n : await response.text();\n } catch {\n errorData = 'Failed to parse response body';\n }\n\n if (isApiErrorBody(errorData)) {\n throw new ApiError(errorData);\n }\n\n throw new Error(\n `API request to ${method} ${response.url} failed with status ${response.status}: ${\n typeof errorData === 'string' ? errorData : JSON.stringify(errorData)\n }`,\n );\n}\n\n/** A device discovered on the network but not yet opened. */\nexport interface DiscoveredDevice {\n host: string;\n info: Information;\n}\n\n/** Options for opening a device. */\nexport interface OpenOptions {\n /** Open read-only: skip `session/open`, mint no token, send no session header. */\n readonly?: boolean;\n /** Human-readable holder label shown to other clients and on the front panel. */\n label?: string;\n}\n\n/** @hideconstructor */\nexport class AT1000Base {\n /**\n * IP address or hostname of the AT1000 device\n */\n public readonly host: string;\n /**\n * Device information\n */\n public readonly info: Information;\n /**\n * Session token minted by `open()`, or `undefined` for a read-only or legacy\n * (pre-session) client. Sent as the `X-AT1000-Session` header on every request.\n */\n public readonly token: string | undefined;\n /**\n * `false` only when a pre-session server was detected during an exclusive\n * `open()` (no `/v0/session/*` routes). Read-only opens never probe and report `true`.\n */\n public readonly sessionSupported: boolean;\n\n private fetch: UpFetch;\n /**\n * COM API\n */\n public readonly com: ComApi;\n /**\n * HMI API\n */\n public readonly hmi: HmiApi;\n /**\n * GPIO API\n */\n public readonly gpio: GpioApi;\n /**\n * Management API\n */\n public readonly management: ManagementApi;\n /**\n * Power management API\n */\n public readonly power: PowerApi;\n /**\n * Relays API\n */\n public readonly relays: RelaysApi;\n /**\n * Real-time events API (WebSocket). Call `events.connect()` then `events.on(...)`.\n */\n public readonly events: EventsApi;\n /**\n * Session API. Read the current holder via `session.state()`.\n */\n public readonly session: SessionApi;\n\n constructor(host: string, info: Information, token?: string, sessionSupported = true) {\n this.host = host;\n this.info = info;\n this.token = token;\n this.sessionSupported = sessionSupported;\n\n this.fetch = up(\n fetch,\n () => ({\n baseUrl: `http://${format_host_for_url(this.host)}/v0`,\n timeout: 1000,\n headers: this.token\n ? { 'X-AT1000-Session': this.token }\n : undefined,\n parseRejected: (response, request) => parseApiError(response, request.method),\n }),\n );\n\n this.com = new ComApi(this.fetch);\n this.hmi = new HmiApi(this.fetch);\n this.gpio = new GpioApi(this.fetch, this.info);\n this.management = new ManagementApi(this.fetch, this.host);\n this.power = new PowerApi(this.fetch, this.info);\n this.relays = new RelaysApi(this.fetch);\n this.events = new EventsApi(this.host);\n this.session = new SessionApi(this.fetch);\n }\n\n async reset() {\n await this.com.reset();\n await this.hmi.reset();\n await this.gpio.reset();\n await this.power.reset();\n await this.relays.reset();\n }\n\n protected static async openBase(\n device: DiscoveredDevice | string,\n options?: OpenOptions,\n session?: { kind: 'remote' | 'project'; project?: string; defaultLabel?: string },\n ): Promise<AT1000Base> {\n const host = typeof device === 'string' ? device : device.host;\n const base = `http://${format_host_for_url(host)}/v0`;\n\n // Re-fetch info to validate reachability (bare client, no session header yet).\n const bare = up(\n fetch,\n () => ({\n baseUrl: base,\n timeout: 1000,\n parseRejected: (response, request) =>\n parseApiError(response, request.method),\n }),\n );\n const info = await new ManagementApi(bare, host).info();\n\n if (options?.readonly) {\n return new AT1000Base(host, info);\n }\n\n const token = await open_session(base, {\n kind: session?.kind ?? 'remote',\n label: options?.label ?? session?.defaultLabel,\n project: session?.project,\n });\n // `undefined` => pre-session server: keep a working, tokenless client.\n return token === undefined\n ? new AT1000Base(host, info, undefined, false)\n : new AT1000Base(host, info, token);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import{ComApi as e,canConfig as t,canFrame as n,i2cConfig as r,rs232Config as i,rs485Config as a,spiConfig as o,uartConfig as s}from"./com.mjs";import{GpioApi as ee,analogInputConfiguration as te,analogOutputConfiguration as c,analogState as ne,digitalInputConfiguration as re,digitalOutputConfiguration as ie,digitalState as ae,openDrainConfiguration as oe,syncState as se}from"./gpio.mjs";import{HmiApi as ce,color as le,internalKnobEvent as ue,playbackConfiguration as de,progress as fe,text as pe}from"./hmi.mjs";import{PowerApi as me,dutState as he,usbState as ge}from"./power.mjs";import{RelaysApi as _e,relayState as l,states as ve}from"./relays.mjs";import{up as u}from"up-fetch";import*as d from"valibot";let f=function(e){return e.Can=`can`,e.Rs232=`rs232`,e.Rs485=`rs485`,e.I2c=`i2c`,e.Spi=`spi`,e.Uart=`uart`,e}({});const ye=d.enum(f),p=d.object({min:d.pipe(d.number(),d.minValue(-24),d.maxValue(24)),max:d.pipe(d.number(),d.minValue(-24),d.maxValue(24))}),be=d.object({max:d.number()}),xe=d.object({count:d.pipe(d.number(),d.minValue(1)),input_voltage:p,output_voltage:p}),Se=d.object({count:d.pipe(d.number(),d.minValue(0)),max_current:d.number(),max_voltage:d.number(),on_resistance:d.pipe(d.number(),d.minValue(0))}),Ce=d.object({type:d.picklist([`3.0`,`2.0`]),count:d.pipe(d.number(),d.minValue(1))}),we=d.object({type:d.picklist([`1000BASE-T`,`100BASE-T`,`10BASE-T`]),count:d.pipe(d.number(),d.minValue(1))}),Te=d.object({type:ye,count:d.pipe(d.number(),d.minValue(1))}),Ee=d.object({count:d.pipe(d.number(),d.minValue(1)),output_voltage:p,current:be}),m=d.object({type:d.picklist([`low_precision`,`high_precision`]),source:d.string(),resolution:d.pipe(d.number(),d.minValue(0))}),h=d.object({width:d.pipe(d.number(),d.minValue(1)),height:d.pipe(d.number(),d.minValue(1))}),g=d.object({type:d.string(),size:h}),_=d.object({count:d.pipe(d.number(),d.minValue(0)),type:d.string()}),v=d.object({count:d.pipe(d.number(),d.minValue(0)),type:d.string()}),y=d.object({display:g,input:_,speaker:v}),b=d.object({io:xe,relays:Se,usb_ports:d.array(Ce),ethernet:d.array(we),communication:d.array(Te),power_supply:Ee,current_measurement:d.array(m),hmi:y}),x=d.object({model:d.string(),hostname:d.string(),serial_number:d.string(),firmware_version:d.string(),firmware_commit:d.string(),hardware_version:d.optional(d.number()),disk_used:d.number(),disk_total:d.number(),docker_used:d.number(),capabilities:b}),S=d.picklist([`idle`,`running`,`error`]),C=d.picklist([`created`,`started`,`stopped`,`removed`,`error`]),w=d.picklist([`tcp`,`udp`]),T=d.pipe(d.number(),d.integer(),d.minValue(1),d.maxValue(65535)),E=d.object({host:T,container:T,proto:w}),D=d.pipe(d.string(),d.trim(),d.regex(/^(ssh-|ecdsa-sha2-|sk-ssh-|sk-ecdsa-)/,`Not a recognized SSH public key`)),O=d.picklist([`normal`,`development`]),k=d.object({stream:d.picklist([`stdout`,`stderr`]),line:d.string()}),A=d.object({name:d.string(),path:d.string(),id:d.string(),image:d.string(),is_default:d.boolean(),ports:d.optional(d.array(E),[]),command:d.optional(d.string(),``),state:S,mode:d.optional(O)}),j=d.variant(`change`,[d.object({change:d.literal(`state_changed`),id:d.string(),state:C}),d.object({change:d.literal(`renamed`),id:d.string(),name:d.string()}),d.object({change:d.literal(`default_changed`),id:d.string(),is_default:d.boolean()})]),De=d.partial(d.pick(A,[`name`,`is_default`,`command`])),Oe=d.picklist([`nodejs`,`python`]);d.object({name:d.string(),template:Oe,command:d.optional(d.string())});const M=d.object({bundle:d.string(),version:d.string(),compatible:d.string(),description:d.string()}),ke=d.object({message:d.string(),bundle:d.string()}),N=d.object({state:d.picklist([`idle`,`installing`,`failed`]),progress:d.number(),message:d.string(),error:d.string()}),Ae=d.object({message:d.string()}),P=d.pipe(d.number(),d.finite()),F=d.pipe(d.number(),d.finite(),d.minValue(Number.MIN_VALUE)),je=d.pipe(d.number(),d.integer(),d.minValue(0),d.maxValue(31)),I=d.pipe(d.number(),d.integer(),d.minValue(0),d.maxValue(7)),Me=d.pipe(d.number(),d.integer(),d.minValue(0),d.maxValue(1)),L=d.picklist([`pass`,`fail`]),R=d.object({set:P,measured:P,reading:d.optional(P)}),z=d.object({channel:je,points:d.array(R),max_error:P,avg_error:P}),Ne=d.object({voltage:d.object({points:d.array(R),max_error:P,avg_error:P}),current:d.object({load_ohms:F,points:d.array(R),max_error:P,avg_error:P})}),Pe=d.object({load_ohms:F,voltage_applied:P,measured_current:P,expected_current:P,error_percent:P}),Fe=d.object({relay:I,status:L,test_voltage:P,closed_current:P,open_current:P,calibration:d.optional(z)}),Ie=d.object({port:Me,status:L,voltage:P,current_tests:d.optional(d.array(Pe))}),B=d.object({schema_version:d.literal(1),tool_version:d.string(),device_id:d.string(),timestamp:d.string(),firmware_version:d.string(),dac:d.array(z),adc_positive:d.array(z),adc_negative:d.optional(d.array(z)),dut_psu:Ne,relays:d.array(Fe),usb:d.array(Ie)}),V=d.object({enabled:d.boolean(),loaded:d.boolean(),last_error:d.optional(d.nullable(d.string())),calibrated_at:d.optional(d.nullable(d.string()))}),Le=d.object({enabled:d.boolean()});var H=class{fetch;host;constructor(e,t){this.fetch=e,this.host=t}info(){return this.fetch(`/management/info`,{schema:x})}get_calibration(){return this.fetch(`/management/calibration`,{schema:B})}set_calibration(e){return this.fetch(`/management/calibration`,{method:`PUT`,body:d.parse(B,e),schema:B})}get_calibration_state(){return this.fetch(`/management/calibration/state`,{schema:V})}set_calibration_enabled(e){return this.fetch(`/management/calibration/state`,{method:`PATCH`,body:d.parse(Le,{enabled:e}),schema:V})}enable_calibration(){return this.set_calibration_enabled(!0)}disable_calibration(){return this.set_calibration_enabled(!1)}async datetime(){let e=await this.fetch(`/management/datetime`,{schema:d.object({epoch_seconds:d.number()})});return new Date(e.epoch_seconds*1e3)}setDatetime(e){return this.fetch(`/management/datetime`,{method:`POST`,body:{epoch_seconds:Math.floor(e.getTime()/1e3)},parseResponse:()=>{}})}getProjects(){return this.fetch(`/management/projects`,{schema:d.array(A)})}listImages(){return this.fetch(`/management/images`,{schema:d.array(d.string())})}getProject(e){return this.fetch(`/management/projects/${e}`,{schema:A})}createProject(e){return this.fetch(`/management/projects`,{method:`POST`,body:e,timeout:3e4,schema:A})}importProject(e,t){return this.fetch(`/management/projects/import`,{method:`POST`,body:e,headers:{"Content-Type":`application/x-tar`,"X-Project-Name":encodeURIComponent(t)},serializeBody:e=>e,timeout:6e5,schema:A})}updateProject(e,t){return this.fetch(`/management/projects/${e}`,{method:`PATCH`,body:d.parse(De,t),schema:A})}runProject(e,t=`normal`){return this.fetch(`/management/projects/${e}/run`,{method:`POST`,body:{mode:t},timeout:3e4})}stopProject(e){return this.fetch(`/management/projects/${e}/stop`,{method:`POST`,timeout:3e4})}getProjectPorts(e){return this.fetch(`/management/projects/${e}/ports`,{schema:d.array(E)})}setProjectPorts(e,t){return this.fetch(`/management/projects/${e}/ports`,{method:`PUT`,body:d.parse(d.array(E),t),timeout:3e4,schema:d.array(E)})}addProjectPort(e,t){return this.fetch(`/management/projects/${e}/ports`,{method:`POST`,body:d.parse(E,t),timeout:3e4,schema:d.array(E)})}deleteProjectPort(e,t){return this.fetch(`/management/projects/${e}/ports/${t}`,{method:`DELETE`,parseResponse:()=>{},timeout:3e4})}setProjectPassword(e,t){return this.fetch(`/management/projects/${e}/password`,{method:`POST`,body:d.parse(d.object({password:d.pipe(d.string(),d.minLength(1))}),{password:t}),parseResponse:()=>{},timeout:3e4})}getProjectAuthorizedKeys(e){return this.fetch(`/management/projects/${e}/authorized_keys`,{schema:d.array(d.string())})}setProjectAuthorizedKeys(e,t){return this.fetch(`/management/projects/${e}/authorized_keys`,{method:`PUT`,body:d.parse(d.array(D),t),timeout:3e4,schema:d.array(d.string())})}deleteProject(e){return this.fetch(`/management/projects/${e}`,{method:`DELETE`,timeout:3e4})}upload_bundle(e,t=`bundle.raucb`){return this.fetch(`/management/update/upload`,{method:`POST`,body:e,headers:{"Content-Type":`application/octet-stream`,"X-Bundle-Filename":t},serializeBody:e=>e,timeout:6e5,schema:M})}bundle_info(){return this.fetch(`/management/update/bundle-info`,{schema:M})}install_update(){return this.fetch(`/management/update/install`,{method:`POST`,schema:ke})}update_status(){return this.fetch(`/management/update/status`,{schema:N})}reboot(){return this.fetch(`/management/reboot`,{method:`POST`,schema:Ae})}getProjectLogs(e,t){let n=t?.tail??1e3;return this.fetch(`/management/projects/${e}/logs?tail=${n}`,{schema:d.array(k)})}streamProjectLogs(e,t,n){let r=n?.tail??1e3,i=new AbortController,a=`http://${Q(this.host)}/v0/management/projects/${e}/logs/stream?tail=${r}`;return(async()=>{try{let e=await fetch(a,{signal:i.signal});if(!e.ok||e.body===null)throw Error(`Log stream failed: HTTP ${e.status}`);let n=e.body.getReader(),r=new TextDecoder,o=``,s=e=>{let n=`message`,r=``;for(let t of e.split(`
2
+ `))t.startsWith(`event:`)?n=t.slice(6).trim():t.startsWith(`data:`)&&(r+=t.slice(5).trim());if(n===`end`)return t.onEnd?.(),!0;if(n===`log`&&r.length>0){let e=d.safeParse(k,JSON.parse(r));e.success&&t.onLine(e.output)}return!1};for(;;){let{done:e,value:t}=await n.read();if(e)break;o+=r.decode(t,{stream:!0});let a=o.indexOf(`
3
+
4
+ `);for(;a!==-1;){let e=o.slice(0,a);if(o=o.slice(a+2),s(e)){i.abort();return}a=o.indexOf(`
5
+
6
+ `)}}}catch(e){if(i.signal.aborted)return;t.onError?.(e)}})(),{close:()=>i.abort()}}};const U=d.object({active:d.boolean(),kind:d.optional(d.picklist([`remote`,`project`])),label:d.optional(d.string()),project:d.optional(d.string()),since:d.optional(d.number())}),Re=d.object({token:d.string()});async function W(e,t){let n=await fetch(`${e}/session/open`,{method:`POST`,body:JSON.stringify(t),headers:{"content-type":`application/json`},signal:AbortSignal.timeout(3e4)});if(!(n.status===404||n.status===405))return n.ok||await $(n,`POST`),d.parse(Re,await n.json()).token}var G=class{fetch;constructor(e){this.fetch=e}state(){return this.fetch(`/session/state`,{schema:U})}};const K=d.array(d.pipe(d.number(),d.integer(),d.minValue(0),d.maxValue(255))),q={"com.can_config":t,"com.rs232_config":i,"com.rs485_config":a,"com.spi_config":o,"com.i2c_config":r,"com.uart_config":s,"com.can_rx":d.array(n),"com.uart_rx":K,"com.rs232_rx":K,"com.rs485_rx":K,"gpio.state":d.union([ae,ne]),"gpio.config":d.union([te,c,re,ie,oe]),"gpio.sync":se,"hmi.text":pe,"hmi.colors":le,"hmi.progress":d.optional(fe),"hmi.play":de,"hmi.knob":ue,"management.datetime":d.object({epoch_seconds:d.number()}),"management.calibration":B,"management.calibration_state":V,"management.project":j,"management.update_status":N,"power.dut":he,"power.usb":ge,"relays.state":l,"relays.states":ve,"session.state":U},ze=[`com.reset`,`gpio.reset`,`hmi.clear`,`hmi.reset`,`power.reset`,`relays.reset`],Be=d.object({type:d.string(),id:d.optional(d.pipe(d.number(),d.integer())),ts:d.number(),data:d.optional(d.unknown())}),J=5e3,Y=(...e)=>console.debug(`[at1000:events]`,...e);var X=class{host;socket;listeners=new Map;stateListeners=new Set;shouldReconnect=!1;reconnectDelayMs=500;reconnectTimer;_connected=!1;connectPromise;constructor(e){this.host=e}get connected(){return this._connected}async connect(e){if(typeof WebSocket>`u`)throw Error(`WebSocket is not available in this runtime (Node >= 22, Bun, or a browser is required)`);if(!this._connected){this.connectPromise??=this.openSocket(e?.timeout??J);try{await this.connectPromise}finally{this.connectPromise=void 0}}}close(){this.shouldReconnect=!1,this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0);let e=this.socket;this.socket=void 0,e!==void 0&&e.close(),this.setConnected(!1)}on(e,t){let n=this.listeners.get(e);return n===void 0&&(n=new Set,this.listeners.set(e,n)),n.add(t),()=>{let n=this.listeners.get(e);n!==void 0&&(n.delete(t),n.size===0&&this.listeners.delete(e))}}onStateChange(e){return this.stateListeners.add(e),()=>{this.stateListeners.delete(e)}}openSocket(e){let{promise:t,resolve:n,reject:r}=Promise.withResolvers(),i=`ws://${Q(this.host)}/v0/events`,a=new WebSocket(i);this.socket=a;let o=!1,s=setTimeout(()=>{o||(o=!0,a.close(),r(Error(`Timed out connecting to ${i}`)))},e);return a.onopen=()=>{this.socket!==a||o||(o=!0,clearTimeout(s),this.shouldReconnect=!0,this.reconnectDelayMs=500,this.setConnected(!0),Y(`open`,i),n())},a.onmessage=e=>{this.socket===a&&this.handleMessage(e.data)},a.onerror=()=>{Y(`error`,i),this.socket===a&&!o&&(o=!0,clearTimeout(s),r(Error(`Failed to connect to ${i}`)))},a.onclose=()=>{if(this.socket===a){if(Y(`close`,i),clearTimeout(s),this.setConnected(!1),!o){o=!0,r(Error(`Connection to ${i} closed before opening`));return}this.shouldReconnect&&this.scheduleReconnect()}},t}scheduleReconnect(){if(!this.shouldReconnect||this.reconnectTimer!==void 0)return;let e=this.reconnectDelayMs;Y(`reconnecting in`,e,`ms`),this.reconnectDelayMs=Math.min(this.reconnectDelayMs*2,8e3),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=void 0,this.shouldReconnect&&this.openSocket(J).catch(()=>{})},e)}handleMessage(e){if(typeof e!=`string`){Y(`drop: non-string frame`);return}let t;try{t=JSON.parse(e)}catch{Y(`drop: invalid JSON`,e);return}Y(`recv`,t);let n=d.safeParse(Be,t);if(!n.success){Y(`drop: malformed envelope`,n.issues);return}let{type:r,id:i,ts:a}=n.output;if(r in q){let e=q[r],t=d.safeParse(e,n.output.data);if(!t.success){Y(`drop: payload validation failed for`,r,t.issues);return}let o={type:r,id:i,ts:a,data:t.output};Y(`dispatch`,r),this.emitTo(r,o),this.emitTo(`*`,o);return}if(ze.includes(r)){let e={type:r,id:i,ts:a};Y(`dispatch (dataless)`,r),this.emitTo(r,e),this.emitTo(`*`,e);return}Y(`drop: unknown type`,r),this.emitTo(`*`,{type:r,id:i,ts:a,data:n.output.data})}emitTo(e,t){let n=this.listeners.get(e);if(n!==void 0)for(let e of n)try{e(t)}catch{}}setConnected(e){if(this._connected!==e){this._connected=e;for(let t of this.stateListeners)try{t(e)}catch{}}}},Z=class extends Error{code;detail;suggestion;path;constructor(e){let t=e.suggestion.replace(/\n/g,`
7
+ `);super(`${e.message} (${e.code})\n Suggestion: ${t}\n Path: ${e.path}`),this.code=e.code,this.detail=e.message,this.suggestion=e.suggestion,this.path=e.path}};function Q(e,t){let n=t||8080;if(e.startsWith(`[`))return e.includes(`]:`)?e:`${e}:${n}`;let r=e.indexOf(`:`);return r===-1?`${e}:${n}`:r===e.lastIndexOf(`:`)?e:`[${e}]:${n}`}function Ve(e){if(e.startsWith(`[`)){let t=e.indexOf(`]`);return t===-1?e:e.slice(1,t)}let t=e.indexOf(`:`);return t!==-1&&t===e.lastIndexOf(`:`)?e.slice(0,t):e}function He(e){if(typeof e!=`object`||!e)return!1;let t=e;return[`code`,`message`,`suggestion`,`path`].every(e=>typeof t[e]==`string`)}async function $(e,t){let n,r=e.headers.get(`content-type`);try{n=r?.includes(`application/json`)?await e.json():await e.text()}catch{n=`Failed to parse response body`}throw He(n)?new Z(n):Error(`API request to ${t} ${e.url} failed with status ${e.status}: ${typeof n==`string`?n:JSON.stringify(n)}`)}var Ue=class t{host;info;token;sessionSupported;fetch;com;hmi;gpio;management;power;relays;events;session;constructor(t,n,r,i=!0){this.host=t,this.info=n,this.token=r,this.sessionSupported=i,this.fetch=u(fetch,()=>({baseUrl:`http://${Q(this.host)}/v0`,timeout:1e3,headers:this.token?{"X-AT1000-Session":this.token}:void 0,parseRejected:(e,t)=>$(e,t.method)})),this.com=new e(this.fetch),this.hmi=new ce(this.fetch),this.gpio=new ee(this.fetch,this.info),this.management=new H(this.fetch,this.host),this.power=new me(this.fetch,this.info),this.relays=new _e(this.fetch),this.events=new X(this.host),this.session=new G(this.fetch)}async reset(){await this.com.reset(),await this.hmi.reset(),await this.gpio.reset(),await this.power.reset(),await this.relays.reset()}static async openBase(e,n,r){let i=typeof e==`string`?e:e.host,a=`http://${Q(i)}/v0`,o=await new H(u(fetch,()=>({baseUrl:a,timeout:1e3,parseRejected:(e,t)=>$(e,t.method)})),i).info();if(n?.readonly)return new t(i,o);let s=await W(a,{kind:r?.kind??`remote`,label:n?.label??r?.defaultLabel,project:r?.project});return s===void 0?new t(i,o,void 0,!1):new t(i,o,s)}};export{O as _,$ as a,W as c,H as d,B as f,j as g,A as h,Ve as i,U as l,E as m,Z as n,X as o,V as p,Q as r,G as s,Ue as t,f as u,N as v};
8
+ //# sourceMappingURL=at1000-BjVj0Ujk.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"at1000-BjVj0Ujk.mjs","names":[],"sources":["../src/api/management.ts","../src/api/session.ts","../src/api/events.ts","../src/at1000.ts"],"sourcesContent":["import { type UpFetch } from 'up-fetch';\nimport * as v from 'valibot';\n\nimport { format_host_for_url } from '../at1000';\n\nexport enum CommunicationInterfaceType {\n Can = 'can',\n Rs232 = 'rs232',\n Rs485 = 'rs485',\n I2c = 'i2c',\n Spi = 'spi',\n Uart = 'uart',\n}\nconst communicationInterface = v.enum(CommunicationInterfaceType);\n\nconst voltageRange = v.object({\n min: v.pipe(v.number(), v.minValue(-24), v.maxValue(24)),\n max: v.pipe(v.number(), v.minValue(-24), v.maxValue(24)),\n});\n\nconst currentRange = v.object({ max: v.number() });\n\nconst ioCapabilities = v.object({\n count: v.pipe(v.number(), v.minValue(1)),\n input_voltage: voltageRange,\n output_voltage: voltageRange,\n});\n\nconst relaysCapabilities = v.object({\n count: v.pipe(v.number(), v.minValue(0)),\n max_current: v.number(),\n max_voltage: v.number(),\n on_resistance: v.pipe(v.number(), v.minValue(0)),\n});\n\nconst usbCapability = v.object({\n type: v.picklist(['3.0', '2.0']),\n count: v.pipe(v.number(), v.minValue(1)),\n});\n\nconst ethernetCapability = v.object({\n type: v.picklist(['1000BASE-T', '100BASE-T', '10BASE-T']),\n count: v.pipe(v.number(), v.minValue(1)),\n});\n\nconst communicationCapability = v.object({\n type: communicationInterface,\n count: v.pipe(v.number(), v.minValue(1)),\n});\n\nconst powerSupplyCapabilities = v.object({\n count: v.pipe(v.number(), v.minValue(1)),\n output_voltage: voltageRange,\n current: currentRange,\n});\n\nconst currentMeasurementCapabilities = v.object({\n type: v.picklist(['low_precision', 'high_precision']),\n source: v.string(),\n resolution: v.pipe(v.number(), v.minValue(0)),\n});\n\nconst screenSize = v.object({\n width: v.pipe(v.number(), v.minValue(1)),\n height: v.pipe(v.number(), v.minValue(1)),\n});\n\nconst displayCapabilities = v.object({ type: v.string(), size: screenSize });\n\nconst inputCapabilities = v.object({ count: v.pipe(v.number(), v.minValue(0)), type: v.string() });\n\nconst audioCapabilities = v.object({ count: v.pipe(v.number(), v.minValue(0)), type: v.string() });\n\nconst hmiCapabilities = v.object({\n display: displayCapabilities,\n input: inputCapabilities,\n speaker: audioCapabilities,\n});\n\nconst capabilities = v.object({\n io: ioCapabilities,\n relays: relaysCapabilities,\n usb_ports: v.array(usbCapability),\n ethernet: v.array(ethernetCapability),\n communication: v.array(communicationCapability),\n power_supply: powerSupplyCapabilities,\n current_measurement: v.array(currentMeasurementCapabilities),\n hmi: hmiCapabilities,\n});\n\nconst info = v.object({\n model: v.string(),\n hostname: v.string(),\n serial_number: v.string(),\n firmware_version: v.string(),\n firmware_commit: v.string(),\n hardware_version: v.optional(v.number()),\n disk_used: v.number(),\n disk_total: v.number(),\n docker_used: v.number(),\n capabilities: capabilities,\n});\nexport type Information = v.InferOutput<typeof info>;\n\nconst projectStatus = v.picklist(['idle', 'running', 'error']);\nconst projectEventState = v.picklist(['created', 'started', 'stopped', 'removed', 'error']);\nconst portProtocol = v.picklist(['tcp', 'udp']);\nconst port = v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(65535));\nexport const portMapping = v.object({ host: port, container: port, proto: portProtocol });\nexport type PortMapping = v.InferOutput<typeof portMapping>;\nconst authorizedKey = v.pipe(\n v.string(),\n v.trim(),\n v.regex(/^(ssh-|ecdsa-sha2-|sk-ssh-|sk-ecdsa-)/, 'Not a recognized SSH public key'),\n);\nexport const runMode = v.picklist(['normal', 'development']);\nexport type RunMode = v.InferOutput<typeof runMode>;\n\nconst logLine = v.object({ stream: v.picklist(['stdout', 'stderr']), line: v.string() });\nexport type LogLine = v.InferOutput<typeof logLine>;\n\nexport const project = v.object({\n name: v.string(),\n path: v.string(),\n id: v.string(),\n image: v.string(),\n is_default: v.boolean(),\n ports: v.optional(v.array(portMapping), []),\n command: v.optional(v.string(), ''),\n state: projectStatus,\n mode: v.optional(runMode),\n});\nexport type Project = v.InferOutput<typeof project>;\n\n// Merged management.project realtime event, discriminated on `change`.\nexport const projectEvent = v.variant('change', [\n v.object({ change: v.literal('state_changed'), id: v.string(), state: projectEventState }),\n v.object({ change: v.literal('renamed'), id: v.string(), name: v.string() }),\n v.object({ change: v.literal('default_changed'), id: v.string(), is_default: v.boolean() }),\n]);\nexport type ProjectEvent = v.InferOutput<typeof projectEvent>;\n\nconst projectPatch = v.partial(v.pick(project, ['name', 'is_default', 'command']));\nexport type ProjectPatch = v.InferOutput<typeof projectPatch>;\n\nconst projectTemplate = v.picklist(['nodejs', 'python']);\nexport type ProjectTemplate = v.InferOutput<typeof projectTemplate>;\n\nconst createProject = v.object({\n name: v.string(),\n template: projectTemplate,\n command: v.optional(v.string()),\n});\nexport type CreateProject = v.InferOutput<typeof createProject>;\n\nconst bundleInfo = v.object({\n bundle: v.string(),\n version: v.string(),\n compatible: v.string(),\n description: v.string(),\n});\nexport type BundleInfo = v.InferOutput<typeof bundleInfo>;\n\nconst installResponse = v.object({ message: v.string(), bundle: v.string() });\nexport type InstallResponse = v.InferOutput<typeof installResponse>;\n\nexport const updateStatus = v.object({\n state: v.picklist(['idle', 'installing', 'failed']),\n progress: v.number(),\n message: v.string(),\n error: v.string(),\n});\nexport type UpdateStatus = v.InferOutput<typeof updateStatus>;\n\nconst rebootResponse = v.object({ message: v.string() });\nexport type RebootResponse = v.InferOutput<typeof rebootResponse>;\n\nconst finiteNumber = v.pipe(v.number(), v.finite());\nconst positiveFiniteNumber = v.pipe(v.number(), v.finite(), v.minValue(Number.MIN_VALUE));\nconst channelId = v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(31));\nconst relayId = v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(7));\nconst usbPortId = v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(1));\nconst calibrationStatus = v.picklist(['pass', 'fail']);\n\nconst calibrationPoint = v.object({\n set: finiteNumber,\n measured: finiteNumber,\n reading: v.optional(finiteNumber),\n});\nexport type CalibrationPoint = v.InferOutput<typeof calibrationPoint>;\n\nconst channelCalibration = v.object({\n channel: channelId,\n points: v.array(calibrationPoint),\n max_error: finiteNumber,\n avg_error: finiteNumber,\n});\nexport type ChannelCalibration = v.InferOutput<typeof channelCalibration>;\n\nconst dutPsuCalibration = v.object({\n voltage: v.object({\n points: v.array(calibrationPoint),\n max_error: finiteNumber,\n avg_error: finiteNumber,\n }),\n current: v.object({\n load_ohms: positiveFiniteNumber,\n points: v.array(calibrationPoint),\n max_error: finiteNumber,\n avg_error: finiteNumber,\n }),\n});\nexport type DutPsuCalibration = v.InferOutput<typeof dutPsuCalibration>;\n\nconst currentTestResult = v.object({\n load_ohms: positiveFiniteNumber,\n voltage_applied: finiteNumber,\n measured_current: finiteNumber,\n expected_current: finiteNumber,\n error_percent: finiteNumber,\n});\n/** @deprecated See {@link currentTestResult}. */\nexport type CurrentTestResult = v.InferOutput<typeof currentTestResult>;\n\nconst relayTestResult = v.object({\n relay: relayId,\n status: calibrationStatus,\n test_voltage: finiteNumber,\n closed_current: finiteNumber,\n open_current: finiteNumber,\n calibration: v.optional(channelCalibration),\n});\nexport type RelayTestResult = v.InferOutput<typeof relayTestResult>;\n\nconst usbTestResult = v.object({\n port: usbPortId,\n status: calibrationStatus,\n voltage: finiteNumber,\n /** @deprecated Legacy field; only present on calibrations from older devices. */\n current_tests: v.optional(v.array(currentTestResult)),\n});\nexport type UsbTestResult = v.InferOutput<typeof usbTestResult>;\n\nexport const calibrationData = v.object({\n schema_version: v.literal(1),\n tool_version: v.string(),\n device_id: v.string(),\n timestamp: v.string(),\n firmware_version: v.string(),\n dac: v.array(channelCalibration),\n adc_positive: v.array(channelCalibration),\n adc_negative: v.optional(v.array(channelCalibration)),\n dut_psu: dutPsuCalibration,\n relays: v.array(relayTestResult),\n usb: v.array(usbTestResult),\n});\nexport type CalibrationData = v.InferOutput<typeof calibrationData>;\n\nexport const calibrationState = v.object({\n enabled: v.boolean(),\n loaded: v.boolean(),\n last_error: v.optional(v.nullable(v.string())),\n calibrated_at: v.optional(v.nullable(v.string())),\n});\nexport type CalibrationState = v.InferOutput<typeof calibrationState>;\n\nconst calibrationStatePatch = v.object({ enabled: v.boolean() });\n\n/** @hideconstructor */\nexport class ManagementApi {\n constructor(private readonly fetch: UpFetch, private readonly host: string) {}\n\n /**\n * Gets device information and capabilities.\n * @returns The device information.\n */\n info() {\n return this.fetch('/management/info', { schema: info });\n }\n\n /**\n * Gets the loaded calibration data.\n * @returns The canonical calibration data stored on the device.\n */\n get_calibration(): Promise<CalibrationData> {\n return this.fetch('/management/calibration', { schema: calibrationData });\n }\n\n /**\n * Uploads and activates calibration data.\n * @param data The full calibration report to store on the device.\n */\n set_calibration(data: CalibrationData): Promise<CalibrationData> {\n return this.fetch('/management/calibration', {\n method: 'PUT',\n body: v.parse(calibrationData, data),\n schema: calibrationData,\n });\n }\n\n /**\n * Gets runtime calibration enable/load state.\n */\n get_calibration_state(): Promise<CalibrationState> {\n return this.fetch('/management/calibration/state', { schema: calibrationState });\n }\n\n /**\n * Enables or disables runtime calibration correction.\n * @param enabled Whether calibration correction should be applied.\n */\n set_calibration_enabled(enabled: boolean): Promise<CalibrationState> {\n return this.fetch('/management/calibration/state', {\n method: 'PATCH',\n body: v.parse(calibrationStatePatch, { enabled }),\n schema: calibrationState,\n });\n }\n\n /** Enables runtime calibration correction. */\n enable_calibration(): Promise<CalibrationState> {\n return this.set_calibration_enabled(true);\n }\n\n /** Disables runtime calibration correction. */\n disable_calibration(): Promise<CalibrationState> {\n return this.set_calibration_enabled(false);\n }\n\n /**\n * Gets the current date and time of the device.\n * @returns The current date and time.\n */\n async datetime(): Promise<Date> {\n const time = await this.fetch('/management/datetime', {\n schema: v.object({ epoch_seconds: v.number() }),\n });\n return new Date(time.epoch_seconds * 1000);\n }\n\n /**\n * Sets the date and time of the device.\n *\n * This will only work if the device is not connected to the internet.\n *\n * @param datetime The date and time to set.\n */\n setDatetime(datetime: Date): Promise<void> {\n return this.fetch('/management/datetime', {\n method: 'POST',\n body: { epoch_seconds: Math.floor(datetime.getTime() / 1000) },\n parseResponse: () => {},\n });\n }\n\n /**\n * Gets a list of all projects.\n * @returns An array of projects.\n */\n getProjects() {\n return this.fetch('/management/projects', { schema: v.array(project) });\n }\n\n /**\n * Lists the AT1000 base images present on the device.\n * @returns Repo tags of on-device base images (e.g. \"localhost/at1000-base:latest\").\n */\n listImages() {\n return this.fetch('/management/images', { schema: v.array(v.string()) });\n }\n\n /**\n * Gets a single project by id.\n * @param projectId The project slug id.\n * @returns The project info.\n */\n getProject(projectId: string) {\n return this.fetch(`/management/projects/${projectId}`, { schema: project });\n }\n\n /**\n * Creates a new project from a preinstalled template.\n * @param newProject The project creation data.\n */\n createProject(newProject: CreateProject) {\n return this.fetch('/management/projects', {\n method: 'POST',\n body: newProject,\n timeout: 30_000,\n schema: project,\n });\n }\n\n /**\n * Imports a project from a container image archive (.tar / .tar.gz).\n * @param file The image archive to import.\n * @param name The project name (sent percent-encoded; the server decodes it).\n * @returns The created project.\n */\n importProject(file: Blob, name: string) {\n return this.fetch('/management/projects/import', {\n method: 'POST',\n body: file,\n headers: { 'Content-Type': 'application/x-tar', 'X-Project-Name': encodeURIComponent(name) },\n serializeBody: (b) => b as any,\n timeout: 600_000,\n schema: project,\n });\n }\n\n /**\n * Patches an existing project (`name`, `is_default`, `command`). An empty `command` clears it (entrypoint autodetect).\n * @param projectId The project slug id.\n * @param data The data to patch the project with.\n */\n updateProject(projectId: string, data: ProjectPatch) {\n return this.fetch(`/management/projects/${projectId}`, {\n method: 'PATCH',\n body: v.parse(projectPatch, data),\n schema: project,\n });\n }\n\n /**\n * Runs a project.\n * @param projectId The project slug id.\n * @param mode The run mode: 'normal' (default) or 'development' (starts sshd).\n */\n runProject(projectId: string, mode: RunMode = 'normal') {\n return this.fetch(`/management/projects/${projectId}/run`, {\n method: 'POST',\n body: { mode },\n timeout: 30_000,\n });\n }\n\n /**\n * Stops a running project.\n * @param projectId The project slug id.\n */\n stopProject(projectId: string) {\n return this.fetch(`/management/projects/${projectId}/stop`, {\n method: 'POST',\n timeout: 30_000,\n });\n }\n\n /**\n * Gets the port mappings for a project.\n * @param projectId The project slug id.\n * @returns The project's port mappings.\n */\n getProjectPorts(projectId: string) {\n return this.fetch(`/management/projects/${projectId}/ports`, { schema: v.array(portMapping) });\n }\n\n /**\n * Replaces the full set of port mappings for a project.\n * @param projectId The project slug id.\n * @param ports The complete list of port mappings.\n * @returns The updated port mappings.\n */\n setProjectPorts(projectId: string, ports: PortMapping[]) {\n return this.fetch(`/management/projects/${projectId}/ports`, {\n method: 'PUT',\n body: v.parse(v.array(portMapping), ports),\n timeout: 30_000,\n schema: v.array(portMapping),\n });\n }\n\n /**\n * Adds a single port mapping to a project.\n * @param projectId The project slug id.\n * @param mapping The port mapping to add.\n * @returns The updated port mappings.\n */\n addProjectPort(projectId: string, mapping: PortMapping) {\n return this.fetch(`/management/projects/${projectId}/ports`, {\n method: 'POST',\n body: v.parse(portMapping, mapping),\n timeout: 30_000,\n schema: v.array(portMapping),\n });\n }\n\n /**\n * Removes the port mapping for a host port from a project.\n * @param projectId The project slug id.\n * @param hostPort The host port to unmap.\n */\n deleteProjectPort(projectId: string, hostPort: number) {\n return this.fetch(`/management/projects/${projectId}/ports/${hostPort}`, {\n method: 'DELETE',\n parseResponse: () => {},\n timeout: 30_000,\n });\n }\n\n /**\n * Sets the password of the project's dev user (used for SSH password login in\n * development mode).\n * @param projectId The project slug id.\n * @param password The new password.\n */\n setProjectPassword(projectId: string, password: string) {\n return this.fetch(`/management/projects/${projectId}/password`, {\n method: 'POST',\n body: v.parse(v.object({ password: v.pipe(v.string(), v.minLength(1)) }), { password }),\n parseResponse: () => {},\n timeout: 30_000,\n });\n }\n\n /**\n * Gets the SSH authorized_keys for the project's dev user.\n * @param projectId The project slug id.\n * @returns The list of authorized public keys.\n */\n getProjectAuthorizedKeys(projectId: string) {\n return this.fetch(`/management/projects/${projectId}/authorized_keys`, {\n schema: v.array(v.string()),\n });\n }\n\n /**\n * Replaces the SSH authorized_keys for the project's dev user.\n * @param projectId The project slug id.\n * @param keys The complete list of authorized public keys.\n * @returns The updated list of authorized public keys.\n */\n setProjectAuthorizedKeys(projectId: string, keys: string[]) {\n return this.fetch(`/management/projects/${projectId}/authorized_keys`, {\n method: 'PUT',\n body: v.parse(v.array(authorizedKey), keys),\n timeout: 30_000,\n schema: v.array(v.string()),\n });\n }\n\n /**\n * Deletes a project.\n * @param projectId The project slug id.\n */\n deleteProject(projectId: string) {\n return this.fetch(`/management/projects/${projectId}`, { method: 'DELETE', timeout: 30_000 });\n }\n\n /**\n * Uploads a firmware bundle to the device and inspects it.\n * The bundle is staged for installation but not installed yet.\n * @param file The firmware bundle file (.raucb).\n * @param filename Optional filename for the bundle. Defaults to 'bundle.raucb'.\n * @returns Bundle metadata including version information.\n */\n upload_bundle(file: Blob, filename = 'bundle.raucb') {\n return this.fetch('/management/update/upload', {\n method: 'POST',\n body: file,\n headers: { 'Content-Type': 'application/octet-stream', 'X-Bundle-Filename': filename },\n serializeBody: (b) => b as any,\n timeout: 600_000,\n schema: bundleInfo,\n });\n }\n\n /**\n * Gets metadata for the currently staged firmware bundle.\n * @returns Bundle metadata including version information.\n */\n bundle_info() {\n return this.fetch('/management/update/bundle-info', { schema: bundleInfo });\n }\n\n /**\n * Installs a previously uploaded firmware bundle.\n * @returns The install response with message and bundle name.\n */\n install_update() {\n return this.fetch('/management/update/install', { method: 'POST', schema: installResponse });\n }\n\n /**\n * Gets the current firmware update status.\n * @returns The update status including state, progress, message and error.\n */\n update_status() {\n return this.fetch('/management/update/status', { schema: updateStatus });\n }\n\n /**\n * Reboots the device.\n * @returns The reboot response message.\n */\n reboot() {\n return this.fetch('/management/reboot', { method: 'POST', schema: rebootResponse });\n }\n\n /**\n * Gets the current run's container logs for a project (one-shot).\n *\n * Lines are scoped server-side to the container's most recent start: a running\n * project returns the live run so far, an idle/stopped project the last run.\n * stdout and stderr are merged into one chronological array; each entry's\n * `stream` field says which it came from.\n *\n * @param projectId The project slug id.\n * @param options.tail Max number of trailing lines (default 1000, clamped 1..10000).\n * @returns The current run's log lines, in order.\n */\n getProjectLogs(projectId: string, options?: { tail?: number }) {\n const tail = options?.tail ?? 1000;\n return this.fetch(`/management/projects/${projectId}/logs?tail=${tail}`, {\n schema: v.array(logLine),\n });\n }\n\n /**\n * Streams the current run's container logs for a project over SSE.\n *\n * Replays the current run's recent lines (bounded by `tail`) then follows live\n * with zero gap. `onLine` fires once per line (stdout and stderr merged in\n * order); `onEnd` fires when the follow ends (the container stopped, or an\n * idle project's last-run replay completed). No auto-reconnect.\n *\n * @param projectId The project slug id.\n * @param handlers Line/end/error callbacks.\n * @param options.tail Max number of trailing lines (default 1000, clamped 1..10000).\n * @returns A handle whose `close()` aborts the stream.\n */\n streamProjectLogs(\n projectId: string,\n handlers: {\n onLine: (line: LogLine) => void;\n onEnd?: () => void;\n onError?: (err: unknown) => void;\n },\n options?: { tail?: number },\n ): { close: () => void } {\n const tail = options?.tail ?? 1000;\n const controller = new AbortController();\n const url = `http://${format_host_for_url(this.host)}`\n + `/v0/management/projects/${projectId}/logs/stream?tail=${tail}`;\n\n void (async () => {\n try {\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok || response.body === null) {\n throw new Error(`Log stream failed: HTTP ${response.status}`);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n const dispatch = (record: string): boolean => {\n let event = 'message';\n let data = '';\n for (const rawLine of record.split('\\n')) {\n if (rawLine.startsWith('event:')) {\n event = rawLine.slice('event:'.length).trim();\n } else if (rawLine.startsWith('data:')) {\n data += rawLine.slice('data:'.length).trim();\n }\n }\n if (event === 'end') {\n handlers.onEnd?.();\n return true; // terminal\n }\n if (event === 'log' && data.length > 0) {\n const parsed = v.safeParse(logLine, JSON.parse(data));\n if (parsed.success) {\n handlers.onLine(parsed.output);\n }\n }\n return false;\n };\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n buffer += decoder.decode(value, { stream: true });\n let separator = buffer.indexOf('\\n\\n');\n while (separator !== -1) {\n const record = buffer.slice(0, separator);\n buffer = buffer.slice(separator + 2);\n if (dispatch(record)) {\n controller.abort();\n return;\n }\n separator = buffer.indexOf('\\n\\n');\n }\n }\n } catch (err) {\n if (controller.signal.aborted) {\n return; // caller closed, or terminal end handled above\n }\n handlers.onError?.(err);\n }\n })();\n\n return { close: () => controller.abort() };\n }\n}\n","import type { UpFetch } from 'up-fetch';\nimport * as v from 'valibot';\n\nimport { parseApiError } from '../at1000';\n\nexport const sessionState = v.object({\n active: v.boolean(),\n kind: v.optional(v.picklist(['remote', 'project'])),\n label: v.optional(v.string()),\n project: v.optional(v.string()),\n since: v.optional(v.number()),\n});\nexport type SessionState = v.InferOutput<typeof sessionState>;\n\n/** Body for `POST /v0/session/open`. */\nexport type SessionOpenBody = { kind: 'remote' | 'project'; label?: string; project?: string };\n\nconst openResponse = v.object({ token: v.string() });\n\n/**\n * Opens an exclusive session on the device (last-open-wins). Returns the minted\n * session token, or `undefined` when the server predates the session model.\n *\n * Deliberately uses a direct `fetch` (not the shared up-fetch client) so the\n * response status is inspectable before any structured-error handler throws:\n * pre-session servers answer the unmatched route with 404/405, which we degrade\n * to `undefined` rather than surface as a failure. A 30 s timeout (not the SDK\n * default 1 s) covers a remote-kind open synchronously stopping any running\n * project container; the host is already known-alive from the preceding info\n * probe, so the long timeout never hangs on dead hosts.\n */\nexport async function open_session(\n baseUrl: string,\n body: SessionOpenBody,\n): Promise<string | undefined> {\n const response = await fetch(`${baseUrl}/session/open`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json' },\n signal: AbortSignal.timeout(30_000),\n });\n\n if (response.status === 404 || response.status === 405) {\n return undefined; // legacy server without the session model\n }\n if (!response.ok) {\n await parseApiError(response, 'POST');\n }\n\n return v.parse(openResponse, await response.json()).token;\n}\n\n/** @hideconstructor */\nexport class SessionApi {\n constructor(private readonly fetch: UpFetch) {}\n\n /**\n * Reads the current device session state.\n * @returns The active holder (kind/label/project/since) or `{ active: false }`.\n */\n state(): Promise<SessionState> {\n return this.fetch('/session/state', { schema: sessionState });\n }\n}\n","import * as v from 'valibot';\n\nimport { format_host_for_url } from '../at1000';\nimport {\n canConfig,\n canFrame,\n i2cConfig,\n rs232Config,\n rs485Config,\n spiConfig,\n uartConfig,\n} from './com';\nimport {\n analogInputConfiguration,\n analogOutputConfiguration,\n analogState,\n digitalInputConfiguration,\n digitalOutputConfiguration,\n digitalState,\n openDrainConfiguration,\n syncState,\n} from './gpio';\nimport { color, internalKnobEvent, playbackConfiguration, progress, text } from './hmi';\nimport { calibrationData, calibrationState, projectEvent, updateStatus } from './management';\nimport { dutState, usbState } from './power';\nimport { relayState, states } from './relays';\nimport { sessionState } from './session';\n\n// Receive-side serial bytes. Distinct from com's `serialData`, whose string->bytes\n// transform is for request bodies; events deliver raw byte arrays.\nconst rxBytes = v.array(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(255)));\n\n// Schema for the `data` payload of each event, keyed by the wire `type` literal.\nconst eventDataSchemas = {\n 'com.can_config': canConfig,\n 'com.rs232_config': rs232Config,\n 'com.rs485_config': rs485Config,\n 'com.spi_config': spiConfig,\n 'com.i2c_config': i2cConfig,\n 'com.uart_config': uartConfig,\n 'com.can_rx': v.array(canFrame),\n 'com.uart_rx': rxBytes,\n 'com.rs232_rx': rxBytes,\n 'com.rs485_rx': rxBytes,\n 'gpio.state': v.union([digitalState, analogState]),\n 'gpio.config': v.union([\n analogInputConfiguration,\n analogOutputConfiguration,\n digitalInputConfiguration,\n digitalOutputConfiguration,\n openDrainConfiguration,\n ]),\n // Mirrors the glz::meta of gpio::sync_hold_state_t (apps/server/src/api/resources/gpio.hpp).\n 'gpio.sync': syncState,\n 'hmi.text': text,\n 'hmi.colors': color,\n // `data` absent (progress hidden) is accepted by v.optional.\n 'hmi.progress': v.optional(progress),\n 'hmi.play': playbackConfiguration,\n 'hmi.knob': internalKnobEvent,\n 'management.datetime': v.object({ epoch_seconds: v.number() }),\n 'management.calibration': calibrationData,\n 'management.calibration_state': calibrationState,\n 'management.project': projectEvent,\n 'management.update_status': updateStatus,\n 'power.dut': dutState,\n 'power.usb': usbState,\n 'relays.state': relayState,\n 'relays.states': states,\n 'session.state': sessionState,\n} as const;\n\nconst dataLessEvents = [\n 'com.reset',\n 'gpio.reset',\n 'hmi.clear',\n 'hmi.reset',\n 'power.reset',\n 'relays.reset',\n] as const;\n\nconst envelope = v.object({\n type: v.string(),\n id: v.optional(v.pipe(v.number(), v.integer())),\n ts: v.number(),\n data: v.optional(v.unknown()),\n});\n\ntype EventDataSchemas = typeof eventDataSchemas;\n\n/** Every event `type` literal the server can emit. */\nexport type EventType = keyof EventDataSchemas | (typeof dataLessEvents)[number];\n\ntype PayloadEvent = {\n [K in keyof EventDataSchemas]: {\n type: K;\n id?: number;\n ts: number;\n data: v.InferOutput<EventDataSchemas[K]>;\n };\n}[keyof EventDataSchemas];\n\ntype DataLessEvent = { type: (typeof dataLessEvents)[number]; id?: number; ts: number };\n\n/** A fully-typed device event for a known `type`. */\nexport type DeviceEvent = PayloadEvent | DataLessEvent;\n\n/**\n * Shape delivered to `'*'` listeners. Forward-compatible: a newer server may emit\n * a `type` this SDK does not model, surfaced here with an untyped `data`.\n */\nexport type AnyDeviceEvent = DeviceEvent | {\n type: string;\n id?: number;\n ts: number;\n data?: unknown;\n};\n\ntype EventListener = (event: AnyDeviceEvent) => void;\ntype StateListener = (connected: boolean) => void;\n\nconst reconnectBaseDelayMs = 500;\nconst reconnectMaxDelayMs = 8_000;\nconst defaultConnectTimeoutMs = 5_000;\n\nconst debugLog = (...args: unknown[]): void => console.debug('[at1000:events]', ...args);\n\n/** @hideconstructor */\nexport class EventsApi {\n private socket: WebSocket | undefined;\n private readonly listeners = new Map<string, Set<EventListener>>();\n private readonly stateListeners = new Set<StateListener>();\n private shouldReconnect = false;\n private reconnectDelayMs = reconnectBaseDelayMs;\n private reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n private _connected = false;\n private connectPromise: Promise<void> | undefined;\n\n constructor(private readonly host: string) {}\n\n /** Whether the WebSocket is currently open. */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Open the event stream. Resolves once the socket is open, rejects on failure or\n * after `timeout` ms. A no-op while already connected. Auto-reconnect arms only\n * after the first successful open, so a failed initial `connect()` (e.g. old\n * firmware without `/v0/events`) rejects without scheduling retries.\n */\n async connect(options?: { timeout?: number }): Promise<void> {\n if (typeof WebSocket === 'undefined') {\n throw new Error(\n 'WebSocket is not available in this runtime (Node >= 22, Bun, or a browser is required)',\n );\n }\n if (this._connected) {\n return;\n }\n // Coalesce concurrent opens: the layout holder-banner effect and the\n // projects page both call connect() on the shared per-client socket. Without\n // this a second caller starts a second openSocket() that orphans the first\n // (leaked WS + a connect() that never settles). Reuse the in-flight open.\n this.connectPromise ??= this.openSocket(options?.timeout ?? defaultConnectTimeoutMs);\n try {\n await this.connectPromise;\n } finally {\n this.connectPromise = undefined;\n }\n }\n\n /** Close the stream and cancel any pending reconnect. */\n close(): void {\n this.shouldReconnect = false;\n if (this.reconnectTimer !== undefined) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n const socket = this.socket;\n this.socket = undefined;\n if (socket !== undefined) {\n socket.close();\n }\n this.setConnected(false);\n }\n\n /**\n * Subscribe to an event `type`, or `'*'` for every event. Returns an unsubscribe\n * function. Listeners survive reconnects and are isolated: a throwing listener\n * does not break dispatch to the others.\n */\n on<T extends EventType>(\n type: T,\n listener: (event: Extract<DeviceEvent, { type: T }>) => void,\n ): () => void;\n on(type: '*', listener: (event: AnyDeviceEvent) => void): () => void;\n on(type: string, listener: (event: any) => void): () => void {\n let set = this.listeners.get(type);\n if (set === undefined) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(listener);\n return () => {\n const current = this.listeners.get(type);\n if (current === undefined) {\n return;\n }\n current.delete(listener);\n if (current.size === 0) {\n this.listeners.delete(type);\n }\n };\n }\n\n /** Subscribe to connection state changes. Returns an unsubscribe function. */\n onStateChange(listener: StateListener): () => void {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n private openSocket(timeoutMs: number): Promise<void> {\n const { promise, resolve, reject } = Promise.withResolvers<void>();\n const url = `ws://${format_host_for_url(this.host)}/v0/events`;\n const socket = new WebSocket(url);\n this.socket = socket;\n\n let settled = false;\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n socket.close();\n reject(new Error(`Timed out connecting to ${url}`));\n }\n }, timeoutMs);\n\n socket.onopen = () => {\n if (this.socket !== socket || settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n this.shouldReconnect = true;\n this.reconnectDelayMs = reconnectBaseDelayMs;\n this.setConnected(true);\n debugLog('open', url);\n resolve();\n };\n\n socket.onmessage = (event) => {\n if (this.socket === socket) {\n this.handleMessage(event.data);\n }\n };\n\n socket.onerror = () => {\n debugLog('error', url);\n if (this.socket === socket && !settled) {\n settled = true;\n clearTimeout(timer);\n reject(new Error(`Failed to connect to ${url}`));\n }\n };\n\n socket.onclose = () => {\n if (this.socket !== socket) {\n return;\n }\n debugLog('close', url);\n clearTimeout(timer);\n this.setConnected(false);\n if (!settled) {\n settled = true;\n reject(new Error(`Connection to ${url} closed before opening`));\n return;\n }\n if (this.shouldReconnect) {\n this.scheduleReconnect();\n }\n };\n\n return promise;\n }\n\n private scheduleReconnect(): void {\n if (!this.shouldReconnect || this.reconnectTimer !== undefined) {\n return;\n }\n const delay = this.reconnectDelayMs;\n debugLog('reconnecting in', delay, 'ms');\n this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, reconnectMaxDelayMs);\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = undefined;\n if (!this.shouldReconnect) {\n return;\n }\n // A failed attempt rejects this promise; onclose then schedules the next retry.\n this.openSocket(defaultConnectTimeoutMs).catch(() => {});\n }, delay);\n }\n\n private handleMessage(data: unknown): void {\n if (typeof data !== 'string') {\n debugLog('drop: non-string frame');\n return;\n }\n let raw: unknown;\n try {\n raw = JSON.parse(data);\n } catch {\n debugLog('drop: invalid JSON', data);\n return;\n }\n\n debugLog('recv', raw);\n const parsed = v.safeParse(envelope, raw);\n if (!parsed.success) {\n debugLog('drop: malformed envelope', parsed.issues);\n return;\n }\n const { type, id, ts } = parsed.output;\n\n if (type in eventDataSchemas) {\n const schema = eventDataSchemas[type as keyof EventDataSchemas] as v.GenericSchema;\n const payload = v.safeParse(schema, parsed.output.data);\n if (!payload.success) {\n debugLog('drop: payload validation failed for', type, payload.issues);\n return;\n }\n const event: AnyDeviceEvent = { type, id, ts, data: payload.output };\n debugLog('dispatch', type);\n this.emitTo(type, event);\n this.emitTo('*', event);\n return;\n }\n\n if ((dataLessEvents as readonly string[]).includes(type)) {\n const event: AnyDeviceEvent = { type, id, ts };\n debugLog('dispatch (dataless)', type);\n this.emitTo(type, event);\n this.emitTo('*', event);\n return;\n }\n\n // Unknown type: deliver the raw envelope to '*' only (server may be newer).\n debugLog('drop: unknown type', type);\n this.emitTo('*', { type, id, ts, data: parsed.output.data });\n }\n\n private emitTo(key: string, event: AnyDeviceEvent): void {\n const set = this.listeners.get(key);\n if (set === undefined) {\n return;\n }\n for (const listener of set) {\n try {\n listener(event);\n } catch {\n // One throwing listener must not break dispatch to the others.\n }\n }\n }\n\n private setConnected(connected: boolean): void {\n if (this._connected === connected) {\n return;\n }\n this._connected = connected;\n for (const listener of this.stateListeners) {\n try {\n listener(connected);\n } catch {\n // Ignore throwing state listeners.\n }\n }\n }\n}\n","import { up, type UpFetch } from 'up-fetch';\n\nimport { ComApi } from './api/com';\nimport { EventsApi } from './api/events';\nimport { GpioApi } from './api/gpio';\nimport { HmiApi } from './api/hmi';\nimport { type Information, ManagementApi } from './api/management';\nimport { PowerApi } from './api/power';\nimport { RelaysApi } from './api/relays';\nimport { open_session, SessionApi } from './api/session';\n\n/** Structured error thrown for AT1000 API responses carrying `{ code, message, suggestion, path }`. */\nexport class ApiError extends Error {\n readonly code: string;\n /** Raw human-readable server message (without code/suggestion/path). */\n readonly detail: string;\n readonly suggestion: string;\n readonly path: string;\n\n constructor(body: { code: string; message: string; suggestion: string; path: string }) {\n const formattedSuggestion = body.suggestion.replace(/\\n/g, '\\n ');\n super(\n `${body.message} (${body.code})\\n Suggestion: ${formattedSuggestion}\\n Path: ${body.path}`,\n );\n this.code = body.code;\n this.detail = body.message;\n this.suggestion = body.suggestion;\n this.path = body.path;\n }\n}\n\nconst DEFAULT_PORT = 8080;\n\n/**\n * Formats a host into a URL authority. Returns `host` unchanged when it already\n * carries an explicit port; otherwise appends `fallbackPort` (the device's\n * mDNS-advertised port when known, else the default 8080). Bare IPv6 literals\n * are bracketed. Idempotent: re-formatting an authority is a no-op.\n */\nexport function format_host_for_url(host: string, fallbackPort?: number): string {\n const port = fallbackPort || DEFAULT_PORT;\n if (host.startsWith('[')) {\n return host.includes(']:') ? host : `${host}:${port}`;\n }\n const firstColon = host.indexOf(':');\n if (firstColon === -1) {\n return `${host}:${port}`;\n }\n if (firstColon === host.lastIndexOf(':')) {\n return host; // exactly one colon: host:port already specified\n }\n return `[${host}]:${port}`; // bare IPv6 literal, no port\n}\n\n/** Returns the bare host (IP or hostname) from a URL authority, dropping any port. */\nexport function host_without_port(host: string): string {\n if (host.startsWith('[')) {\n const end = host.indexOf(']');\n return end === -1 ? host : host.slice(1, end); // [ipv6] / [ipv6]:port -> ipv6\n }\n const firstColon = host.indexOf(':');\n if (firstColon !== -1 && firstColon === host.lastIndexOf(':')) {\n return host.slice(0, firstColon); // host:port -> host\n }\n return host; // bare hostname/IPv4 or bare IPv6 literal\n}\n\n/** Type guard for the AT1000 structured error body. */\nfunction isApiErrorBody(\n value: unknown,\n): value is { code: string; message: string; suggestion: string; path: string } {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const record = value as Record<string, unknown>;\n return ['code', 'message', 'suggestion', 'path'].every((key) => typeof record[key] === 'string');\n}\n\n/**\n * Shared rejection handler for every AT1000 up-fetch client. Parses a structured\n * `{ code, message, suggestion, path }` body into an {@link ApiError}, otherwise\n * throws a generic error carrying the HTTP status. Written once so the per-client\n * factory and `open_session` share one code path.\n */\nexport async function parseApiError(response: Response, method: string): Promise<never> {\n let errorData: unknown;\n const contentType = response.headers.get('content-type');\n try {\n errorData = contentType?.includes('application/json')\n ? await response.json()\n : await response.text();\n } catch {\n errorData = 'Failed to parse response body';\n }\n\n if (isApiErrorBody(errorData)) {\n throw new ApiError(errorData);\n }\n\n throw new Error(\n `API request to ${method} ${response.url} failed with status ${response.status}: ${\n typeof errorData === 'string' ? errorData : JSON.stringify(errorData)\n }`,\n );\n}\n\n/** A device discovered on the network but not yet opened. */\nexport interface DiscoveredDevice {\n host: string;\n info: Information;\n}\n\n/** Options for opening a device. */\nexport interface OpenOptions {\n /** Open read-only: skip `session/open`, mint no token, send no session header. */\n readonly?: boolean;\n /** Human-readable holder label shown to other clients and on the front panel. */\n label?: string;\n}\n\n/** @hideconstructor */\nexport class AT1000Base {\n /**\n * IP address or hostname of the AT1000 device\n */\n public readonly host: string;\n /**\n * Device information\n */\n public readonly info: Information;\n /**\n * Session token minted by `open()`, or `undefined` for a read-only or legacy\n * (pre-session) client. Sent as the `X-AT1000-Session` header on every request.\n */\n public readonly token: string | undefined;\n /**\n * `false` only when a pre-session server was detected during an exclusive\n * `open()` (no `/v0/session/*` routes). Read-only opens never probe and report `true`.\n */\n public readonly sessionSupported: boolean;\n\n private fetch: UpFetch;\n /**\n * COM API\n */\n public readonly com: ComApi;\n /**\n * HMI API\n */\n public readonly hmi: HmiApi;\n /**\n * GPIO API\n */\n public readonly gpio: GpioApi;\n /**\n * Management API\n */\n public readonly management: ManagementApi;\n /**\n * Power management API\n */\n public readonly power: PowerApi;\n /**\n * Relays API\n */\n public readonly relays: RelaysApi;\n /**\n * Real-time events API (WebSocket). Call `events.connect()` then `events.on(...)`.\n */\n public readonly events: EventsApi;\n /**\n * Session API. Read the current holder via `session.state()`.\n */\n public readonly session: SessionApi;\n\n constructor(host: string, info: Information, token?: string, sessionSupported = true) {\n this.host = host;\n this.info = info;\n this.token = token;\n this.sessionSupported = sessionSupported;\n\n this.fetch = up(\n fetch,\n () => ({\n baseUrl: `http://${format_host_for_url(this.host)}/v0`,\n timeout: 1000,\n headers: this.token\n ? { 'X-AT1000-Session': this.token }\n : undefined,\n parseRejected: (response, request) => parseApiError(response, request.method),\n }),\n );\n\n this.com = new ComApi(this.fetch);\n this.hmi = new HmiApi(this.fetch);\n this.gpio = new GpioApi(this.fetch, this.info);\n this.management = new ManagementApi(this.fetch, this.host);\n this.power = new PowerApi(this.fetch, this.info);\n this.relays = new RelaysApi(this.fetch);\n this.events = new EventsApi(this.host);\n this.session = new SessionApi(this.fetch);\n }\n\n async reset() {\n await this.com.reset();\n await this.hmi.reset();\n await this.gpio.reset();\n await this.power.reset();\n await this.relays.reset();\n }\n\n protected static async openBase(\n device: DiscoveredDevice | string,\n options?: OpenOptions,\n session?: { kind: 'remote' | 'project'; project?: string; defaultLabel?: string },\n ): Promise<AT1000Base> {\n const host = typeof device === 'string' ? device : device.host;\n const base = `http://${format_host_for_url(host)}/v0`;\n\n // Re-fetch info to validate reachability (bare client, no session header yet).\n const bare = up(\n fetch,\n () => ({\n baseUrl: base,\n timeout: 1000,\n parseRejected: (response, request) =>\n parseApiError(response, request.method),\n }),\n );\n const info = await new ManagementApi(bare, host).info();\n\n if (options?.readonly) {\n return new AT1000Base(host, info);\n }\n\n const token = await open_session(base, {\n kind: session?.kind ?? 'remote',\n label: options?.label ?? session?.defaultLabel,\n project: session?.project,\n });\n // `undefined` => pre-session server: keep a working, tokenless client.\n return token === undefined\n ? new AT1000Base(host, info, undefined, false)\n : new AT1000Base(host, info, token);\n }\n}\n"],"mappings":"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"}