appduct 0.10.0 → 0.11.1

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 (230) hide show
  1. package/README.md +122 -52
  2. package/dist/artifact-inspect.d.ts +0 -1
  3. package/dist/bin.d.ts +0 -1
  4. package/dist/bin.js +1250 -2
  5. package/dist/cli/command-options.d.ts +8 -1
  6. package/dist/cli/create-cli.d.ts +0 -1
  7. package/dist/cli/dispatch.d.ts +10 -1
  8. package/dist/cli/envelope.d.ts +19 -0
  9. package/dist/cli/global-flags.d.ts +30 -0
  10. package/dist/cli/open-target.d.ts +25 -8
  11. package/dist/cli/result-types.d.ts +31 -5
  12. package/dist/cli/router.d.ts +78 -0
  13. package/dist/cli/routes/daemon/index.d.ts +9 -0
  14. package/dist/cli/routes/daemon/index.js +848 -0
  15. package/dist/cli/routes/daemon/run.d.ts +3 -0
  16. package/dist/cli/routes/daemon/run.js +3246 -0
  17. package/dist/cli/routes/daemon/start.d.ts +3 -0
  18. package/dist/cli/routes/daemon/start.js +1677 -0
  19. package/dist/cli/routes/daemon/status.d.ts +4 -0
  20. package/dist/cli/routes/daemon/status.js +1685 -0
  21. package/dist/cli/routes/daemon/stop.d.ts +4 -0
  22. package/dist/cli/routes/daemon/stop.js +1655 -0
  23. package/dist/cli/routes/doctor.d.ts +3 -0
  24. package/dist/cli/routes/doctor.js +1075 -0
  25. package/dist/cli/routes/events.d.ts +3 -0
  26. package/dist/cli/routes/events.js +1915 -0
  27. package/dist/cli/routes/init.d.ts +3 -0
  28. package/dist/cli/routes/init.js +1486 -0
  29. package/dist/cli/routes/invoke.d.ts +3 -0
  30. package/dist/cli/routes/invoke.js +1893 -0
  31. package/dist/cli/routes/keygen.d.ts +3 -0
  32. package/dist/cli/routes/keygen.js +928 -0
  33. package/dist/cli/routes/link.d.ts +3 -0
  34. package/dist/cli/routes/link.js +2714 -0
  35. package/dist/cli/routes/ls.d.ts +3 -0
  36. package/dist/cli/routes/ls.js +1668 -0
  37. package/dist/cli/routes/mcp.d.ts +5 -0
  38. package/dist/cli/routes/mcp.js +3929 -0
  39. package/dist/cli/routes/revoke.d.ts +3 -0
  40. package/dist/cli/routes/revoke.js +1684 -0
  41. package/dist/cli/routes/tools.d.ts +3 -0
  42. package/dist/cli/routes/tools.js +1860 -0
  43. package/dist/cli/runner.d.ts +3 -11
  44. package/dist/cli/types.d.ts +9 -6
  45. package/dist/cli/version-guard.d.ts +20 -0
  46. package/dist/cli.d.ts +0 -1
  47. package/dist/client/app-client.d.ts +4 -4
  48. package/dist/client/bootstrap.d.ts +4 -4
  49. package/dist/client/connect.d.ts +0 -1
  50. package/dist/client/errors.d.ts +0 -1
  51. package/dist/client/index.d.ts +1 -2
  52. package/dist/client/index.js +2389 -16
  53. package/dist/commands/daemon/context.d.ts +14 -0
  54. package/dist/commands/daemon/run.d.ts +11 -0
  55. package/dist/commands/daemon/start.d.ts +5 -0
  56. package/dist/commands/daemon/status.d.ts +5 -0
  57. package/dist/commands/daemon/stop.d.ts +5 -0
  58. package/dist/commands/doctor.d.ts +0 -1
  59. package/dist/commands/events.d.ts +0 -1
  60. package/dist/commands/init.d.ts +17 -4
  61. package/dist/commands/invoke.d.ts +0 -1
  62. package/dist/commands/keygen.d.ts +0 -1
  63. package/dist/commands/link.d.ts +1 -2
  64. package/dist/commands/ls.d.ts +0 -1
  65. package/dist/commands/mcp.d.ts +2 -4
  66. package/dist/commands/revoke.d.ts +0 -1
  67. package/dist/commands/tools.d.ts +19 -3
  68. package/dist/daemon/address.d.ts +0 -1
  69. package/dist/daemon/audit.d.ts +6 -11
  70. package/dist/daemon/calls.d.ts +0 -1
  71. package/dist/daemon/config.d.ts +13 -16
  72. package/dist/daemon/daemon.d.ts +0 -1
  73. package/dist/daemon/event-bus.d.ts +0 -1
  74. package/dist/daemon/links.d.ts +0 -1
  75. package/dist/daemon/listener.d.ts +0 -1
  76. package/dist/daemon/log-rotation.d.ts +0 -1
  77. package/dist/daemon/pidfile.d.ts +14 -8
  78. package/dist/daemon/policy.d.ts +0 -1
  79. package/dist/daemon/registry.d.ts +0 -1
  80. package/dist/daemon/rpc-errors.d.ts +16 -0
  81. package/dist/daemon/rpc-server.d.ts +0 -11
  82. package/dist/daemon/sessions.d.ts +0 -1
  83. package/dist/daemon/socket-probe.d.ts +0 -1
  84. package/dist/daemon/state-dir.d.ts +0 -1
  85. package/dist/daemon/timers.d.ts +0 -1
  86. package/dist/daemon/tls.d.ts +0 -1
  87. package/dist/errors.d.ts +0 -1
  88. package/dist/host-certificate.d.ts +0 -1
  89. package/dist/index.d.ts +0 -1
  90. package/dist/index.js +1307 -3
  91. package/dist/key-material.d.ts +0 -1
  92. package/dist/link.d.ts +8 -7
  93. package/dist/mcp/app-tools.d.ts +153 -0
  94. package/dist/mcp/connect-tool.d.ts +14 -8
  95. package/dist/mcp/daemon-tools.d.ts +2 -11
  96. package/dist/mcp/events-tool.d.ts +0 -1
  97. package/dist/mcp/server.d.ts +33 -14
  98. package/dist/native-scheme.d.ts +0 -1
  99. package/dist/output.d.ts +8 -11
  100. package/dist/package-root.d.ts +11 -0
  101. package/dist/package-version.d.ts +0 -1
  102. package/dist/qr-terminal.d.ts +0 -1
  103. package/dist/rpc/client.d.ts +3 -3
  104. package/dist/scheme.d.ts +60 -2
  105. package/dist/spki-pin.d.ts +0 -1
  106. package/package.json +6 -3
  107. package/dist/artifact-inspect.d.ts.map +0 -1
  108. package/dist/artifact-inspect.js +0 -334
  109. package/dist/bin.d.ts.map +0 -1
  110. package/dist/cli/command-options.d.ts.map +0 -1
  111. package/dist/cli/command-options.js +0 -95
  112. package/dist/cli/create-cli.d.ts.map +0 -1
  113. package/dist/cli/create-cli.js +0 -56
  114. package/dist/cli/dispatch.d.ts.map +0 -1
  115. package/dist/cli/dispatch.js +0 -275
  116. package/dist/cli/open-target.d.ts.map +0 -1
  117. package/dist/cli/open-target.js +0 -438
  118. package/dist/cli/result-types.d.ts.map +0 -1
  119. package/dist/cli/result-types.js +0 -6
  120. package/dist/cli/runner.d.ts.map +0 -1
  121. package/dist/cli/runner.js +0 -111
  122. package/dist/cli/types.d.ts.map +0 -1
  123. package/dist/cli/types.js +0 -10
  124. package/dist/cli.d.ts.map +0 -1
  125. package/dist/cli.js +0 -2
  126. package/dist/client/app-client.d.ts.map +0 -1
  127. package/dist/client/app-client.js +0 -223
  128. package/dist/client/bootstrap.d.ts.map +0 -1
  129. package/dist/client/bootstrap.js +0 -108
  130. package/dist/client/connect.d.ts.map +0 -1
  131. package/dist/client/connect.js +0 -38
  132. package/dist/client/errors.d.ts.map +0 -1
  133. package/dist/client/errors.js +0 -40
  134. package/dist/client/index.d.ts.map +0 -1
  135. package/dist/commands/daemon.d.ts +0 -22
  136. package/dist/commands/daemon.d.ts.map +0 -1
  137. package/dist/commands/daemon.js +0 -128
  138. package/dist/commands/doctor.d.ts.map +0 -1
  139. package/dist/commands/doctor.js +0 -42
  140. package/dist/commands/events.d.ts.map +0 -1
  141. package/dist/commands/events.js +0 -76
  142. package/dist/commands/init.d.ts.map +0 -1
  143. package/dist/commands/init.js +0 -231
  144. package/dist/commands/invoke.d.ts.map +0 -1
  145. package/dist/commands/invoke.js +0 -54
  146. package/dist/commands/keygen.d.ts.map +0 -1
  147. package/dist/commands/keygen.js +0 -46
  148. package/dist/commands/link.d.ts.map +0 -1
  149. package/dist/commands/link.js +0 -43
  150. package/dist/commands/ls.d.ts.map +0 -1
  151. package/dist/commands/ls.js +0 -7
  152. package/dist/commands/mcp.d.ts.map +0 -1
  153. package/dist/commands/mcp.js +0 -98
  154. package/dist/commands/revoke.d.ts.map +0 -1
  155. package/dist/commands/revoke.js +0 -7
  156. package/dist/commands/tools.d.ts.map +0 -1
  157. package/dist/commands/tools.js +0 -52
  158. package/dist/daemon/address.d.ts.map +0 -1
  159. package/dist/daemon/address.js +0 -71
  160. package/dist/daemon/audit.d.ts.map +0 -1
  161. package/dist/daemon/audit.js +0 -237
  162. package/dist/daemon/calls.d.ts.map +0 -1
  163. package/dist/daemon/calls.js +0 -158
  164. package/dist/daemon/config.d.ts.map +0 -1
  165. package/dist/daemon/config.js +0 -170
  166. package/dist/daemon/daemon.d.ts.map +0 -1
  167. package/dist/daemon/daemon.js +0 -560
  168. package/dist/daemon/event-bus.d.ts.map +0 -1
  169. package/dist/daemon/event-bus.js +0 -111
  170. package/dist/daemon/links.d.ts.map +0 -1
  171. package/dist/daemon/links.js +0 -118
  172. package/dist/daemon/listener.d.ts.map +0 -1
  173. package/dist/daemon/listener.js +0 -132
  174. package/dist/daemon/log-rotation.d.ts.map +0 -1
  175. package/dist/daemon/log-rotation.js +0 -136
  176. package/dist/daemon/pidfile.d.ts.map +0 -1
  177. package/dist/daemon/pidfile.js +0 -90
  178. package/dist/daemon/policy.d.ts.map +0 -1
  179. package/dist/daemon/policy.js +0 -24
  180. package/dist/daemon/registry.d.ts.map +0 -1
  181. package/dist/daemon/registry.js +0 -31
  182. package/dist/daemon/rpc-server.d.ts.map +0 -1
  183. package/dist/daemon/rpc-server.js +0 -203
  184. package/dist/daemon/sessions.d.ts.map +0 -1
  185. package/dist/daemon/sessions.js +0 -429
  186. package/dist/daemon/socket-probe.d.ts.map +0 -1
  187. package/dist/daemon/socket-probe.js +0 -28
  188. package/dist/daemon/state-dir.d.ts.map +0 -1
  189. package/dist/daemon/state-dir.js +0 -43
  190. package/dist/daemon/timers.d.ts.map +0 -1
  191. package/dist/daemon/timers.js +0 -12
  192. package/dist/daemon/tls.d.ts.map +0 -1
  193. package/dist/daemon/tls.js +0 -79
  194. package/dist/errors.d.ts.map +0 -1
  195. package/dist/errors.js +0 -162
  196. package/dist/host-certificate.d.ts.map +0 -1
  197. package/dist/host-certificate.js +0 -111
  198. package/dist/index.d.ts.map +0 -1
  199. package/dist/key-material.d.ts.map +0 -1
  200. package/dist/key-material.js +0 -42
  201. package/dist/link.d.ts.map +0 -1
  202. package/dist/link.js +0 -117
  203. package/dist/mcp/connect-tool.d.ts.map +0 -1
  204. package/dist/mcp/connect-tool.js +0 -396
  205. package/dist/mcp/daemon-tools.d.ts.map +0 -1
  206. package/dist/mcp/daemon-tools.js +0 -29
  207. package/dist/mcp/events-tool.d.ts.map +0 -1
  208. package/dist/mcp/events-tool.js +0 -322
  209. package/dist/mcp/server.d.ts.map +0 -1
  210. package/dist/mcp/server.js +0 -538
  211. package/dist/mcp/tool-mapping.d.ts +0 -58
  212. package/dist/mcp/tool-mapping.d.ts.map +0 -1
  213. package/dist/mcp/tool-mapping.js +0 -143
  214. package/dist/mcp/tool-namespace.d.ts +0 -40
  215. package/dist/mcp/tool-namespace.d.ts.map +0 -1
  216. package/dist/mcp/tool-namespace.js +0 -65
  217. package/dist/native-scheme.d.ts.map +0 -1
  218. package/dist/native-scheme.js +0 -465
  219. package/dist/output.d.ts.map +0 -1
  220. package/dist/output.js +0 -342
  221. package/dist/package-version.d.ts.map +0 -1
  222. package/dist/package-version.js +0 -47
  223. package/dist/qr-terminal.d.ts.map +0 -1
  224. package/dist/qr-terminal.js +0 -45
  225. package/dist/rpc/client.d.ts.map +0 -1
  226. package/dist/rpc/client.js +0 -819
  227. package/dist/scheme.d.ts.map +0 -1
  228. package/dist/scheme.js +0 -321
  229. package/dist/spki-pin.d.ts.map +0 -1
  230. package/dist/spki-pin.js +0 -14
@@ -1,52 +0,0 @@
1
- /**
2
- * `appduct tools` (ARCHITECTURE.md §10): `tools [selector] [--full]` lists tools for a session;
3
- * `tools [selector] <name>` shows one tool's full schema/annotations.
4
- *
5
- * The command table gives both forms a leading optional `[selector]`, which makes a single
6
- * positional argument inherently ambiguous (is it the selector, or the tool name in `tools <name>`
7
- * with the selector omitted?). This resolves it the same way a human reading the table would:
8
- * first try the arg as a tool name in the implicit-selector session's registry; if no such tool
9
- * exists there (or the implicit selector doesn't resolve, e.g. `ambiguous_session`), fall back to
10
- * treating it as a selector and list that session's tools instead.
11
- */
12
- import { RPC_METHODS } from "@appduct/shared";
13
- import { usageError } from "../errors.js";
14
- import { callDaemon, DaemonRpcError } from "../rpc/client.js";
15
- const listTools = (selector, context) => {
16
- return callDaemon(RPC_METHODS.toolsList, { selector }, { stateDir: context.stateDir, spawn: context.spawn });
17
- };
18
- const findTool = (tools, name) => {
19
- return tools.find((tool) => tool.name === name);
20
- };
21
- export const handleToolsCommand = async (options, context) => {
22
- if (options.selector !== undefined && options.name !== undefined) {
23
- const tools = await listTools(options.selector, context);
24
- const tool = findTool(tools, options.name);
25
- if (!tool) {
26
- throw usageError(`Tool "${options.name}" is not registered on session "${options.selector}".`, { available: tools.map((entry) => entry.name) });
27
- }
28
- return { ok: true, data: tool };
29
- }
30
- if (options.selector !== undefined) {
31
- // A single positional arg: try it as the implicit session's tool name first.
32
- let implicitTools;
33
- try {
34
- implicitTools = await listTools(undefined, context);
35
- }
36
- catch (error) {
37
- if (!(error instanceof DaemonRpcError)) {
38
- throw error;
39
- }
40
- }
41
- if (implicitTools) {
42
- const tool = findTool(implicitTools, options.selector);
43
- if (tool) {
44
- return { ok: true, data: tool };
45
- }
46
- }
47
- // Not a tool name on the implicit session (or there is no implicit session): treat the arg as
48
- // a selector and list that session's tools instead.
49
- return { ok: true, data: await listTools(options.selector, context) };
50
- }
51
- return { ok: true, data: await listTools(undefined, context) };
52
- };
@@ -1 +0,0 @@
1
- {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../../src/daemon/address.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,4BAA4B,GAAI,SAAS,MAAM,KAAG,OAE9D,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,OAWpD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,SAAS,MAAM,KAAG,OAEvD,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAS,8BAAmC,KAC3C,iBA+CF,CAAC"}
@@ -1,71 +0,0 @@
1
- /**
2
- * Advertised-address detection (ARCHITECTURE.md §2, §8 bootstrap endpoint). Prefers a
3
- * non-internal private IPv4 address, falling back to IPv6, then loopback. Fixes v1's
4
- * `detectHostIp`, which happily returned a Docker bridge address (`172.17.x.x`, inside the RFC
5
- * 1918 172.16/12 block v1 treated as "private") — that address is unreachable from a physical
6
- * device and made the printed deep link useless. Also excludes the IPv4 CGNAT range
7
- * (`100.64.0.0/10`), which is routable-looking but not device-reachable in the common case.
8
- */
9
- import { networkInterfaces } from "node:os";
10
- import { isLinkLocalIpv6Address, isPrivateIpv4Address, isUniqueLocalIpv6Address, } from "@appduct/shared";
11
- /** Docker's default bridge network (`docker0`), the exact defect v1 shipped with. */
12
- export const isDockerDefaultBridgeAddress = (address) => {
13
- return address.startsWith("172.17.");
14
- };
15
- /** Carrier-grade NAT shared address space (RFC 6598): `100.64.0.0/10`. */
16
- export const isCgnatIpv4Address = (address) => {
17
- const parts = address.split(".");
18
- if (parts.length !== 4) {
19
- return false;
20
- }
21
- const first = Number(parts[0]);
22
- const second = Number(parts[1]);
23
- return first === 100 && Number.isInteger(second) && second >= 64 && second <= 127;
24
- };
25
- export const isExcludedIpv4Address = (address) => {
26
- return isDockerDefaultBridgeAddress(address) || isCgnatIpv4Address(address);
27
- };
28
- /**
29
- * Enumerates non-internal interface addresses and picks the best advertisable candidate: a
30
- * private (RFC 1918) IPv4 address first (excluding Docker/CGNAT), then any other non-internal
31
- * IPv4, then a unique-local (`fd00::/8`) IPv6 address, then any other non-internal, non-link-local
32
- * IPv6 address, finally falling back to loopback if nothing else is available.
33
- */
34
- export const detectAdvertisedAddress = (options = {}) => {
35
- if (options.override) {
36
- return {
37
- family: options.override.includes(":") ? 6 : 4,
38
- address: options.override,
39
- };
40
- }
41
- const ipv4Candidates = [];
42
- const ipv6Candidates = [];
43
- for (const entries of Object.values(networkInterfaces())) {
44
- for (const entry of entries ?? []) {
45
- if (entry.internal) {
46
- continue;
47
- }
48
- if (entry.family === "IPv4") {
49
- if (isExcludedIpv4Address(entry.address)) {
50
- continue;
51
- }
52
- ipv4Candidates.push(entry.address);
53
- }
54
- else if (entry.family === "IPv6") {
55
- if (isLinkLocalIpv6Address(entry.address)) {
56
- continue;
57
- }
58
- ipv6Candidates.push(entry.address);
59
- }
60
- }
61
- }
62
- const preferredIpv4 = ipv4Candidates.find((address) => isPrivateIpv4Address(address)) ?? ipv4Candidates[0];
63
- if (preferredIpv4) {
64
- return { family: 4, address: preferredIpv4 };
65
- }
66
- const preferredIpv6 = ipv6Candidates.find((address) => isUniqueLocalIpv6Address(address)) ?? ipv6Candidates[0];
67
- if (preferredIpv6) {
68
- return { family: 6, address: preferredIpv6 };
69
- }
70
- return { family: 4, address: "127.0.0.1" };
71
- };
@@ -1 +0,0 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/daemon/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAG7C,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AACnE,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG;IACxB,gBAAgB;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,oBAAoB,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;CACpC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,sGAAsG;IACtG,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB;uFACmF;IACnF,MAAM,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC;IACjD,2EAA2E;IAC3E,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,2EAA2E;IAC3E,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B;;;;OAIG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvC;sCACkC;IAClC,KAAK,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACjC,uFAAuF;IACvF,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;+CAC2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8FAA8F;IAC9F,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC,CAAC;AAiBF,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAEjE,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAE1D,CAAC;AA8DF,eAAO,MAAM,iBAAiB,GAAI,SAAS,kBAAkB,KAAG,WA4K/D,CAAC"}
@@ -1,237 +0,0 @@
1
- /**
2
- * Append-only per-day audit log (ARCHITECTURE.md §3, §12): one JSONL line per `tools.call`
3
- * attempt (CLI and MCP alike), regardless of outcome. Raw args are **never** written — only a hex
4
- * sha256 digest of their canonical JSON, so an operator can correlate repeated/identical calls
5
- * without the log becoming a second copy of whatever secrets a tool's arguments carried.
6
- *
7
- * Writes are serialized through an internal promise queue so a slow disk never blocks the
8
- * `tools.call` response path: {@link AuditLogger.record} enqueues and returns immediately. A write
9
- * failure never throws back into the call path — it is logged to stderr and counted in
10
- * {@link AuditLogger.failedWrites} (surfaced via `daemon.status`, ARCHITECTURE.md §12 item 4).
11
- * {@link AuditLogger.flush} lets the daemon's shutdown teardown wait for the queue to drain.
12
- *
13
- * Retention (ARCHITECTURE.md §3): day files older than `config.auditRetentionDays` are deleted by
14
- * {@link AuditLogger.prune}, which the daemon runs at startup and once a day thereafter. Pruning
15
- * runs on the same queue as the writes, so it can never race an in-flight append, and its failures
16
- * are counted in {@link AuditLogger.failedPrunes} and warned rather than thrown — exactly like a
17
- * failed write.
18
- */
19
- import { createHash } from "node:crypto";
20
- import { chmod, mkdir, open, readdir, rm, stat } from "node:fs/promises";
21
- import { join } from "node:path";
22
- import { DEFAULT_AUDIT_RETENTION_DAYS } from "./config.js";
23
- /** Recursively sorts object keys so semantically-identical args always hash identically,
24
- * regardless of the property insertion order the caller happened to use. */
25
- const canonicalize = (value) => {
26
- if (Array.isArray(value)) {
27
- return value.map(canonicalize);
28
- }
29
- if (value !== null && typeof value === "object") {
30
- const record = value;
31
- return Object.fromEntries(Object.keys(record).sort().map((key) => [key, canonicalize(record[key])]));
32
- }
33
- return value;
34
- };
35
- export const canonicalArgsJson = (args) => {
36
- return JSON.stringify(canonicalize(args));
37
- };
38
- export const argsSha256 = (args) => {
39
- return createHash("sha256").update(canonicalArgsJson(args)).digest("hex");
40
- };
41
- /** The UTC day stamp a record written at `date` lands in. UTC (not local time) because that is
42
- * exactly what the file names have always been derived from — see {@link auditFilePathForDate}. */
43
- const dayStamp = (date) => {
44
- return date.toISOString().slice(0, 10); // YYYY-MM-DD
45
- };
46
- /** Matches only the day files this module writes. Anything else in `audit/` — an operator's
47
- * notes, a partial copy, a subdirectory — is never a pruning candidate. */
48
- const AUDIT_DAY_FILE = /^(\d{4}-\d{2}-\d{2})\.jsonl$/u;
49
- /** The captured day stamp when `name` is one of our day files *and* the stamp is a real calendar
50
- * date (so a plausible-looking `2026-13-45.jsonl` is left alone rather than compared as a string). */
51
- const dayStampOfAuditFile = (name) => {
52
- const match = AUDIT_DAY_FILE.exec(name);
53
- if (!match) {
54
- return undefined;
55
- }
56
- const stamp = match[1];
57
- const parsed = new Date(`${stamp}T00:00:00.000Z`);
58
- return Number.isNaN(parsed.getTime()) || dayStamp(parsed) !== stamp ? undefined : stamp;
59
- };
60
- const MS_PER_DAY = 24 * 60 * 60 * 1000;
61
- /**
62
- * The oldest day stamp retention keeps: `retentionDays` UTC days before the day containing `now`.
63
- * A day file is pruned when its stamp sorts strictly before this (ISO stamps are zero-padded, so
64
- * lexicographic order is chronological order), i.e. once it is *more* than `retentionDays` days
65
- * behind today. Today's file therefore can never be a candidate for any `retentionDays >= 1`.
66
- */
67
- const oldestRetainedStamp = (now, retentionDays) => {
68
- const todayUtcMidnight = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
69
- const cutoff = new Date(todayUtcMidnight - retentionDays * MS_PER_DAY);
70
- // `auditRetentionDays` is validated as a positive integer, not a *small* one: a window wide
71
- // enough to run off the end of the `Date` range would make `toISOString` throw. That config
72
- // means "keep everything", so answer with a stamp no real day file can sort before.
73
- return Number.isNaN(cutoff.getTime()) ? "0000-00-00" : dayStamp(cutoff);
74
- };
75
- const auditFilePathForDate = (auditDir, date) => {
76
- return join(auditDir, `${dayStamp(date)}.jsonl`);
77
- };
78
- const appendLine = async (filePath, line) => {
79
- const handle = await open(filePath, "a", 0o600);
80
- try {
81
- await handle.appendFile(line);
82
- // `open`'s mode only applies at creation and is further subject to umask; chmod explicitly so
83
- // an existing (or umask-widened) file always ends up at the ARCHITECTURE.md §3 mode.
84
- await chmod(filePath, 0o600);
85
- }
86
- finally {
87
- await handle.close();
88
- }
89
- };
90
- export const createAuditLogger = (options) => {
91
- const clock = options.clock ?? { now: () => new Date() };
92
- const warn = options.warn ??
93
- ((message) => {
94
- process.stderr.write(`${message}\n`);
95
- });
96
- const retentionDays = options.retentionDays ?? DEFAULT_AUDIT_RETENTION_DAYS;
97
- let failedWrites = 0;
98
- let failedPrunes = 0;
99
- let queue = Promise.resolve();
100
- /**
101
- * Lists the day files in the audit directory. A *missing* directory is genuinely empty — the
102
- * daemon creates it lazily — and reports `failed: false`. Any other error (EACCES on a
103
- * tightened-up directory, ENOTDIR where a file now sits, EIO on a failing disk) is a refusal to
104
- * answer, not an answer of zero, and is reported as such: callers must not turn "we could not
105
- * look" into "there is nothing there".
106
- */
107
- const listDayFiles = async () => {
108
- let entries;
109
- try {
110
- entries = await readdir(options.auditDir, { withFileTypes: true });
111
- }
112
- catch (error) {
113
- if (error.code === "ENOENT") {
114
- return { files: [], failed: false };
115
- }
116
- return { files: [], failed: true, error: error };
117
- }
118
- const files = [];
119
- for (const entry of entries) {
120
- if (!entry.isFile()) {
121
- continue;
122
- }
123
- const stamp = dayStampOfAuditFile(entry.name);
124
- if (stamp !== undefined) {
125
- files.push({ name: entry.name, stamp });
126
- }
127
- }
128
- return { files, failed: false };
129
- };
130
- const pruneOnce = async () => {
131
- const today = dayStamp(clock.now());
132
- const cutoff = oldestRetainedStamp(clock.now(), retentionDays);
133
- let deleted = 0;
134
- let failed = 0;
135
- const listing = await listDayFiles();
136
- if (listing.failed) {
137
- // A sweep that could not read the directory did not prune anything and cannot know whether
138
- // it needed to — counted and warned like any other prune failure, so a permanently
139
- // unreadable `audit/` shows up in `daemon.status` instead of looking like a tidy one.
140
- failedPrunes += 1;
141
- warn(`appduct daemon: failed to read the audit directory to prune it: ${listing.error?.message}`);
142
- return { deleted: 0, failed: 1 };
143
- }
144
- for (const file of listing.files) {
145
- // Belt and braces: `cutoff` is already strictly behind `today` for every valid
146
- // `retentionDays`, but today's file is the one file that must never disappear from under an
147
- // open append, so it is excluded explicitly (issue #32).
148
- if (file.stamp === today || file.stamp >= cutoff) {
149
- continue;
150
- }
151
- try {
152
- await rm(join(options.auditDir, file.name), { force: true });
153
- deleted += 1;
154
- }
155
- catch (error) {
156
- failed += 1;
157
- failedPrunes += 1;
158
- warn(`appduct daemon: failed to prune the audit record "${file.name}": ${error.message}`);
159
- }
160
- }
161
- return { deleted, failed };
162
- };
163
- const record = (entry) => {
164
- const now = clock.now();
165
- const full = { ts: now.toISOString(), ...entry };
166
- const line = `${JSON.stringify(full)}\n`;
167
- const filePath = auditFilePathForDate(options.auditDir, now);
168
- // Chained onto the existing queue tail so writes for the same (or different) day never race
169
- // each other; a failure is swallowed here (never rejects the chain) so one bad write can't
170
- // poison every audit record after it.
171
- queue = queue.then(async () => {
172
- try {
173
- await mkdir(options.auditDir, { recursive: true });
174
- await appendLine(filePath, line);
175
- }
176
- catch (error) {
177
- failedWrites += 1;
178
- warn(`appduct daemon: failed to write an audit record: ${error.message}`);
179
- }
180
- });
181
- };
182
- /**
183
- * Chained onto the write queue for the same reason the writes are chained onto each other: a
184
- * prune must never interleave with the append it would otherwise delete out from under. The
185
- * returned promise resolves with this prune's own result while the queue itself stays a
186
- * never-rejecting `Promise<void>` that `flush` can keep awaiting.
187
- */
188
- const prune = () => {
189
- let settle;
190
- const result = new Promise((resolve) => {
191
- settle = resolve;
192
- });
193
- queue = queue.then(async () => {
194
- try {
195
- settle(await pruneOnce());
196
- }
197
- catch (error) {
198
- // `pruneOnce` handles per-file failures itself; this only catches a failure of the listing
199
- // step, which must still never reject the queue.
200
- failedPrunes += 1;
201
- warn(`appduct daemon: failed to prune the audit log: ${error.message}`);
202
- settle({ deleted: 0, failed: 1 });
203
- }
204
- });
205
- return result;
206
- };
207
- const stats = async () => {
208
- const listing = await listDayFiles();
209
- if (listing.failed) {
210
- // Reported as "not measured", never as an empty directory: `daemon.status` would otherwise
211
- // tell an operator that `audit/` holds nothing at exactly the moment it has become
212
- // unreadable — the moment they most need to know otherwise.
213
- return {};
214
- }
215
- let files = 0;
216
- let bytes = 0;
217
- for (const file of listing.files) {
218
- try {
219
- bytes += (await stat(join(options.auditDir, file.name))).size;
220
- files += 1;
221
- }
222
- catch {
223
- // Raced with a prune (ours or an operator's `rm`); an audit record that is gone by the
224
- // time it is measured simply does not count toward the reported footprint.
225
- }
226
- }
227
- return { files, bytes };
228
- };
229
- return {
230
- record,
231
- failedWrites: () => failedWrites,
232
- failedPrunes: () => failedPrunes,
233
- prune,
234
- stats,
235
- flush: () => queue,
236
- };
237
- };
@@ -1 +0,0 @@
1
- {"version":3,"file":"calls.d.ts","sourceRoot":"","sources":["../../src/daemon/calls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAgB,KAAK,QAAQ,EAAoB,MAAM,aAAa,CAAC;AAE5E;kFACkF;AAClF,eAAO,MAAM,uBAAuB,QAA0B,CAAC;AAC/D,eAAO,MAAM,mBAAmB,OAAsB,CAAC;AACvD,eAAO,MAAM,mBAAmB,SAAsB,CAAC;AAEvD;iFACiF;AACjF,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;oGAEoG;AACpG,MAAM,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC;AAEpF,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC;AAExF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF;;wEAEwE;AACxE,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB;;;;uCAImC;IACnC,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,WAAW,CAAC;IAC7G,mFAAmF;IACnF,gBAAgB,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACvD,kFAAkF;IAClF,eAAe,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACrD;wCACoC;IACpC,sBAAsB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACnE;;;;sEAIkE;IAClE,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IACvE,gGAAgG;IAChG,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,GAAG,iBAAiB,KAAK,IAAI,CAAC;IAC/F,oFAAoF;IACpF,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAWF;;4EAE4E;AAC5E,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAErD,8FAA8F;AAC9F,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,GAAG,SAAS,KAAG,MAM5D,CAAC;AAEF;;kFAEkF;AAClF,eAAO,MAAM,4BAA4B,GAAI,WAAW,MAAM,GAAG,SAAS,KAAG,MAE5E,CAAC;AAYF,eAAO,MAAM,kBAAkB,GAAI,SAAS,mBAAmB,KAAG,YAoJjE,CAAC"}
@@ -1,158 +0,0 @@
1
- /**
2
- * Per-session tool-invocation manager (ARCHITECTURE.md §5 `tools.call`, §7 `tool_call` /
3
- * `tool_result` / `tool_error` / `tool_call_progress`). This is the v2 replacement for v1's
4
- * `pendingCalls` map: the correlation pattern (`call_<random>` ids, resolve on `tool_result`,
5
- * reject on `tool_error`) was sound and is kept. v1's defect was error flattening — the app's
6
- * `error.type` got re-wrapped twice and ended up buried two `details` levels deep. Here the type is
7
- * preserved verbatim: {@link RpcApplicationError.type} is set directly from the app's
8
- * `tool_error.error.type`, never a generic wrapper.
9
- *
10
- * This module knows nothing about sockets or the session state machine — `sessions.ts` (via the
11
- * composition root in `daemon.ts`) supplies a `send` function and routes incoming
12
- * `tool_result`/`tool_error`/`tool_call_progress` frames and suspend/revoke/expiry transitions back
13
- * in. That keeps a single seam (`call`) that a policy layer can wrap.
14
- */
15
- import { randomBytes } from "node:crypto";
16
- import { clampToolTimeoutMs, DEFAULT_TOOL_TIMEOUT_MS, MAX_TOOL_TIMEOUT_MS, MIN_TOOL_TIMEOUT_MS, } from "@appduct/shared";
17
- import { RpcApplicationError } from "./rpc-server.js";
18
- import { systemTimers } from "./timers.js";
19
- /** Re-exported under this module's historical names; the values live in `@appduct/shared` so the
20
- * RN SDK's app-side abort timer and this timer cannot drift apart (issue #25). */
21
- export const DEFAULT_CALL_TIMEOUT_MS = DEFAULT_TOOL_TIMEOUT_MS;
22
- export const MIN_CALL_TIMEOUT_MS = MIN_TOOL_TIMEOUT_MS;
23
- export const MAX_CALL_TIMEOUT_MS = MAX_TOOL_TIMEOUT_MS;
24
- const generateCallId = () => `call_${randomBytes(16).toString("base64url")}`;
25
- /** Extra headroom added on top of the daemon-side deadline so a caller's transport timeout never
26
- * beats the daemon's own `tool_timeout` — mirrored (deliberately duplicated) in
27
- * `client/app-client.ts`, which must not import daemon-internal modules. */
28
- export const CALL_TRANSPORT_TIMEOUT_SLACK_MS = 5_000;
29
- /** The deadline the daemon will actually enforce for a `tools.call` with this `timeoutMs`. */
30
- export const clampTimeout = (timeoutMs) => {
31
- if (timeoutMs === undefined || !Number.isFinite(timeoutMs)) {
32
- return DEFAULT_CALL_TIMEOUT_MS;
33
- }
34
- return clampToolTimeoutMs(timeoutMs);
35
- };
36
- /** Transport timeout a caller should use for a `tools.call` that will be given `timeoutMs`
37
- * daemon-side. Always clamps first: adding slack to an unclamped value could overflow Node's
38
- * 32-bit timer range (~24.8 days), which silently fires the timer immediately. */
39
- export const deriveCallTransportTimeoutMs = (timeoutMs) => {
40
- return clampTimeout(timeoutMs) + CALL_TRANSPORT_TIMEOUT_SLACK_MS;
41
- };
42
- /** Event kinds that terminate every pending call for a session (ARCHITECTURE.md §6/§5: "On
43
- * suspend/revoke/expiry, every pending call rejects immediately with session_suspended (or
44
- * unknown_session on revoke)"). Expiry only ever follows suspend (pending calls are already gone
45
- * by then), but it is handled defensively with the same `unknown_session` type as revoke. */
46
- const TERMINATING_KIND_ERROR_TYPES = {
47
- session_suspended: "session_suspended",
48
- session_revoked: "unknown_session",
49
- session_expired: "unknown_session",
50
- };
51
- export const createCallsManager = (options) => {
52
- const timers = options.timers ?? systemTimers;
53
- // sessionId -> callId -> PendingCall. Concurrent calls to the same session get independent
54
- // entries here, keyed by their own call id, so out-of-order tool_result/tool_error frames still
55
- // resolve the right caller.
56
- const pendingBySession = new Map();
57
- const takePending = (sessionId, callId) => {
58
- const sessionMap = pendingBySession.get(sessionId);
59
- const pending = sessionMap?.get(callId);
60
- if (!sessionMap || !pending) {
61
- return undefined;
62
- }
63
- sessionMap.delete(callId);
64
- if (sessionMap.size === 0) {
65
- pendingBySession.delete(sessionId);
66
- }
67
- timers.clearTimeout(pending.timer);
68
- return pending;
69
- };
70
- const call = (session, name, args, timeoutMs) => {
71
- const callId = generateCallId();
72
- const result = new Promise((resolve, reject) => {
73
- const effectiveTimeoutMs = clampTimeout(timeoutMs);
74
- const timer = timers.setTimeout(() => {
75
- const pending = takePending(session.sessionId, callId);
76
- pending?.reject(new RpcApplicationError("tool_timeout", `Tool call "${name}" timed out after ${effectiveTimeoutMs}ms.`));
77
- }, effectiveTimeoutMs);
78
- let sessionMap = pendingBySession.get(session.sessionId);
79
- if (!sessionMap) {
80
- sessionMap = new Map();
81
- pendingBySession.set(session.sessionId, sessionMap);
82
- }
83
- sessionMap.set(callId, { alias: session.alias, resolve, reject, timer });
84
- const message = {
85
- type: "tool_call",
86
- session_id: session.sessionId,
87
- id: callId,
88
- name,
89
- args,
90
- };
91
- const sent = options.send(session.sessionId, message);
92
- if (!sent) {
93
- const pending = takePending(session.sessionId, callId);
94
- pending?.reject(new RpcApplicationError("session_not_active", "Session has no active socket."));
95
- }
96
- });
97
- return { callId, result };
98
- };
99
- const handleToolResult = (message) => {
100
- const pending = takePending(message.session_id, message.id);
101
- pending?.resolve(message.result);
102
- };
103
- const handleToolError = (message) => {
104
- const pending = takePending(message.session_id, message.id);
105
- // Verbatim: `error.type` comes straight from the app's frame, never re-wrapped (v1's defect).
106
- pending?.reject(new RpcApplicationError(message.error.type, message.error.message, -32000, message.error.details));
107
- };
108
- const handleToolCallProgress = (message) => {
109
- const sessionMap = pendingBySession.get(message.session_id);
110
- const pending = sessionMap?.get(message.id);
111
- if (!pending) {
112
- // Unknown correlation id: dropped (ARCHITECTURE.md task notes say "log at debug level"; a
113
- // full logging seam doesn't exist yet in the daemon, so this is a silent, intentional no-op).
114
- return;
115
- }
116
- options.eventBus.emit({
117
- // Its own EventKind (ARCHITECTURE.md §5), distinct from `tool_call_started`/
118
- // `tool_call_finished` — subscribers correlate all three by `callId` in `data` without
119
- // needing to sniff which kind carries a `progress`/`message` field.
120
- kind: "tool_call_progress",
121
- sessionId: message.session_id,
122
- alias: pending.alias,
123
- data: { callId: message.id, progress: message.progress, message: message.message },
124
- });
125
- };
126
- const cancel = (sessionId, callId, reason) => {
127
- const pending = pendingBySession.get(sessionId)?.get(callId);
128
- if (!pending) {
129
- return false;
130
- }
131
- const message = { type: "tool_cancel", session_id: sessionId, id: callId, reason };
132
- return options.sendCancel(sessionId, message);
133
- };
134
- const rejectSession = (sessionId, errorType) => {
135
- const sessionMap = pendingBySession.get(sessionId);
136
- if (!sessionMap) {
137
- return;
138
- }
139
- pendingBySession.delete(sessionId);
140
- for (const pending of sessionMap.values()) {
141
- timers.clearTimeout(pending.timer);
142
- pending.reject(new RpcApplicationError(errorType, `Session transitioned while the call was pending.`));
143
- }
144
- };
145
- const unsubscribeFromEventBus = options.eventBus.subscribe((event) => {
146
- const errorType = TERMINATING_KIND_ERROR_TYPES[event.kind];
147
- if (errorType && event.sessionId) {
148
- rejectSession(event.sessionId, errorType);
149
- }
150
- });
151
- const disposeAll = () => {
152
- unsubscribeFromEventBus();
153
- for (const sessionId of Array.from(pendingBySession.keys())) {
154
- rejectSession(sessionId, "unknown_session");
155
- }
156
- };
157
- return { call, handleToolResult, handleToolError, handleToolCallProgress, cancel, rejectSession, disposeAll };
158
- };
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/daemon/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,EAAE,cAAc,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB,EAAE,MAAM,CAAC;IACjC;yCACqC;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB;kFAC8E;IAC9E,kBAAkB,EAAE,MAAM,CAAC;IAC3B;kDAC8C;IAC9C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;;;OAKG;IACH,8BAA8B,EAAE,OAAO,CAAC;IACxC,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAsBF,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,4BAA4B,QAAmB,CAAC;AAE7D,qBAAa,kBAAmB,SAAQ,KAAK;IAEzC,QAAQ,CAAC,GAAG,EAAE,MAAM;gBAAX,GAAG,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM;CAKlB;AAsCD,eAAO,MAAM,aAAa,GAAI,OAAO,aAAa,KAAG,aAgBpD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAErD,kGAAkG;AAClG,eAAO,MAAM,UAAU,GACrB,OAAO,aAAa,EACpB,UAAS;IAAE,IAAI,CAAC,EAAE,YAAY,CAAA;CAAO,KACpC,OAAO,CAAC,aAAa,CA4HvB,CAAC"}