@aigne/afs-ui 1.11.0-beta.12

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 (196) hide show
  1. package/LICENSE.md +26 -0
  2. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
  4. package/dist/aup-protocol.cjs +235 -0
  5. package/dist/aup-protocol.d.cts +78 -0
  6. package/dist/aup-protocol.d.cts.map +1 -0
  7. package/dist/aup-protocol.d.mts +78 -0
  8. package/dist/aup-protocol.d.mts.map +1 -0
  9. package/dist/aup-protocol.mjs +235 -0
  10. package/dist/aup-protocol.mjs.map +1 -0
  11. package/dist/aup-registry.cjs +2489 -0
  12. package/dist/aup-registry.mjs +2487 -0
  13. package/dist/aup-registry.mjs.map +1 -0
  14. package/dist/aup-spec.cjs +1467 -0
  15. package/dist/aup-spec.mjs +1466 -0
  16. package/dist/aup-spec.mjs.map +1 -0
  17. package/dist/aup-types.cjs +165 -0
  18. package/dist/aup-types.d.cts +157 -0
  19. package/dist/aup-types.d.cts.map +1 -0
  20. package/dist/aup-types.d.mts +157 -0
  21. package/dist/aup-types.d.mts.map +1 -0
  22. package/dist/aup-types.mjs +157 -0
  23. package/dist/aup-types.mjs.map +1 -0
  24. package/dist/backend.cjs +14 -0
  25. package/dist/backend.d.cts +104 -0
  26. package/dist/backend.d.cts.map +1 -0
  27. package/dist/backend.d.mts +104 -0
  28. package/dist/backend.d.mts.map +1 -0
  29. package/dist/backend.mjs +13 -0
  30. package/dist/backend.mjs.map +1 -0
  31. package/dist/degradation.cjs +85 -0
  32. package/dist/degradation.d.cts +17 -0
  33. package/dist/degradation.d.cts.map +1 -0
  34. package/dist/degradation.d.mts +17 -0
  35. package/dist/degradation.d.mts.map +1 -0
  36. package/dist/degradation.mjs +84 -0
  37. package/dist/degradation.mjs.map +1 -0
  38. package/dist/index.cjs +36 -0
  39. package/dist/index.d.cts +12 -0
  40. package/dist/index.d.mts +12 -0
  41. package/dist/index.mjs +13 -0
  42. package/dist/runtime.cjs +117 -0
  43. package/dist/runtime.d.cts +59 -0
  44. package/dist/runtime.d.cts.map +1 -0
  45. package/dist/runtime.d.mts +59 -0
  46. package/dist/runtime.d.mts.map +1 -0
  47. package/dist/runtime.mjs +118 -0
  48. package/dist/runtime.mjs.map +1 -0
  49. package/dist/session.cjs +159 -0
  50. package/dist/session.d.cts +80 -0
  51. package/dist/session.d.cts.map +1 -0
  52. package/dist/session.d.mts +80 -0
  53. package/dist/session.d.mts.map +1 -0
  54. package/dist/session.mjs +159 -0
  55. package/dist/session.mjs.map +1 -0
  56. package/dist/snapshot.cjs +162 -0
  57. package/dist/snapshot.mjs +163 -0
  58. package/dist/snapshot.mjs.map +1 -0
  59. package/dist/term-page.cjs +264 -0
  60. package/dist/term-page.mjs +264 -0
  61. package/dist/term-page.mjs.map +1 -0
  62. package/dist/term.cjs +295 -0
  63. package/dist/term.d.cts +84 -0
  64. package/dist/term.d.cts.map +1 -0
  65. package/dist/term.d.mts +84 -0
  66. package/dist/term.d.mts.map +1 -0
  67. package/dist/term.mjs +296 -0
  68. package/dist/term.mjs.map +1 -0
  69. package/dist/tty.cjs +136 -0
  70. package/dist/tty.d.cts +53 -0
  71. package/dist/tty.d.cts.map +1 -0
  72. package/dist/tty.d.mts +53 -0
  73. package/dist/tty.d.mts.map +1 -0
  74. package/dist/tty.mjs +135 -0
  75. package/dist/tty.mjs.map +1 -0
  76. package/dist/ui-provider.cjs +4615 -0
  77. package/dist/ui-provider.d.cts +307 -0
  78. package/dist/ui-provider.d.cts.map +1 -0
  79. package/dist/ui-provider.d.mts +307 -0
  80. package/dist/ui-provider.d.mts.map +1 -0
  81. package/dist/ui-provider.mjs +4616 -0
  82. package/dist/ui-provider.mjs.map +1 -0
  83. package/dist/web-page/core.cjs +1388 -0
  84. package/dist/web-page/core.mjs +1387 -0
  85. package/dist/web-page/core.mjs.map +1 -0
  86. package/dist/web-page/css.cjs +1699 -0
  87. package/dist/web-page/css.mjs +1698 -0
  88. package/dist/web-page/css.mjs.map +1 -0
  89. package/dist/web-page/icons.cjs +248 -0
  90. package/dist/web-page/icons.mjs +248 -0
  91. package/dist/web-page/icons.mjs.map +1 -0
  92. package/dist/web-page/overlay-themes.cjs +514 -0
  93. package/dist/web-page/overlay-themes.mjs +513 -0
  94. package/dist/web-page/overlay-themes.mjs.map +1 -0
  95. package/dist/web-page/renderers/action.cjs +72 -0
  96. package/dist/web-page/renderers/action.mjs +72 -0
  97. package/dist/web-page/renderers/action.mjs.map +1 -0
  98. package/dist/web-page/renderers/broadcast.cjs +160 -0
  99. package/dist/web-page/renderers/broadcast.mjs +160 -0
  100. package/dist/web-page/renderers/broadcast.mjs.map +1 -0
  101. package/dist/web-page/renderers/calendar.cjs +137 -0
  102. package/dist/web-page/renderers/calendar.mjs +137 -0
  103. package/dist/web-page/renderers/calendar.mjs.map +1 -0
  104. package/dist/web-page/renderers/canvas.cjs +173 -0
  105. package/dist/web-page/renderers/canvas.mjs +173 -0
  106. package/dist/web-page/renderers/canvas.mjs.map +1 -0
  107. package/dist/web-page/renderers/cdn-loader.cjs +25 -0
  108. package/dist/web-page/renderers/cdn-loader.mjs +25 -0
  109. package/dist/web-page/renderers/cdn-loader.mjs.map +1 -0
  110. package/dist/web-page/renderers/chart.cjs +101 -0
  111. package/dist/web-page/renderers/chart.mjs +101 -0
  112. package/dist/web-page/renderers/chart.mjs.map +1 -0
  113. package/dist/web-page/renderers/deck.cjs +390 -0
  114. package/dist/web-page/renderers/deck.mjs +390 -0
  115. package/dist/web-page/renderers/deck.mjs.map +1 -0
  116. package/dist/web-page/renderers/device.cjs +1015 -0
  117. package/dist/web-page/renderers/device.mjs +1015 -0
  118. package/dist/web-page/renderers/device.mjs.map +1 -0
  119. package/dist/web-page/renderers/editor.cjs +127 -0
  120. package/dist/web-page/renderers/editor.mjs +127 -0
  121. package/dist/web-page/renderers/editor.mjs.map +1 -0
  122. package/dist/web-page/renderers/finance-chart.cjs +178 -0
  123. package/dist/web-page/renderers/finance-chart.mjs +178 -0
  124. package/dist/web-page/renderers/finance-chart.mjs.map +1 -0
  125. package/dist/web-page/renderers/frame.cjs +274 -0
  126. package/dist/web-page/renderers/frame.mjs +274 -0
  127. package/dist/web-page/renderers/frame.mjs.map +1 -0
  128. package/dist/web-page/renderers/globe.cjs +119 -0
  129. package/dist/web-page/renderers/globe.mjs +119 -0
  130. package/dist/web-page/renderers/globe.mjs.map +1 -0
  131. package/dist/web-page/renderers/input.cjs +137 -0
  132. package/dist/web-page/renderers/input.mjs +137 -0
  133. package/dist/web-page/renderers/input.mjs.map +1 -0
  134. package/dist/web-page/renderers/list.cjs +1243 -0
  135. package/dist/web-page/renderers/list.mjs +1243 -0
  136. package/dist/web-page/renderers/list.mjs.map +1 -0
  137. package/dist/web-page/renderers/map.cjs +126 -0
  138. package/dist/web-page/renderers/map.mjs +126 -0
  139. package/dist/web-page/renderers/map.mjs.map +1 -0
  140. package/dist/web-page/renderers/media.cjs +106 -0
  141. package/dist/web-page/renderers/media.mjs +106 -0
  142. package/dist/web-page/renderers/media.mjs.map +1 -0
  143. package/dist/web-page/renderers/moonphase.cjs +105 -0
  144. package/dist/web-page/renderers/moonphase.mjs +105 -0
  145. package/dist/web-page/renderers/moonphase.mjs.map +1 -0
  146. package/dist/web-page/renderers/natal-chart.cjs +222 -0
  147. package/dist/web-page/renderers/natal-chart.mjs +222 -0
  148. package/dist/web-page/renderers/natal-chart.mjs.map +1 -0
  149. package/dist/web-page/renderers/overlay.cjs +531 -0
  150. package/dist/web-page/renderers/overlay.mjs +531 -0
  151. package/dist/web-page/renderers/overlay.mjs.map +1 -0
  152. package/dist/web-page/renderers/table.cjs +74 -0
  153. package/dist/web-page/renderers/table.mjs +74 -0
  154. package/dist/web-page/renderers/table.mjs.map +1 -0
  155. package/dist/web-page/renderers/terminal.cjs +30 -0
  156. package/dist/web-page/renderers/terminal.mjs +30 -0
  157. package/dist/web-page/renderers/terminal.mjs.map +1 -0
  158. package/dist/web-page/renderers/text.cjs +109 -0
  159. package/dist/web-page/renderers/text.mjs +109 -0
  160. package/dist/web-page/renderers/text.mjs.map +1 -0
  161. package/dist/web-page/renderers/ticker.cjs +133 -0
  162. package/dist/web-page/renderers/ticker.mjs +133 -0
  163. package/dist/web-page/renderers/ticker.mjs.map +1 -0
  164. package/dist/web-page/renderers/time.cjs +69 -0
  165. package/dist/web-page/renderers/time.mjs +69 -0
  166. package/dist/web-page/renderers/time.mjs.map +1 -0
  167. package/dist/web-page/renderers/unknown.cjs +20 -0
  168. package/dist/web-page/renderers/unknown.mjs +20 -0
  169. package/dist/web-page/renderers/unknown.mjs.map +1 -0
  170. package/dist/web-page/renderers/view.cjs +161 -0
  171. package/dist/web-page/renderers/view.mjs +161 -0
  172. package/dist/web-page/renderers/view.mjs.map +1 -0
  173. package/dist/web-page/renderers/wm.cjs +669 -0
  174. package/dist/web-page/renderers/wm.mjs +669 -0
  175. package/dist/web-page/renderers/wm.mjs.map +1 -0
  176. package/dist/web-page/skeleton.cjs +103 -0
  177. package/dist/web-page/skeleton.mjs +103 -0
  178. package/dist/web-page/skeleton.mjs.map +1 -0
  179. package/dist/web-page.cjs +114 -0
  180. package/dist/web-page.d.cts +19 -0
  181. package/dist/web-page.d.cts.map +1 -0
  182. package/dist/web-page.d.mts +19 -0
  183. package/dist/web-page.d.mts.map +1 -0
  184. package/dist/web-page.mjs +115 -0
  185. package/dist/web-page.mjs.map +1 -0
  186. package/dist/web.cjs +827 -0
  187. package/dist/web.d.cts +144 -0
  188. package/dist/web.d.cts.map +1 -0
  189. package/dist/web.d.mts +144 -0
  190. package/dist/web.d.mts.map +1 -0
  191. package/dist/web.mjs +828 -0
  192. package/dist/web.mjs.map +1 -0
  193. package/dist/wm-state.cjs +172 -0
  194. package/dist/wm-state.mjs +171 -0
  195. package/dist/wm-state.mjs.map +1 -0
  196. package/package.json +59 -0
@@ -0,0 +1,84 @@
1
+ //#region src/degradation.ts
2
+ /**
3
+ * Each entry maps a primitive type to its ordered degradation chain.
4
+ * The chain is tried left-to-right; the first supported type wins.
5
+ * "unsupported" means no meaningful fallback exists.
6
+ */
7
+ const DEGRADATION_CHAINS = {
8
+ globe: [
9
+ "map",
10
+ "media",
11
+ "text"
12
+ ],
13
+ chart: ["table", "text"],
14
+ map: ["media", "text"],
15
+ editor: ["input", "text"],
16
+ terminal: ["text"],
17
+ canvas: ["media", "text"],
18
+ rtc: ["unsupported"],
19
+ calendar: ["table", "text"],
20
+ time: ["text"],
21
+ overlay: ["text"]
22
+ };
23
+ /**
24
+ * Walk the AUP tree and degrade unsupported primitives.
25
+ * Returns a new tree (does not mutate the original).
26
+ */
27
+ function degradeTree(node, caps) {
28
+ return degradeNode(node, caps);
29
+ }
30
+ function isSupported(primitiveType, caps) {
31
+ const cap = caps.primitives[primitiveType];
32
+ return cap === "native" || cap === "webview" || cap === "partial";
33
+ }
34
+ function degradeNode(node, caps) {
35
+ let result = degradeNodeType(node, caps);
36
+ if (result.children && result.children.length > 0) {
37
+ const degradedChildren = result.children.map((child) => degradeNode(child, caps));
38
+ if (degradedChildren.some((c, i) => c !== result.children[i])) result = {
39
+ ...result,
40
+ children: degradedChildren
41
+ };
42
+ }
43
+ return result;
44
+ }
45
+ function degradeNodeType(node, caps) {
46
+ const nodeType = node.type;
47
+ if (isSupported(nodeType, caps)) return node;
48
+ const chain = DEGRADATION_CHAINS[nodeType];
49
+ if (!chain) return node;
50
+ const originalType = nodeType;
51
+ for (const fallbackType of chain) {
52
+ if (fallbackType === "unsupported") return {
53
+ ...node,
54
+ type: "text",
55
+ props: {
56
+ ...node.props,
57
+ _degradedFrom: originalType,
58
+ _unsupported: true,
59
+ content: node.props?.content ?? `[${originalType}: unsupported on this device]`
60
+ }
61
+ };
62
+ if (isSupported(fallbackType, caps)) return {
63
+ ...node,
64
+ type: fallbackType,
65
+ props: {
66
+ ...node.props,
67
+ _degradedFrom: originalType
68
+ }
69
+ };
70
+ }
71
+ return {
72
+ ...node,
73
+ type: "text",
74
+ props: {
75
+ ...node.props,
76
+ _degradedFrom: originalType,
77
+ content: node.props?.content ?? `[${originalType}: degraded to text]`
78
+ }
79
+ };
80
+ }
81
+
82
+ //#endregion
83
+ export { DEGRADATION_CHAINS, degradeTree };
84
+ //# sourceMappingURL=degradation.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"degradation.mjs","names":[],"sources":["../src/degradation.ts"],"sourcesContent":["/**\n * AUP Degradation Transformer (D14)\n *\n * Walks an AUP node tree and degrades unsupported primitives\n * according to protocol-defined degradation chains.\n * Annotates degraded nodes with `_degradedFrom` in props.\n */\n\nimport type { AUPNode, DeviceCaps, PrimitiveCap } from \"./aup-types.js\";\n\n// ── Degradation Chain Registry ──\n\n/**\n * Each entry maps a primitive type to its ordered degradation chain.\n * The chain is tried left-to-right; the first supported type wins.\n * \"unsupported\" means no meaningful fallback exists.\n */\nexport const DEGRADATION_CHAINS: Record<string, string[]> = {\n globe: [\"map\", \"media\", \"text\"],\n chart: [\"table\", \"text\"],\n map: [\"media\", \"text\"],\n editor: [\"input\", \"text\"],\n terminal: [\"text\"],\n canvas: [\"media\", \"text\"],\n rtc: [\"unsupported\"],\n calendar: [\"table\", \"text\"],\n time: [\"text\"],\n overlay: [\"text\"],\n // media(video) handled via subtype check in degradeNode\n};\n\n// ── Core API ──\n\n/**\n * Walk the AUP tree and degrade unsupported primitives.\n * Returns a new tree (does not mutate the original).\n */\nexport function degradeTree(node: AUPNode, caps: DeviceCaps): AUPNode {\n return degradeNode(node, caps);\n}\n\n// ── Internal ──\n\nfunction isSupported(primitiveType: string, caps: DeviceCaps): boolean {\n const cap: PrimitiveCap | undefined = caps.primitives[primitiveType];\n // \"native\", \"webview\", \"partial\" are all considered supported\n // \"unsupported\" or missing means not supported\n return cap === \"native\" || cap === \"webview\" || cap === \"partial\";\n}\n\nfunction degradeNode(node: AUPNode, caps: DeviceCaps): AUPNode {\n // First, degrade this node if needed\n let result = degradeNodeType(node, caps);\n\n // Then recurse into children\n if (result.children && result.children.length > 0) {\n const degradedChildren = result.children.map((child) => degradeNode(child, caps));\n // Only create new object if children actually changed\n if (degradedChildren.some((c, i) => c !== result.children![i])) {\n result = { ...result, children: degradedChildren };\n }\n }\n\n return result;\n}\n\nfunction degradeNodeType(node: AUPNode, caps: DeviceCaps): AUPNode {\n const nodeType = node.type;\n\n // Already supported — no degradation needed\n if (isSupported(nodeType, caps)) {\n return node;\n }\n\n // No degradation chain defined — pass through (unknown/custom type)\n const chain = DEGRADATION_CHAINS[nodeType];\n if (!chain) {\n return node;\n }\n\n // Walk the chain to find the first supported fallback\n const originalType = nodeType;\n for (const fallbackType of chain) {\n if (fallbackType === \"unsupported\") {\n // No meaningful fallback — mark as unsupported\n return {\n ...node,\n type: \"text\",\n props: {\n ...node.props,\n _degradedFrom: originalType,\n _unsupported: true,\n content: node.props?.content ?? `[${originalType}: unsupported on this device]`,\n },\n };\n }\n\n if (isSupported(fallbackType, caps)) {\n // Found a supported fallback\n return {\n ...node,\n type: fallbackType,\n props: {\n ...node.props,\n _degradedFrom: originalType,\n },\n };\n }\n }\n\n // Exhausted chain without finding support — degrade to text with annotation\n return {\n ...node,\n type: \"text\",\n props: {\n ...node.props,\n _degradedFrom: originalType,\n content: node.props?.content ?? `[${originalType}: degraded to text]`,\n },\n };\n}\n"],"mappings":";;;;;;AAiBA,MAAa,qBAA+C;CAC1D,OAAO;EAAC;EAAO;EAAS;EAAO;CAC/B,OAAO,CAAC,SAAS,OAAO;CACxB,KAAK,CAAC,SAAS,OAAO;CACtB,QAAQ,CAAC,SAAS,OAAO;CACzB,UAAU,CAAC,OAAO;CAClB,QAAQ,CAAC,SAAS,OAAO;CACzB,KAAK,CAAC,cAAc;CACpB,UAAU,CAAC,SAAS,OAAO;CAC3B,MAAM,CAAC,OAAO;CACd,SAAS,CAAC,OAAO;CAElB;;;;;AAQD,SAAgB,YAAY,MAAe,MAA2B;AACpE,QAAO,YAAY,MAAM,KAAK;;AAKhC,SAAS,YAAY,eAAuB,MAA2B;CACrE,MAAM,MAAgC,KAAK,WAAW;AAGtD,QAAO,QAAQ,YAAY,QAAQ,aAAa,QAAQ;;AAG1D,SAAS,YAAY,MAAe,MAA2B;CAE7D,IAAI,SAAS,gBAAgB,MAAM,KAAK;AAGxC,KAAI,OAAO,YAAY,OAAO,SAAS,SAAS,GAAG;EACjD,MAAM,mBAAmB,OAAO,SAAS,KAAK,UAAU,YAAY,OAAO,KAAK,CAAC;AAEjF,MAAI,iBAAiB,MAAM,GAAG,MAAM,MAAM,OAAO,SAAU,GAAG,CAC5D,UAAS;GAAE,GAAG;GAAQ,UAAU;GAAkB;;AAItD,QAAO;;AAGT,SAAS,gBAAgB,MAAe,MAA2B;CACjE,MAAM,WAAW,KAAK;AAGtB,KAAI,YAAY,UAAU,KAAK,CAC7B,QAAO;CAIT,MAAM,QAAQ,mBAAmB;AACjC,KAAI,CAAC,MACH,QAAO;CAIT,MAAM,eAAe;AACrB,MAAK,MAAM,gBAAgB,OAAO;AAChC,MAAI,iBAAiB,cAEnB,QAAO;GACL,GAAG;GACH,MAAM;GACN,OAAO;IACL,GAAG,KAAK;IACR,eAAe;IACf,cAAc;IACd,SAAS,KAAK,OAAO,WAAW,IAAI,aAAa;IAClD;GACF;AAGH,MAAI,YAAY,cAAc,KAAK,CAEjC,QAAO;GACL,GAAG;GACH,MAAM;GACN,OAAO;IACL,GAAG,KAAK;IACR,eAAe;IAChB;GACF;;AAKL,QAAO;EACL,GAAG;EACH,MAAM;EACN,OAAO;GACL,GAAG,KAAK;GACR,eAAe;GACf,SAAS,KAAK,OAAO,WAAW,IAAI,aAAa;GAClD;EACF"}
package/dist/index.cjs ADDED
@@ -0,0 +1,36 @@
1
+ const require_aup_types = require('./aup-types.cjs');
2
+ const require_aup_protocol = require('./aup-protocol.cjs');
3
+ const require_backend = require('./backend.cjs');
4
+ const require_degradation = require('./degradation.cjs');
5
+ const require_runtime = require('./runtime.cjs');
6
+ const require_session = require('./session.cjs');
7
+ const require_tty = require('./tty.cjs');
8
+ const require_term = require('./term.cjs');
9
+ const require_web_page = require('./web-page.cjs');
10
+ const require_web = require('./web.cjs');
11
+ const require_ui_provider = require('./ui-provider.cjs');
12
+
13
+ exports.AFSRuntime = require_runtime.AFSRuntime;
14
+ exports.AFSUIProvider = require_ui_provider.AFSUIProvider;
15
+ exports.AUPNodeStore = require_aup_protocol.AUPNodeStore;
16
+ exports.AUPSceneManager = require_aup_protocol.AUPSceneManager;
17
+ exports.AUP_PRIMITIVES = require_aup_types.AUP_PRIMITIVES;
18
+ exports.DEGRADATION_CHAINS = require_degradation.DEGRADATION_CHAINS;
19
+ exports.DEVICE_CAPS_TERM = require_aup_types.DEVICE_CAPS_TERM;
20
+ exports.DEVICE_CAPS_TTY = require_aup_types.DEVICE_CAPS_TTY;
21
+ exports.DEVICE_CAPS_WEB_CHAT = require_aup_types.DEVICE_CAPS_WEB_CHAT;
22
+ exports.DEVICE_CAPS_WEB_FULL = require_aup_types.DEVICE_CAPS_WEB_FULL;
23
+ exports.Session = require_session.Session;
24
+ exports.SessionManager = require_session.SessionManager;
25
+ exports.TTYBackend = require_tty.TTYBackend;
26
+ exports.TermBackend = require_term.TermBackend;
27
+ exports.WEB_CLIENT_HTML = require_web_page.WEB_CLIENT_HTML;
28
+ exports.WebBackend = require_web.WebBackend;
29
+ exports.createMockInputSource = require_tty.createMockInputSource;
30
+ exports.degradeTree = require_degradation.degradeTree;
31
+ exports.fillPrimitives = require_aup_types.fillPrimitives;
32
+ exports.isAUPTransport = require_backend.isAUPTransport;
33
+ exports.isSessionAware = require_backend.isSessionAware;
34
+ exports.validateDeviceCaps = require_aup_types.validateDeviceCaps;
35
+ exports.validateNode = require_aup_types.validateNode;
36
+ exports.validatePatchOp = require_aup_types.validatePatchOp;
@@ -0,0 +1,12 @@
1
+ import { AUPClientMessage, AUPCreateOp, AUPEvent, AUPEventMessage, AUPEventResultMessage, AUPIntent, AUPNode, AUPPatchMessage, AUPPatchOp, AUPRemoveOp, AUPRenderMessage, AUPReorderOp, AUPServerMessage, AUPSize, AUPUpdateOp, AUPVariant, AUP_PRIMITIVES, DEVICE_CAPS_TERM, DEVICE_CAPS_TTY, DEVICE_CAPS_WEB_CHAT, DEVICE_CAPS_WEB_FULL, DeviceCaps, DeviceDisplay, DeviceInput, PrimitiveCap, fillPrimitives, validateDeviceCaps, validateNode, validatePatchOp } from "./aup-types.cjs";
2
+ import { AUPNodeStore, AUPSceneManager } from "./aup-protocol.cjs";
3
+ import { AUPTransportBackend, AupEventHandler, ChannelJoinHandler, FormField, PageResolver, PromptOptions, PromptResult, ReadOptions, SessionAwareBackend, SessionFactory, SessionJoinHandler, UIBackend, ViewportInfo, WriteOptions, isAUPTransport, isSessionAware } from "./backend.cjs";
4
+ import { DEGRADATION_CHAINS, degradeTree } from "./degradation.cjs";
5
+ import { AFSRuntime, RuntimeHandler } from "./runtime.cjs";
6
+ import { Message, MessageFilter, PageData, Session, SessionManager } from "./session.cjs";
7
+ import { TTYBackend, TTYBackendOptions, TTYInputSource, createMockInputSource } from "./tty.cjs";
8
+ import { TermBackend, TermBackendOptions } from "./term.cjs";
9
+ import { WebBackend, WebBackendOptions } from "./web.cjs";
10
+ import { AFSUIProvider, AFSUIProviderOptions } from "./ui-provider.cjs";
11
+ import { WEB_CLIENT_HTML } from "./web-page.cjs";
12
+ export { AFSRuntime, AFSUIProvider, type AFSUIProviderOptions, type AUPClientMessage, type AUPCreateOp, type AUPEvent, type AUPEventMessage, type AUPEventResultMessage, type AUPIntent, type AUPNode, AUPNodeStore, type AUPPatchMessage, type AUPPatchOp, type AUPRemoveOp, type AUPRenderMessage, type AUPReorderOp, AUPSceneManager, type AUPServerMessage, type AUPSize, type AUPTransportBackend, type AUPUpdateOp, type AUPVariant, AUP_PRIMITIVES, type AupEventHandler, type ChannelJoinHandler, DEGRADATION_CHAINS, DEVICE_CAPS_TERM, DEVICE_CAPS_TTY, DEVICE_CAPS_WEB_CHAT, DEVICE_CAPS_WEB_FULL, type DeviceCaps, type DeviceDisplay, type DeviceInput, type FormField, type Message, type MessageFilter, type PageData, type PageResolver, type PrimitiveCap, type PromptOptions, type PromptResult, type ReadOptions, type RuntimeHandler, Session, type SessionAwareBackend, type SessionFactory, type SessionJoinHandler, SessionManager, TTYBackend, type TTYBackendOptions, type TTYInputSource, TermBackend, type TermBackendOptions, type UIBackend, type ViewportInfo, WEB_CLIENT_HTML, WebBackend, type WebBackendOptions, type WriteOptions, createMockInputSource, degradeTree, fillPrimitives, isAUPTransport, isSessionAware, validateDeviceCaps, validateNode, validatePatchOp };
@@ -0,0 +1,12 @@
1
+ import { AUPClientMessage, AUPCreateOp, AUPEvent, AUPEventMessage, AUPEventResultMessage, AUPIntent, AUPNode, AUPPatchMessage, AUPPatchOp, AUPRemoveOp, AUPRenderMessage, AUPReorderOp, AUPServerMessage, AUPSize, AUPUpdateOp, AUPVariant, AUP_PRIMITIVES, DEVICE_CAPS_TERM, DEVICE_CAPS_TTY, DEVICE_CAPS_WEB_CHAT, DEVICE_CAPS_WEB_FULL, DeviceCaps, DeviceDisplay, DeviceInput, PrimitiveCap, fillPrimitives, validateDeviceCaps, validateNode, validatePatchOp } from "./aup-types.mjs";
2
+ import { AUPNodeStore, AUPSceneManager } from "./aup-protocol.mjs";
3
+ import { AUPTransportBackend, AupEventHandler, ChannelJoinHandler, FormField, PageResolver, PromptOptions, PromptResult, ReadOptions, SessionAwareBackend, SessionFactory, SessionJoinHandler, UIBackend, ViewportInfo, WriteOptions, isAUPTransport, isSessionAware } from "./backend.mjs";
4
+ import { DEGRADATION_CHAINS, degradeTree } from "./degradation.mjs";
5
+ import { AFSRuntime, RuntimeHandler } from "./runtime.mjs";
6
+ import { Message, MessageFilter, PageData, Session, SessionManager } from "./session.mjs";
7
+ import { TTYBackend, TTYBackendOptions, TTYInputSource, createMockInputSource } from "./tty.mjs";
8
+ import { TermBackend, TermBackendOptions } from "./term.mjs";
9
+ import { WebBackend, WebBackendOptions } from "./web.mjs";
10
+ import { AFSUIProvider, AFSUIProviderOptions } from "./ui-provider.mjs";
11
+ import { WEB_CLIENT_HTML } from "./web-page.mjs";
12
+ export { AFSRuntime, AFSUIProvider, type AFSUIProviderOptions, type AUPClientMessage, type AUPCreateOp, type AUPEvent, type AUPEventMessage, type AUPEventResultMessage, type AUPIntent, type AUPNode, AUPNodeStore, type AUPPatchMessage, type AUPPatchOp, type AUPRemoveOp, type AUPRenderMessage, type AUPReorderOp, AUPSceneManager, type AUPServerMessage, type AUPSize, type AUPTransportBackend, type AUPUpdateOp, type AUPVariant, AUP_PRIMITIVES, type AupEventHandler, type ChannelJoinHandler, DEGRADATION_CHAINS, DEVICE_CAPS_TERM, DEVICE_CAPS_TTY, DEVICE_CAPS_WEB_CHAT, DEVICE_CAPS_WEB_FULL, type DeviceCaps, type DeviceDisplay, type DeviceInput, type FormField, type Message, type MessageFilter, type PageData, type PageResolver, type PrimitiveCap, type PromptOptions, type PromptResult, type ReadOptions, type RuntimeHandler, Session, type SessionAwareBackend, type SessionFactory, type SessionJoinHandler, SessionManager, TTYBackend, type TTYBackendOptions, type TTYInputSource, TermBackend, type TermBackendOptions, type UIBackend, type ViewportInfo, WEB_CLIENT_HTML, WebBackend, type WebBackendOptions, type WriteOptions, createMockInputSource, degradeTree, fillPrimitives, isAUPTransport, isSessionAware, validateDeviceCaps, validateNode, validatePatchOp };
package/dist/index.mjs ADDED
@@ -0,0 +1,13 @@
1
+ import { AUP_PRIMITIVES, DEVICE_CAPS_TERM, DEVICE_CAPS_TTY, DEVICE_CAPS_WEB_CHAT, DEVICE_CAPS_WEB_FULL, fillPrimitives, validateDeviceCaps, validateNode, validatePatchOp } from "./aup-types.mjs";
2
+ import { AUPNodeStore, AUPSceneManager } from "./aup-protocol.mjs";
3
+ import { isAUPTransport, isSessionAware } from "./backend.mjs";
4
+ import { DEGRADATION_CHAINS, degradeTree } from "./degradation.mjs";
5
+ import { AFSRuntime } from "./runtime.mjs";
6
+ import { Session, SessionManager } from "./session.mjs";
7
+ import { TTYBackend, createMockInputSource } from "./tty.mjs";
8
+ import { TermBackend } from "./term.mjs";
9
+ import { WEB_CLIENT_HTML } from "./web-page.mjs";
10
+ import { WebBackend } from "./web.mjs";
11
+ import { AFSUIProvider } from "./ui-provider.mjs";
12
+
13
+ export { AFSRuntime, AFSUIProvider, AUPNodeStore, AUPSceneManager, AUP_PRIMITIVES, DEGRADATION_CHAINS, DEVICE_CAPS_TERM, DEVICE_CAPS_TTY, DEVICE_CAPS_WEB_CHAT, DEVICE_CAPS_WEB_FULL, Session, SessionManager, TTYBackend, TermBackend, WEB_CLIENT_HTML, WebBackend, createMockInputSource, degradeTree, fillPrimitives, isAUPTransport, isSessionAware, validateDeviceCaps, validateNode, validatePatchOp };
@@ -0,0 +1,117 @@
1
+ let ufo = require("ufo");
2
+
3
+ //#region src/runtime.ts
4
+ /**
5
+ * AFSRuntime — minimal event loop for UI device interaction.
6
+ *
7
+ * Reads input from a mounted UI device, dispatches to a registered handler,
8
+ * and writes the handler's response back to device output.
9
+ *
10
+ * Usage:
11
+ * const runtime = new AFSRuntime(afs);
12
+ * runtime.on("/ui", async (input) => `Echo: ${input}`);
13
+ * await runtime.start();
14
+ * // ... running ...
15
+ * await runtime.stop();
16
+ */
17
+ var AFSRuntime = class {
18
+ afs;
19
+ handlers = /* @__PURE__ */ new Map();
20
+ running = false;
21
+ stopRequested = false;
22
+ loopPromise = null;
23
+ resolveStop = null;
24
+ stopSignal = null;
25
+ /**
26
+ * Orphaned read promises from previous stop/start cycles.
27
+ * When the loop is blocked on read() and stop() is called, the read
28
+ * promise is preserved here so the next start() can consume it
29
+ * instead of losing the input.
30
+ */
31
+ orphanedReads = /* @__PURE__ */ new Map();
32
+ constructor(afs) {
33
+ this.afs = afs;
34
+ }
35
+ /** Whether the runtime is currently running. */
36
+ get isRunning() {
37
+ return this.running;
38
+ }
39
+ /**
40
+ * Register a handler for a device mount path.
41
+ * The handler receives user input and optionally returns a response.
42
+ */
43
+ on(devicePath, handler) {
44
+ this.handlers.set(devicePath, handler);
45
+ return this;
46
+ }
47
+ /**
48
+ * Start the event loop. Validates all registered device paths,
49
+ * then begins reading input and dispatching to handlers.
50
+ */
51
+ async start() {
52
+ if (this.running) throw new Error("Runtime is already running");
53
+ if (this.handlers.size === 0) throw new Error("No handlers registered");
54
+ for (const path of this.handlers.keys()) await this.afs.stat((0, ufo.joinURL)(path, "input"));
55
+ this.running = true;
56
+ this.stopRequested = false;
57
+ this.stopSignal = new Promise((resolve) => {
58
+ this.resolveStop = resolve;
59
+ });
60
+ this.loopPromise = this.runLoops();
61
+ }
62
+ /**
63
+ * Stop the event loop. Waits for any in-progress handler to complete.
64
+ */
65
+ async stop() {
66
+ if (!this.running) return;
67
+ this.stopRequested = true;
68
+ this.resolveStop?.();
69
+ if (this.loopPromise) await this.loopPromise;
70
+ this.running = false;
71
+ this.loopPromise = null;
72
+ this.resolveStop = null;
73
+ this.stopSignal = null;
74
+ }
75
+ async runLoops() {
76
+ const loops = [...this.handlers.entries()].map(([path, handler]) => this.runLoop(path, handler));
77
+ await Promise.allSettled(loops);
78
+ }
79
+ async runLoop(devicePath, handler) {
80
+ const inputPath = (0, ufo.joinURL)(devicePath, "input");
81
+ const outputPath = (0, ufo.joinURL)(devicePath, "output");
82
+ while (!this.stopRequested) {
83
+ let input;
84
+ const orphaned = this.orphanedReads.get(devicePath);
85
+ if (orphaned) {
86
+ this.orphanedReads.delete(devicePath);
87
+ input = await orphaned;
88
+ } else {
89
+ const readPromise = this.afs.read(inputPath).then((r) => String(r.data?.content ?? ""));
90
+ const result = await Promise.race([readPromise.then((value) => ({
91
+ stopped: false,
92
+ value
93
+ })), this.stopSignal.then(() => ({
94
+ stopped: true,
95
+ value: ""
96
+ }))]);
97
+ if (result.stopped || this.stopRequested) {
98
+ this.orphanedReads.set(devicePath, readPromise);
99
+ break;
100
+ }
101
+ input = result.value;
102
+ }
103
+ try {
104
+ const response = await handler(input);
105
+ if (response != null) await this.afs.write(outputPath, { content: String(response) });
106
+ } catch (err) {
107
+ const message = err instanceof Error ? err.message : String(err);
108
+ try {
109
+ await this.afs.write(outputPath, { content: `Error: ${message}` });
110
+ } catch {}
111
+ }
112
+ }
113
+ }
114
+ };
115
+
116
+ //#endregion
117
+ exports.AFSRuntime = AFSRuntime;
@@ -0,0 +1,59 @@
1
+ import { AFS } from "@aigne/afs";
2
+
3
+ //#region src/runtime.d.ts
4
+ /**
5
+ * Handler function for processing user input.
6
+ * Return a string to write to device output, or null/undefined to skip output.
7
+ */
8
+ type RuntimeHandler = (input: string) => string | void | null | undefined | Promise<string | void | null | undefined>;
9
+ /**
10
+ * AFSRuntime — minimal event loop for UI device interaction.
11
+ *
12
+ * Reads input from a mounted UI device, dispatches to a registered handler,
13
+ * and writes the handler's response back to device output.
14
+ *
15
+ * Usage:
16
+ * const runtime = new AFSRuntime(afs);
17
+ * runtime.on("/ui", async (input) => `Echo: ${input}`);
18
+ * await runtime.start();
19
+ * // ... running ...
20
+ * await runtime.stop();
21
+ */
22
+ declare class AFSRuntime {
23
+ private afs;
24
+ private handlers;
25
+ private running;
26
+ private stopRequested;
27
+ private loopPromise;
28
+ private resolveStop;
29
+ private stopSignal;
30
+ /**
31
+ * Orphaned read promises from previous stop/start cycles.
32
+ * When the loop is blocked on read() and stop() is called, the read
33
+ * promise is preserved here so the next start() can consume it
34
+ * instead of losing the input.
35
+ */
36
+ private orphanedReads;
37
+ constructor(afs: AFS);
38
+ /** Whether the runtime is currently running. */
39
+ get isRunning(): boolean;
40
+ /**
41
+ * Register a handler for a device mount path.
42
+ * The handler receives user input and optionally returns a response.
43
+ */
44
+ on(devicePath: string, handler: RuntimeHandler): this;
45
+ /**
46
+ * Start the event loop. Validates all registered device paths,
47
+ * then begins reading input and dispatching to handlers.
48
+ */
49
+ start(): Promise<void>;
50
+ /**
51
+ * Stop the event loop. Waits for any in-progress handler to complete.
52
+ */
53
+ stop(): Promise<void>;
54
+ private runLoops;
55
+ private runLoop;
56
+ }
57
+ //#endregion
58
+ export { AFSRuntime, RuntimeHandler };
59
+ //# sourceMappingURL=runtime.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.cts","names":[],"sources":["../src/runtime.ts"],"mappings":";;;;;AAOA;;KAAY,cAAA,IACV,KAAA,gDACsC,OAAA;;;AAexC;;;;;;;;;;;cAAa,UAAA;EAAA,QACH,GAAA;EAAA,QACA,QAAA;EAAA,QACA,OAAA;EAAA,QACA,aAAA;EAAA,QACA,WAAA;EAAA,QACA,WAAA;EAAA,QACA,UAAA;EAUI;;;;;;EAAA,QAFJ,aAAA;cAEI,GAAA,EAAK,GAAA;EA+CX;EAAA,IA1CF,SAAA,CAAA;EA0DU;;;;EAlDd,EAAA,CAAG,UAAA,UAAoB,OAAA,EAAS,cAAA;;;;;EAS1B,KAAA,CAAA,GAAS,OAAA;;;;EAyBT,IAAA,CAAA,GAAQ,OAAA;EAAA,QAgBA,QAAA;EAAA,QAOA,OAAA;AAAA"}
@@ -0,0 +1,59 @@
1
+ import { AFS } from "@aigne/afs";
2
+
3
+ //#region src/runtime.d.ts
4
+ /**
5
+ * Handler function for processing user input.
6
+ * Return a string to write to device output, or null/undefined to skip output.
7
+ */
8
+ type RuntimeHandler = (input: string) => string | void | null | undefined | Promise<string | void | null | undefined>;
9
+ /**
10
+ * AFSRuntime — minimal event loop for UI device interaction.
11
+ *
12
+ * Reads input from a mounted UI device, dispatches to a registered handler,
13
+ * and writes the handler's response back to device output.
14
+ *
15
+ * Usage:
16
+ * const runtime = new AFSRuntime(afs);
17
+ * runtime.on("/ui", async (input) => `Echo: ${input}`);
18
+ * await runtime.start();
19
+ * // ... running ...
20
+ * await runtime.stop();
21
+ */
22
+ declare class AFSRuntime {
23
+ private afs;
24
+ private handlers;
25
+ private running;
26
+ private stopRequested;
27
+ private loopPromise;
28
+ private resolveStop;
29
+ private stopSignal;
30
+ /**
31
+ * Orphaned read promises from previous stop/start cycles.
32
+ * When the loop is blocked on read() and stop() is called, the read
33
+ * promise is preserved here so the next start() can consume it
34
+ * instead of losing the input.
35
+ */
36
+ private orphanedReads;
37
+ constructor(afs: AFS);
38
+ /** Whether the runtime is currently running. */
39
+ get isRunning(): boolean;
40
+ /**
41
+ * Register a handler for a device mount path.
42
+ * The handler receives user input and optionally returns a response.
43
+ */
44
+ on(devicePath: string, handler: RuntimeHandler): this;
45
+ /**
46
+ * Start the event loop. Validates all registered device paths,
47
+ * then begins reading input and dispatching to handlers.
48
+ */
49
+ start(): Promise<void>;
50
+ /**
51
+ * Stop the event loop. Waits for any in-progress handler to complete.
52
+ */
53
+ stop(): Promise<void>;
54
+ private runLoops;
55
+ private runLoop;
56
+ }
57
+ //#endregion
58
+ export { AFSRuntime, RuntimeHandler };
59
+ //# sourceMappingURL=runtime.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/runtime.ts"],"mappings":";;;;;AAOA;;KAAY,cAAA,IACV,KAAA,gDACsC,OAAA;;;AAexC;;;;;;;;;;;cAAa,UAAA;EAAA,QACH,GAAA;EAAA,QACA,QAAA;EAAA,QACA,OAAA;EAAA,QACA,aAAA;EAAA,QACA,WAAA;EAAA,QACA,WAAA;EAAA,QACA,UAAA;EAUI;;;;;;EAAA,QAFJ,aAAA;cAEI,GAAA,EAAK,GAAA;EA+CX;EAAA,IA1CF,SAAA,CAAA;EA0DU;;;;EAlDd,EAAA,CAAG,UAAA,UAAoB,OAAA,EAAS,cAAA;;;;;EAS1B,KAAA,CAAA,GAAS,OAAA;;;;EAyBT,IAAA,CAAA,GAAQ,OAAA;EAAA,QAgBA,QAAA;EAAA,QAOA,OAAA;AAAA"}
@@ -0,0 +1,118 @@
1
+ import { joinURL } from "ufo";
2
+
3
+ //#region src/runtime.ts
4
+ /**
5
+ * AFSRuntime — minimal event loop for UI device interaction.
6
+ *
7
+ * Reads input from a mounted UI device, dispatches to a registered handler,
8
+ * and writes the handler's response back to device output.
9
+ *
10
+ * Usage:
11
+ * const runtime = new AFSRuntime(afs);
12
+ * runtime.on("/ui", async (input) => `Echo: ${input}`);
13
+ * await runtime.start();
14
+ * // ... running ...
15
+ * await runtime.stop();
16
+ */
17
+ var AFSRuntime = class {
18
+ afs;
19
+ handlers = /* @__PURE__ */ new Map();
20
+ running = false;
21
+ stopRequested = false;
22
+ loopPromise = null;
23
+ resolveStop = null;
24
+ stopSignal = null;
25
+ /**
26
+ * Orphaned read promises from previous stop/start cycles.
27
+ * When the loop is blocked on read() and stop() is called, the read
28
+ * promise is preserved here so the next start() can consume it
29
+ * instead of losing the input.
30
+ */
31
+ orphanedReads = /* @__PURE__ */ new Map();
32
+ constructor(afs) {
33
+ this.afs = afs;
34
+ }
35
+ /** Whether the runtime is currently running. */
36
+ get isRunning() {
37
+ return this.running;
38
+ }
39
+ /**
40
+ * Register a handler for a device mount path.
41
+ * The handler receives user input and optionally returns a response.
42
+ */
43
+ on(devicePath, handler) {
44
+ this.handlers.set(devicePath, handler);
45
+ return this;
46
+ }
47
+ /**
48
+ * Start the event loop. Validates all registered device paths,
49
+ * then begins reading input and dispatching to handlers.
50
+ */
51
+ async start() {
52
+ if (this.running) throw new Error("Runtime is already running");
53
+ if (this.handlers.size === 0) throw new Error("No handlers registered");
54
+ for (const path of this.handlers.keys()) await this.afs.stat(joinURL(path, "input"));
55
+ this.running = true;
56
+ this.stopRequested = false;
57
+ this.stopSignal = new Promise((resolve) => {
58
+ this.resolveStop = resolve;
59
+ });
60
+ this.loopPromise = this.runLoops();
61
+ }
62
+ /**
63
+ * Stop the event loop. Waits for any in-progress handler to complete.
64
+ */
65
+ async stop() {
66
+ if (!this.running) return;
67
+ this.stopRequested = true;
68
+ this.resolveStop?.();
69
+ if (this.loopPromise) await this.loopPromise;
70
+ this.running = false;
71
+ this.loopPromise = null;
72
+ this.resolveStop = null;
73
+ this.stopSignal = null;
74
+ }
75
+ async runLoops() {
76
+ const loops = [...this.handlers.entries()].map(([path, handler]) => this.runLoop(path, handler));
77
+ await Promise.allSettled(loops);
78
+ }
79
+ async runLoop(devicePath, handler) {
80
+ const inputPath = joinURL(devicePath, "input");
81
+ const outputPath = joinURL(devicePath, "output");
82
+ while (!this.stopRequested) {
83
+ let input;
84
+ const orphaned = this.orphanedReads.get(devicePath);
85
+ if (orphaned) {
86
+ this.orphanedReads.delete(devicePath);
87
+ input = await orphaned;
88
+ } else {
89
+ const readPromise = this.afs.read(inputPath).then((r) => String(r.data?.content ?? ""));
90
+ const result = await Promise.race([readPromise.then((value) => ({
91
+ stopped: false,
92
+ value
93
+ })), this.stopSignal.then(() => ({
94
+ stopped: true,
95
+ value: ""
96
+ }))]);
97
+ if (result.stopped || this.stopRequested) {
98
+ this.orphanedReads.set(devicePath, readPromise);
99
+ break;
100
+ }
101
+ input = result.value;
102
+ }
103
+ try {
104
+ const response = await handler(input);
105
+ if (response != null) await this.afs.write(outputPath, { content: String(response) });
106
+ } catch (err) {
107
+ const message = err instanceof Error ? err.message : String(err);
108
+ try {
109
+ await this.afs.write(outputPath, { content: `Error: ${message}` });
110
+ } catch {}
111
+ }
112
+ }
113
+ }
114
+ };
115
+
116
+ //#endregion
117
+ export { AFSRuntime };
118
+ //# sourceMappingURL=runtime.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.mjs","names":[],"sources":["../src/runtime.ts"],"sourcesContent":["import type { AFS } from \"@aigne/afs\";\nimport { joinURL } from \"ufo\";\n\n/**\n * Handler function for processing user input.\n * Return a string to write to device output, or null/undefined to skip output.\n */\nexport type RuntimeHandler = (\n input: string,\n) => string | void | null | undefined | Promise<string | void | null | undefined>;\n\n/**\n * AFSRuntime — minimal event loop for UI device interaction.\n *\n * Reads input from a mounted UI device, dispatches to a registered handler,\n * and writes the handler's response back to device output.\n *\n * Usage:\n * const runtime = new AFSRuntime(afs);\n * runtime.on(\"/ui\", async (input) => `Echo: ${input}`);\n * await runtime.start();\n * // ... running ...\n * await runtime.stop();\n */\nexport class AFSRuntime {\n private afs: AFS;\n private handlers = new Map<string, RuntimeHandler>();\n private running = false;\n private stopRequested = false;\n private loopPromise: Promise<void> | null = null;\n private resolveStop: (() => void) | null = null;\n private stopSignal: Promise<void> | null = null;\n\n /**\n * Orphaned read promises from previous stop/start cycles.\n * When the loop is blocked on read() and stop() is called, the read\n * promise is preserved here so the next start() can consume it\n * instead of losing the input.\n */\n private orphanedReads = new Map<string, Promise<string>>();\n\n constructor(afs: AFS) {\n this.afs = afs;\n }\n\n /** Whether the runtime is currently running. */\n get isRunning(): boolean {\n return this.running;\n }\n\n /**\n * Register a handler for a device mount path.\n * The handler receives user input and optionally returns a response.\n */\n on(devicePath: string, handler: RuntimeHandler): this {\n this.handlers.set(devicePath, handler);\n return this;\n }\n\n /**\n * Start the event loop. Validates all registered device paths,\n * then begins reading input and dispatching to handlers.\n */\n async start(): Promise<void> {\n if (this.running) {\n throw new Error(\"Runtime is already running\");\n }\n if (this.handlers.size === 0) {\n throw new Error(\"No handlers registered\");\n }\n\n // Validate all device paths exist by stat-ing the input channel\n for (const path of this.handlers.keys()) {\n await this.afs.stat(joinURL(path, \"input\"));\n }\n\n this.running = true;\n this.stopRequested = false;\n this.stopSignal = new Promise((resolve) => {\n this.resolveStop = resolve;\n });\n\n this.loopPromise = this.runLoops();\n }\n\n /**\n * Stop the event loop. Waits for any in-progress handler to complete.\n */\n async stop(): Promise<void> {\n if (!this.running) return;\n\n this.stopRequested = true;\n this.resolveStop?.();\n\n if (this.loopPromise) {\n await this.loopPromise;\n }\n\n this.running = false;\n this.loopPromise = null;\n this.resolveStop = null;\n this.stopSignal = null;\n }\n\n private async runLoops(): Promise<void> {\n const loops = [...this.handlers.entries()].map(([path, handler]) =>\n this.runLoop(path, handler),\n );\n await Promise.allSettled(loops);\n }\n\n private async runLoop(devicePath: string, handler: RuntimeHandler): Promise<void> {\n const inputPath = joinURL(devicePath, \"input\");\n const outputPath = joinURL(devicePath, \"output\");\n\n while (!this.stopRequested) {\n let input: string;\n\n // Check for an orphaned read from a previous stop/start cycle\n const orphaned = this.orphanedReads.get(devicePath);\n if (orphaned) {\n this.orphanedReads.delete(devicePath);\n input = await orphaned;\n } else {\n // Race between reading input and the stop signal\n const readPromise = this.afs.read(inputPath).then((r) => String(r.data?.content ?? \"\"));\n\n const result = await Promise.race([\n readPromise.then((value) => ({ stopped: false as const, value })),\n this.stopSignal!.then(() => ({ stopped: true as const, value: \"\" })),\n ]);\n\n if (result.stopped || this.stopRequested) {\n // Preserve the orphaned read for the next cycle\n this.orphanedReads.set(devicePath, readPromise);\n break;\n }\n\n input = result.value;\n }\n\n try {\n const response = await handler(input);\n if (response != null) {\n await this.afs.write(outputPath, { content: String(response) });\n }\n } catch (err) {\n // Write error to device output; don't crash the loop\n const message = err instanceof Error ? err.message : String(err);\n try {\n await this.afs.write(outputPath, { content: `Error: ${message}` });\n } catch {\n // If even error output fails, silently continue\n }\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,IAAa,aAAb,MAAwB;CACtB,AAAQ;CACR,AAAQ,2BAAW,IAAI,KAA6B;CACpD,AAAQ,UAAU;CAClB,AAAQ,gBAAgB;CACxB,AAAQ,cAAoC;CAC5C,AAAQ,cAAmC;CAC3C,AAAQ,aAAmC;;;;;;;CAQ3C,AAAQ,gCAAgB,IAAI,KAA8B;CAE1D,YAAY,KAAU;AACpB,OAAK,MAAM;;;CAIb,IAAI,YAAqB;AACvB,SAAO,KAAK;;;;;;CAOd,GAAG,YAAoB,SAA+B;AACpD,OAAK,SAAS,IAAI,YAAY,QAAQ;AACtC,SAAO;;;;;;CAOT,MAAM,QAAuB;AAC3B,MAAI,KAAK,QACP,OAAM,IAAI,MAAM,6BAA6B;AAE/C,MAAI,KAAK,SAAS,SAAS,EACzB,OAAM,IAAI,MAAM,yBAAyB;AAI3C,OAAK,MAAM,QAAQ,KAAK,SAAS,MAAM,CACrC,OAAM,KAAK,IAAI,KAAK,QAAQ,MAAM,QAAQ,CAAC;AAG7C,OAAK,UAAU;AACf,OAAK,gBAAgB;AACrB,OAAK,aAAa,IAAI,SAAS,YAAY;AACzC,QAAK,cAAc;IACnB;AAEF,OAAK,cAAc,KAAK,UAAU;;;;;CAMpC,MAAM,OAAsB;AAC1B,MAAI,CAAC,KAAK,QAAS;AAEnB,OAAK,gBAAgB;AACrB,OAAK,eAAe;AAEpB,MAAI,KAAK,YACP,OAAM,KAAK;AAGb,OAAK,UAAU;AACf,OAAK,cAAc;AACnB,OAAK,cAAc;AACnB,OAAK,aAAa;;CAGpB,MAAc,WAA0B;EACtC,MAAM,QAAQ,CAAC,GAAG,KAAK,SAAS,SAAS,CAAC,CAAC,KAAK,CAAC,MAAM,aACrD,KAAK,QAAQ,MAAM,QAAQ,CAC5B;AACD,QAAM,QAAQ,WAAW,MAAM;;CAGjC,MAAc,QAAQ,YAAoB,SAAwC;EAChF,MAAM,YAAY,QAAQ,YAAY,QAAQ;EAC9C,MAAM,aAAa,QAAQ,YAAY,SAAS;AAEhD,SAAO,CAAC,KAAK,eAAe;GAC1B,IAAI;GAGJ,MAAM,WAAW,KAAK,cAAc,IAAI,WAAW;AACnD,OAAI,UAAU;AACZ,SAAK,cAAc,OAAO,WAAW;AACrC,YAAQ,MAAM;UACT;IAEL,MAAM,cAAc,KAAK,IAAI,KAAK,UAAU,CAAC,MAAM,MAAM,OAAO,EAAE,MAAM,WAAW,GAAG,CAAC;IAEvF,MAAM,SAAS,MAAM,QAAQ,KAAK,CAChC,YAAY,MAAM,WAAW;KAAE,SAAS;KAAgB;KAAO,EAAE,EACjE,KAAK,WAAY,YAAY;KAAE,SAAS;KAAe,OAAO;KAAI,EAAE,CACrE,CAAC;AAEF,QAAI,OAAO,WAAW,KAAK,eAAe;AAExC,UAAK,cAAc,IAAI,YAAY,YAAY;AAC/C;;AAGF,YAAQ,OAAO;;AAGjB,OAAI;IACF,MAAM,WAAW,MAAM,QAAQ,MAAM;AACrC,QAAI,YAAY,KACd,OAAM,KAAK,IAAI,MAAM,YAAY,EAAE,SAAS,OAAO,SAAS,EAAE,CAAC;YAE1D,KAAK;IAEZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,QAAI;AACF,WAAM,KAAK,IAAI,MAAM,YAAY,EAAE,SAAS,UAAU,WAAW,CAAC;YAC5D"}