acrylblends 0.8.1 → 0.9.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 (235) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +83 -7
  3. package/dist/index.js.map +1 -1
  4. package/lib/audit-BXnYsLge.d.ts +58 -0
  5. package/lib/cell-ChU41DZD.d.ts +32 -0
  6. package/lib/client-modules-CLvWOjbd.js +267 -0
  7. package/lib/connection-ZLEso7XL.d.ts +25 -0
  8. package/lib/conninfo.d.ts +1 -23
  9. package/lib/cordis/db-sqlite.d.ts +56 -0
  10. package/lib/cordis/db-sqlite.js +58 -0
  11. package/lib/cordis/db.d.ts +8 -0
  12. package/lib/cordis/db.js +1 -0
  13. package/lib/cordis/door.d.ts +159 -0
  14. package/lib/cordis/door.js +757 -0
  15. package/lib/cordis/inspect.d.ts +77 -0
  16. package/lib/cordis/inspect.js +149 -0
  17. package/lib/cordis/kv-db.d.ts +16 -0
  18. package/lib/cordis/kv-db.js +18 -0
  19. package/lib/cordis/kv-json.d.ts +16 -0
  20. package/lib/cordis/kv-json.js +44 -0
  21. package/lib/cordis/kv.d.ts +8 -0
  22. package/lib/cordis/kv.js +1 -0
  23. package/lib/cordis/node-server.d.ts +45 -0
  24. package/lib/cordis/node-server.js +93 -0
  25. package/lib/cordis/router-http.d.ts +56 -0
  26. package/lib/cordis/router-http.js +117 -0
  27. package/lib/cordis/router.d.ts +8 -0
  28. package/lib/cordis/router.js +7 -0
  29. package/lib/cordis/state/react.d.ts +6 -0
  30. package/lib/cordis/state/react.js +11 -0
  31. package/lib/cordis/state.d.ts +33 -0
  32. package/lib/cordis/state.js +163 -0
  33. package/lib/core-CaY8XDZ6.js +19 -0
  34. package/lib/database-DBHdwefi.d.ts +44 -0
  35. package/lib/db/conformance.d.ts +11 -0
  36. package/lib/db/conformance.js +267 -0
  37. package/lib/db/sqlite.d.ts +1 -1
  38. package/lib/db/sqlite.js +1 -196
  39. package/lib/db.d.ts +2 -1
  40. package/lib/{src-0hI7M7cN.js → deprecated-BP4fPt38.js} +0 -17
  41. package/lib/door/host.d.ts +24 -0
  42. package/lib/door/host.js +2 -0
  43. package/lib/door/page.d.ts +105 -0
  44. package/lib/door/page.js +1030 -0
  45. package/lib/door/protocol.d.ts +2 -0
  46. package/lib/door/protocol.js +197 -0
  47. package/lib/error-t4N2js2I.d.ts +8 -0
  48. package/lib/etag-DaKMOjty.js +135 -0
  49. package/lib/etag.js +1 -134
  50. package/lib/helpers-CvTauQT5.js +5 -0
  51. package/lib/http-exception-kpGJkmpi.d.ts +57 -0
  52. package/lib/http-exception.d.ts +1 -56
  53. package/lib/{index-BLTYsyc1.d.ts → index-BnGgRKTv.d.ts} +3 -39
  54. package/lib/index-BqVQkTnu.d.ts +98 -0
  55. package/lib/index-Dmst2QCQ.d.ts +44 -0
  56. package/lib/index.d.ts +1 -1
  57. package/lib/index.js +2 -1
  58. package/lib/jwk-LkzeoaU8.js +117 -0
  59. package/lib/jwk.js +1 -116
  60. package/lib/jwt-CskdhEW9.js +115 -0
  61. package/lib/jwt.js +2 -114
  62. package/lib/kv/conformance.d.ts +13 -0
  63. package/lib/kv/conformance.js +301 -0
  64. package/lib/kv/db.d.ts +2 -2
  65. package/lib/kv/json.d.ts +1 -1
  66. package/lib/kv-9f-AlRKN.d.ts +39 -0
  67. package/lib/kv.d.ts +2 -1
  68. package/lib/language-D9a4zqvn.js +198 -0
  69. package/lib/language.js +1 -197
  70. package/lib/middleware-CPQ8himN.js +12 -0
  71. package/lib/node-server/serve-static.d.ts +0 -1
  72. package/lib/node-server/serve-static.js +1 -1
  73. package/lib/node-server.d.ts +1 -7
  74. package/lib/node-server.js +3 -1586
  75. package/lib/open-CMhnJODn.js +197 -0
  76. package/lib/protocol-BizVt4J6.d.ts +144 -0
  77. package/lib/request-id-CKVvHOO1.js +34 -0
  78. package/lib/request-id.js +1 -33
  79. package/lib/secure-headers-mP3j6Hm6.js +163 -0
  80. package/lib/secure-headers.js +1 -162
  81. package/lib/{serve-static-B849HlX2.js → serve-static-3uo_9rjF.js} +1 -4
  82. package/lib/server-DvgMGtzo.js +1323 -0
  83. package/lib/snapshot-text-D3NspDyT.js +338 -0
  84. package/lib/src-CwABFr6r.js +328 -0
  85. package/lib/timeout.d.ts +1 -1
  86. package/lib/timing-Cky7C1Jf.js +183 -0
  87. package/lib/timing.js +1 -182
  88. package/package.json +89 -1
  89. package/plugins/acryl-db/plugin.json +6 -1
  90. package/plugins/acryl-db-sqlite/plugin.json +4 -0
  91. package/plugins/acryl-db-sqlite/src/facade/node.ts +1 -1
  92. package/plugins/acryl-db-sqlite/src/index.ts +2 -2
  93. package/plugins/acryl-db-sqlite/src/open.ts +2 -2
  94. package/plugins/acryl-door/plugin.json +4 -1
  95. package/plugins/acryl-door/src/host/http.ts +2 -2
  96. package/plugins/acryl-door/src/host/mcp-routes.ts +1 -1
  97. package/plugins/acryl-door/src/host/operator-routes.ts +1 -1
  98. package/plugins/acryl-door/src/host/page-routes.ts +3 -3
  99. package/plugins/acryl-door/src/index.ts +2 -2
  100. package/plugins/acryl-inspect/plugin.json +4 -1
  101. package/plugins/acryl-inspect/src/service.ts +2 -2
  102. package/plugins/acryl-kv/plugin.json +6 -1
  103. package/plugins/acryl-kv-db/plugin.json +4 -0
  104. package/plugins/acryl-kv-db/src/index.ts +1 -1
  105. package/plugins/acryl-kv-db/src/open.ts +3 -3
  106. package/plugins/acryl-kv-json/plugin.json +4 -0
  107. package/plugins/acryl-kv-json/src/index.ts +1 -1
  108. package/plugins/acryl-kv-json/src/open.ts +2 -2
  109. package/plugins/acryl-node-server/plugin.json +5 -1
  110. package/plugins/acryl-node-server/src/adapter/conninfo.ts +1 -1
  111. package/plugins/acryl-node-server/src/adapter/early-hints.ts +1 -1
  112. package/plugins/acryl-node-server/src/adapter/serve-static.ts +2 -2
  113. package/plugins/acryl-node-server/src/adapter/websocket.ts +2 -2
  114. package/plugins/acryl-node-server/src/client-modules.ts +2 -2
  115. package/plugins/acryl-node-server/src/index.ts +1 -1
  116. package/plugins/acryl-router/plugin.json +11 -1
  117. package/plugins/acryl-router/src/connection.d.ts +25 -0
  118. package/plugins/acryl-router/src/core/app-base.d.ts +215 -0
  119. package/plugins/acryl-router/src/core/app.d.ts +19 -0
  120. package/plugins/acryl-router/src/core/client/client.d.ts +4 -0
  121. package/plugins/acryl-router/src/core/client/fetch-result-please.d.ts +35 -0
  122. package/plugins/acryl-router/src/core/client/index.d.ts +7 -0
  123. package/plugins/acryl-router/src/core/client/types.d.ts +229 -0
  124. package/plugins/acryl-router/src/core/client/utils.d.ts +18 -0
  125. package/plugins/acryl-router/src/core/compose.d.ts +14 -0
  126. package/plugins/acryl-router/src/core/context.d.ts +443 -0
  127. package/plugins/acryl-router/src/core/helper/accepts/accepts.d.ts +30 -0
  128. package/plugins/acryl-router/src/core/helper/accepts/index.d.ts +5 -0
  129. package/plugins/acryl-router/src/core/helper/conninfo/index.d.ts +5 -0
  130. package/plugins/acryl-router/src/core/helper/conninfo/types.d.ts +36 -0
  131. package/plugins/acryl-router/src/core/helper/cookie/index.d.ts +25 -0
  132. package/plugins/acryl-router/src/core/helper/css/common.d.ts +59 -0
  133. package/plugins/acryl-router/src/core/helper/css/index.d.ts +80 -0
  134. package/plugins/acryl-router/src/core/helper/dev/index.d.ts +20 -0
  135. package/plugins/acryl-router/src/core/helper/factory/index.d.ts +82 -0
  136. package/plugins/acryl-router/src/core/helper/html/index.d.ts +8 -0
  137. package/plugins/acryl-router/src/core/helper/proxy/index.d.ts +73 -0
  138. package/plugins/acryl-router/src/core/helper/route/index.d.ts +75 -0
  139. package/plugins/acryl-router/src/core/helper/ssg/index.d.ts +7 -0
  140. package/plugins/acryl-router/src/core/helper/ssg/middleware.d.ts +44 -0
  141. package/plugins/acryl-router/src/core/helper/ssg/plugins.d.ts +27 -0
  142. package/plugins/acryl-router/src/core/helper/ssg/ssg.d.ts +91 -0
  143. package/plugins/acryl-router/src/core/helper/ssg/utils.d.ts +16 -0
  144. package/plugins/acryl-router/src/core/helper/streaming/index.d.ts +8 -0
  145. package/plugins/acryl-router/src/core/helper/streaming/sse.d.ts +13 -0
  146. package/plugins/acryl-router/src/core/helper/streaming/stream.d.ts +3 -0
  147. package/plugins/acryl-router/src/core/helper/streaming/text.d.ts +3 -0
  148. package/plugins/acryl-router/src/core/helper/streaming/utils.d.ts +1 -0
  149. package/plugins/acryl-router/src/core/helper/testing/index.d.ts +13 -0
  150. package/plugins/acryl-router/src/core/helper/websocket/index.d.ts +70 -0
  151. package/plugins/acryl-router/src/core/http-exception.d.ts +58 -0
  152. package/plugins/acryl-router/src/core/index.d.ts +37 -0
  153. package/plugins/acryl-router/src/core/jsx/base.d.ts +61 -0
  154. package/plugins/acryl-router/src/core/jsx/children.d.ts +9 -0
  155. package/plugins/acryl-router/src/core/jsx/components.d.ts +15 -0
  156. package/plugins/acryl-router/src/core/jsx/constants.d.ts +6 -0
  157. package/plugins/acryl-router/src/core/jsx/context.d.ts +51 -0
  158. package/plugins/acryl-router/src/core/jsx/dom/components.d.ts +10 -0
  159. package/plugins/acryl-router/src/core/jsx/dom/context.d.ts +3 -0
  160. package/plugins/acryl-router/src/core/jsx/dom/css.d.ts +83 -0
  161. package/plugins/acryl-router/src/core/jsx/dom/hooks/index.d.ts +39 -0
  162. package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.d.ts +16 -0
  163. package/plugins/acryl-router/src/core/jsx/dom/jsx-dev-runtime.d.ts +8 -0
  164. package/plugins/acryl-router/src/core/jsx/dom/jsx-runtime.d.ts +7 -0
  165. package/plugins/acryl-router/src/core/jsx/dom/render.d.ts +73 -0
  166. package/plugins/acryl-router/src/core/jsx/dom/utils.d.ts +1 -0
  167. package/plugins/acryl-router/src/core/jsx/hooks/index.d.ts +47 -0
  168. package/plugins/acryl-router/src/core/jsx/index.d.ts +76 -0
  169. package/plugins/acryl-router/src/core/jsx/intrinsic-element/common.d.ts +6 -0
  170. package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.d.ts +13 -0
  171. package/plugins/acryl-router/src/core/jsx/intrinsic-elements.d.ts +735 -0
  172. package/plugins/acryl-router/src/core/jsx/streaming.d.ts +36 -0
  173. package/plugins/acryl-router/src/core/jsx/types.d.ts +36 -0
  174. package/plugins/acryl-router/src/core/jsx/utils.d.ts +4 -0
  175. package/plugins/acryl-router/src/core/request/constants.d.ts +1 -0
  176. package/plugins/acryl-router/src/core/request.d.ts +306 -0
  177. package/plugins/acryl-router/src/core/router/reg-exp-router/index.d.ts +6 -0
  178. package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.d.ts +7 -0
  179. package/plugins/acryl-router/src/core/router/reg-exp-router/node.d.ts +13 -0
  180. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.d.ts +17 -0
  181. package/plugins/acryl-router/src/core/router/reg-exp-router/router.d.ts +11 -0
  182. package/plugins/acryl-router/src/core/router/reg-exp-router/trie.d.ts +8 -0
  183. package/plugins/acryl-router/src/core/router/smart-router/index.d.ts +5 -0
  184. package/plugins/acryl-router/src/core/router/smart-router/router.d.ts +11 -0
  185. package/plugins/acryl-router/src/core/router/trie-router/index.d.ts +5 -0
  186. package/plugins/acryl-router/src/core/router/trie-router/node.d.ts +6 -0
  187. package/plugins/acryl-router/src/core/router/trie-router/router.d.ts +7 -0
  188. package/plugins/acryl-router/src/core/router/utils.d.ts +1 -0
  189. package/plugins/acryl-router/src/core/router.d.ts +97 -0
  190. package/plugins/acryl-router/src/core/types.d.ts +573 -0
  191. package/plugins/acryl-router/src/core/utils/accept.d.ts +6 -0
  192. package/plugins/acryl-router/src/core/utils/body.d.ts +79 -0
  193. package/plugins/acryl-router/src/core/utils/buffer.d.ts +17 -0
  194. package/plugins/acryl-router/src/core/utils/color.d.ts +21 -0
  195. package/plugins/acryl-router/src/core/utils/concurrent.d.ts +11 -0
  196. package/plugins/acryl-router/src/core/utils/constants.d.ts +4 -0
  197. package/plugins/acryl-router/src/core/utils/cookie.d.ts +41 -0
  198. package/plugins/acryl-router/src/core/utils/crypto.d.ts +15 -0
  199. package/plugins/acryl-router/src/core/utils/handler.d.ts +6 -0
  200. package/plugins/acryl-router/src/core/utils/headers.d.ts +8 -0
  201. package/plugins/acryl-router/src/core/utils/html.d.ts +44 -0
  202. package/plugins/acryl-router/src/core/utils/http-status.d.ts +32 -0
  203. package/plugins/acryl-router/src/core/utils/mime.d.ts +70 -0
  204. package/plugins/acryl-router/src/core/utils/stream.d.ts +31 -0
  205. package/plugins/acryl-router/src/core/utils/types.d.ts +128 -0
  206. package/plugins/acryl-router/src/core/utils/url.d.ts +44 -0
  207. package/plugins/acryl-router/src/core/validator/index.d.ts +7 -0
  208. package/plugins/acryl-router/src/core/validator/utils.d.ts +29 -0
  209. package/plugins/acryl-router/src/core/validator/validator.d.ts +16 -0
  210. package/plugins/acryl-router/src/helpers/index.d.ts +17 -0
  211. package/plugins/acryl-router/src/helpers/schema-validator.d.ts +54 -0
  212. package/plugins/acryl-router-http/plugin.json +4 -1
  213. package/plugins/acryl-router-http/src/index.ts +1 -1
  214. package/plugins/acryl-state/plugin.json +5 -1
  215. package/plugins/acryl-state/src/service.ts +3 -3
  216. package/plugins/tsconfig.json +32 -20
  217. package/docs/framework/UPSTREAM.md +0 -3
  218. package/docs/framework/upstream.json +0 -9
  219. package/lib/index-D4FAsBZu.d.ts +0 -85
  220. package/plugins/acryl-db/upstream.json +0 -18
  221. package/plugins/acryl-db-sqlite/upstream.json +0 -21
  222. package/plugins/acryl-door/upstream.json +0 -28
  223. package/plugins/acryl-inspect/upstream.json +0 -18
  224. package/plugins/acryl-kv/upstream.json +0 -18
  225. package/plugins/acryl-kv-db/upstream.json +0 -21
  226. package/plugins/acryl-kv-json/upstream.json +0 -22
  227. package/plugins/acryl-node-server/upstream.json +0 -21
  228. package/plugins/acryl-router/upstream.json +0 -20
  229. package/plugins/acryl-router-http/upstream.json +0 -22
  230. package/plugins/acryl-state/NOTICE +0 -30
  231. package/plugins/acryl-state/upstream.json +0 -22
  232. package/plugins/desktop/upstream.json +0 -12
  233. package/plugins/desktop-web/upstream.json +0 -12
  234. package/templates/todo/upstream.json +0 -12
  235. /package/lib/{index-CTy4N7ng.d.ts → deprecated-CTy4N7ng.d.ts} +0 -0
@@ -0,0 +1,757 @@
1
+ import { t as Acryl } from "../app-BM0rTCp7.js";
2
+ import { n as clientModules } from "../client-modules-CLvWOjbd.js";
3
+ import { MAX_SNAPSHOT_NODES, MAX_WAIT_MS, UiControlError, parseUiRequest } from "../door/protocol.js";
4
+ import { a as INVALID_JSON, c as operatorOnly, d as AuditLog, f as auditPath, i as DOOR_PREFIX, l as privateApi, o as error, p as UiChannel, r as pageChannelRoutes, s as jsonBody, t as renderSnapshot, u as readJson } from "../snapshot-text-D3NspDyT.js";
5
+ import "../src-CwABFr6r.js";
6
+ import { isAbsolute, join, relative, resolve } from "node:path";
7
+ import { randomBytes } from "node:crypto";
8
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
9
+ import z from "@deepseek-ai/schemastery";
10
+ //#region ../../plugins/acryl-door/src/host/verbs.ts
11
+ /**
12
+ * The one verb list (spec 049 slice 13). Every face of the door reads it: the operator client (`acryl <verb>`, its `verbs` and `brief`),
13
+ * MCP (`tools/list`) and the built-in chat (the `tools` registry). A verb added here is in all three, or the comparison test fails.
14
+ */
15
+ /** Every verb, in the order `brief` lists them. */
16
+ const VERBS = [
17
+ "brief",
18
+ "look",
19
+ "act",
20
+ "plugin-list",
21
+ "plugin-add",
22
+ "plugin-remove",
23
+ "plugin-reload",
24
+ "tool-list",
25
+ "tool-call"
26
+ ];
27
+ const none = {
28
+ type: "object",
29
+ properties: {},
30
+ additionalProperties: false
31
+ };
32
+ const VERB_SPECS = {
33
+ "brief": {
34
+ usage: "brief: this summary",
35
+ description: "What this app is, which files to read first, whether its page is open, and the verbs.",
36
+ arguments: none
37
+ },
38
+ "look": {
39
+ usage: "look [--cursor <n>] [--max-nodes <n>]: the page as text, with refs",
40
+ description: "The open page as text: its roles, names, values and visible text, each with a ref to act on. A long page comes in parts (cursor).",
41
+ arguments: {
42
+ type: "object",
43
+ properties: {
44
+ cursor: {
45
+ type: "integer",
46
+ minimum: 0,
47
+ description: "where the previous part ended"
48
+ },
49
+ maxNodes: {
50
+ type: "integer",
51
+ minimum: 1,
52
+ maximum: MAX_SNAPSHOT_NODES
53
+ }
54
+ },
55
+ additionalProperties: false
56
+ }
57
+ },
58
+ "act": {
59
+ usage: "act click|type|select|press|scroll|wait ...: do one thing on the page (refs from the latest look)",
60
+ description: "Do one thing on the page: click, type, select, press a key, scroll, or wait for text or a control. Look again before the next act.",
61
+ arguments: {
62
+ type: "object",
63
+ properties: {
64
+ op: {
65
+ type: "string",
66
+ enum: [
67
+ "click",
68
+ "type",
69
+ "select",
70
+ "press",
71
+ "scroll",
72
+ "wait"
73
+ ]
74
+ },
75
+ ref: {
76
+ type: "string",
77
+ description: "a ref from the latest look, e.g. 3.12"
78
+ },
79
+ text: {
80
+ type: "string",
81
+ description: "type: what to type; wait: the text to wait for"
82
+ },
83
+ submit: {
84
+ type: "boolean",
85
+ description: "type: press Enter after"
86
+ },
87
+ clear: {
88
+ type: "boolean",
89
+ description: "type: false keeps what the field holds"
90
+ },
91
+ option: {
92
+ type: "string",
93
+ description: "select: the option to choose"
94
+ },
95
+ key: {
96
+ type: "string",
97
+ description: "press: a character or Enter, Escape, Tab, ArrowDown, ..."
98
+ },
99
+ direction: {
100
+ type: "string",
101
+ enum: [
102
+ "up",
103
+ "down",
104
+ "left",
105
+ "right"
106
+ ]
107
+ },
108
+ amount: {
109
+ type: "number",
110
+ minimum: 1,
111
+ maximum: 1e4,
112
+ description: "scroll: pixels"
113
+ },
114
+ role: {
115
+ type: "string",
116
+ description: "wait: the role to wait for"
117
+ },
118
+ name: {
119
+ type: "string",
120
+ description: "wait: its name"
121
+ },
122
+ gone: {
123
+ type: "boolean",
124
+ description: "wait: until it is gone instead"
125
+ },
126
+ timeoutMs: {
127
+ type: "integer",
128
+ minimum: 0,
129
+ maximum: MAX_WAIT_MS
130
+ }
131
+ },
132
+ required: ["op"],
133
+ additionalProperties: false
134
+ }
135
+ },
136
+ "plugin-list": {
137
+ usage: "plugin list: the plugins installed while the app runs",
138
+ description: "The plugins installed into the running app.",
139
+ arguments: none
140
+ },
141
+ "plugin-add": {
142
+ usage: "plugin add <folder>: check, install and start a plugin folder, live",
143
+ description: "Check, install and start a plugin folder inside the app, without a restart.",
144
+ arguments: {
145
+ type: "object",
146
+ properties: { folder: {
147
+ type: "string",
148
+ description: "a folder inside the app, e.g. plugins/pomodoro"
149
+ } },
150
+ required: ["folder"],
151
+ additionalProperties: false
152
+ }
153
+ },
154
+ "plugin-remove": {
155
+ usage: "plugin remove <name>: stop and remove an installed plugin",
156
+ description: "Stop and remove an installed plugin.",
157
+ arguments: {
158
+ type: "object",
159
+ properties: { name: { type: "string" } },
160
+ required: ["name"],
161
+ additionalProperties: false
162
+ }
163
+ },
164
+ "plugin-reload": {
165
+ usage: "plugin reload: bring every installed plugin in line with its folder",
166
+ description: "Bring every installed plugin in line with its folder (after editing one).",
167
+ arguments: none
168
+ },
169
+ "tool-list": {
170
+ usage: "tool list: the app tools the door offers",
171
+ description: "The app's own tools that the door offers.",
172
+ arguments: none
173
+ },
174
+ "tool-call": {
175
+ usage: "tool call --tool <name> [--args <json>]: run one of them",
176
+ description: "Run one of the app tools that tool-list names.",
177
+ arguments: {
178
+ type: "object",
179
+ properties: {
180
+ tool: { type: "string" },
181
+ args: { description: "the tool's input" }
182
+ },
183
+ required: ["tool"],
184
+ additionalProperties: false
185
+ }
186
+ }
187
+ };
188
+ /** A verb's name in the `tools` registry the built-in chat reads: prefixed, so it never meets an app tool's name. */
189
+ const chatToolName = (verb) => `door_${verb.replaceAll("-", "_")}`;
190
+ //#endregion
191
+ //#region ../../plugins/acryl-door/src/host/door.ts
192
+ /**
193
+ * The door's verbs, one implementation for every face: `brief`, `look`, `act`, `plugin list|add|remove|reload` and `tool list|call`
194
+ * (`verbs.ts` lists them; the operator client, MCP and the built-in chat all call these). Each verb takes plain JSON arguments,
195
+ * returns a typed value and a text rendering, and fails with a {@link DoorError} or a {@link UiControlError} carrying a code.
196
+ */
197
+ var DoorError = class extends Error {
198
+ code;
199
+ constructor(code, message) {
200
+ super(message);
201
+ this.code = code;
202
+ this.name = "DoorError";
203
+ }
204
+ };
205
+ /** The files an agent should read first, when the app has them. */
206
+ const SELF_KNOWLEDGE = [
207
+ "AGENTS.md",
208
+ "CLAUDE.md",
209
+ ".agents/skills",
210
+ ".claude/skills",
211
+ "docs",
212
+ "plugins"
213
+ ];
214
+ var Door = class {
215
+ deps;
216
+ constructor(deps) {
217
+ this.deps = deps;
218
+ }
219
+ /** Run one verb. @throws DoorError or UiControlError. */
220
+ async run(verb, args, signal) {
221
+ const input = typeof args === "object" && args !== null && !Array.isArray(args) ? args : {};
222
+ switch (verb) {
223
+ case "brief": return this.brief();
224
+ case "look": {
225
+ const snapshot = await this.look(input, signal);
226
+ return {
227
+ value: snapshot,
228
+ text: renderSnapshot(snapshot, (cursor) => `run look --cursor ${String(cursor)}`)
229
+ };
230
+ }
231
+ case "act": {
232
+ const result = await this.act(input, signal);
233
+ return {
234
+ value: result,
235
+ text: `done${result.target === void 0 ? "" : `: ${result.target.role} "${result.target.name}"`}${result.detail === void 0 ? "" : ` (${result.detail})`}; look again before the next act`
236
+ };
237
+ }
238
+ case "plugin-list": return this.plugin((extensions) => extensions.list());
239
+ case "plugin-add": return this.plugin((extensions) => extensions.install(this.insideApp(input.folder)));
240
+ case "plugin-remove": {
241
+ const name = input.name;
242
+ if (typeof name !== "string" || name === "") throw new DoorError("invalid", "plugin remove needs a plugin name");
243
+ return this.plugin((extensions) => extensions.remove(name));
244
+ }
245
+ case "plugin-reload": return this.plugin((extensions) => extensions.reload());
246
+ case "tool-list": {
247
+ const offered = this.offeredTools();
248
+ return {
249
+ value: offered,
250
+ text: offered.length === 0 ? "no app tools are offered through the door (tools.expose is empty)" : offered.map((tool) => `${tool.name}: ${tool.description}`).join("\n")
251
+ };
252
+ }
253
+ case "tool-call": return this.callTool(input);
254
+ default: throw new DoorError("unknown-verb", `unknown verb ${JSON.stringify(verb)}; the verbs are ${VERBS.join(", ")}`);
255
+ }
256
+ }
257
+ brief() {
258
+ const { name, root } = this.deps.app;
259
+ const read = SELF_KNOWLEDGE.filter((path) => existsSync(resolve(root, path)));
260
+ const value = {
261
+ app: name,
262
+ root,
263
+ read,
264
+ page: this.deps.channel.connected ? "open" : "not open",
265
+ plugins: this.deps.extensions() === void 0 ? "unavailable" : "available",
266
+ verbs: VERBS.map((verb) => VERB_SPECS[verb].usage)
267
+ };
268
+ return {
269
+ value,
270
+ text: [
271
+ `App ${name} in ${root}.`,
272
+ read.length === 0 ? "No AGENTS.md or skill in the folder." : `Read first: ${read.join(", ")}.`,
273
+ `Page: ${value.page}${value.page === "not open" ? " (open the app in a browser to look and act)" : ""}.`,
274
+ `Live plugins: ${value.plugins}.`,
275
+ "Verbs:",
276
+ ...value.verbs.map((line) => ` ${line}`)
277
+ ].join("\n")
278
+ };
279
+ }
280
+ look(input, signal) {
281
+ const request = this.parse({
282
+ ...input,
283
+ op: "snapshot"
284
+ });
285
+ return this.callPage("look", request, signal);
286
+ }
287
+ act(input, signal) {
288
+ if (input.op === "snapshot") throw new DoorError("invalid", "use look to see the page");
289
+ return this.callPage(`act ${String(input.op)}`, this.parse(input), signal);
290
+ }
291
+ parse(input) {
292
+ try {
293
+ return parseUiRequest(input);
294
+ } catch (cause) {
295
+ throw new DoorError("invalid", cause instanceof Error ? cause.message : "invalid request");
296
+ }
297
+ }
298
+ /** Run one call on the page and record it. The audit holds which control was touched and how it ended, never what was typed. */
299
+ async callPage(verb, request, signal) {
300
+ const approval = "token";
301
+ try {
302
+ const value = await this.deps.channel.call(request, {
303
+ signal,
304
+ ...request.op === "wait" ? { timeoutMs: (request.timeoutMs ?? 3e3) + 5e3 } : {}
305
+ });
306
+ const target = "target" in value ? value.target : void 0;
307
+ this.deps.audit.record({
308
+ at: (/* @__PURE__ */ new Date()).toISOString(),
309
+ tool: verb,
310
+ ...target === void 0 ? {} : { target },
311
+ outcome: "ok",
312
+ approval
313
+ });
314
+ return value;
315
+ } catch (cause) {
316
+ const code = cause instanceof UiControlError ? cause.code : "failed";
317
+ const refused = cause instanceof UiControlError && [
318
+ "protected",
319
+ "sensitive",
320
+ "killed",
321
+ "invalid",
322
+ "stale-ref",
323
+ "unknown-ref",
324
+ "not-actionable"
325
+ ].includes(cause.code);
326
+ this.deps.audit.record({
327
+ at: (/* @__PURE__ */ new Date()).toISOString(),
328
+ tool: verb,
329
+ outcome: refused ? "refused" : "failed",
330
+ detail: code,
331
+ approval
332
+ });
333
+ throw cause;
334
+ }
335
+ }
336
+ async plugin(run) {
337
+ const extensions = this.deps.extensions();
338
+ if (extensions === void 0) throw new DoorError("unavailable", "this app has no extensions plugin, so plugins cannot be installed while it runs");
339
+ const value = await run(extensions);
340
+ return {
341
+ value,
342
+ text: JSON.stringify(value, null, 2)
343
+ };
344
+ }
345
+ /** A folder named by an agent, resolved from the app folder; it must be inside the app. */
346
+ insideApp(folder) {
347
+ if (typeof folder !== "string" || folder === "") throw new DoorError("invalid", "plugin add needs a folder, e.g. plugins/pomodoro");
348
+ const root = this.deps.app.root;
349
+ const full = resolve(root, folder);
350
+ const path = relative(root, full);
351
+ if (path === "" || path.startsWith("..") || isAbsolute(path)) throw new DoorError("outside-app", `${folder} is not a folder inside the app (${root})`);
352
+ return full;
353
+ }
354
+ offeredTools() {
355
+ const tools = this.deps.tools();
356
+ if (tools === void 0) return [];
357
+ const allowed = new Set(this.deps.expose);
358
+ return tools.list().filter((tool) => allowed.has(tool.name)).map(({ name, description }) => ({
359
+ name,
360
+ description
361
+ }));
362
+ }
363
+ async callTool(input) {
364
+ const name = input.tool;
365
+ if (typeof name !== "string" || name === "") throw new DoorError("invalid", "tool call needs --tool <name>");
366
+ if (!this.deps.expose.includes(name)) throw new DoorError("not-exposed", `${name} is not offered through the door`);
367
+ const tools = this.deps.tools();
368
+ if (tools === void 0 || !tools.list().some((tool) => tool.name === name)) throw new DoorError("unavailable", `${name} is not available right now`);
369
+ let value;
370
+ try {
371
+ value = await tools.call(name, input.args ?? {});
372
+ } catch (cause) {
373
+ throw new DoorError("failed", cause instanceof Error ? cause.message : String(cause));
374
+ }
375
+ return {
376
+ value,
377
+ text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
378
+ };
379
+ }
380
+ };
381
+ //#endregion
382
+ //#region ../../plugins/acryl-door/src/host/chat-tools.ts
383
+ /**
384
+ * The door in the built-in chat: each verb registered in the app's `tools` registry (the one the chat's agent reads), as
385
+ * `door_<verb>`. A tool answers the verb's text, what an agent reads in a terminal; a failed verb throws with its code, as the operator
386
+ * client prints it.
387
+ */
388
+ /** Register every verb. @returns the function that removes them all; it also aborts the calls still running. */
389
+ function registerChatTools(tools, door) {
390
+ const running = new AbortController();
391
+ const removers = [];
392
+ const removeAll = () => {
393
+ running.abort();
394
+ for (const remove of removers.splice(0).reverse()) remove();
395
+ };
396
+ try {
397
+ for (const verb of VERBS) removers.push(tools.register(chatToolName(verb), {
398
+ description: VERB_SPECS[verb].description,
399
+ run: async (input) => {
400
+ try {
401
+ return (await door.run(verb, input ?? {}, running.signal)).text;
402
+ } catch (cause) {
403
+ if (cause instanceof DoorError || cause instanceof UiControlError) throw new Error(`${cause.code}: ${cause.message}`, { cause });
404
+ throw cause;
405
+ }
406
+ }
407
+ }));
408
+ } catch (cause) {
409
+ removeAll();
410
+ throw cause;
411
+ }
412
+ return removeAll;
413
+ }
414
+ //#endregion
415
+ //#region ../../plugins/acryl-door/src/host/door-file.ts
416
+ /**
417
+ * The door's connection file, `<app>/.acryl/door.json`: where the running app listens and this run's secret, for the operator client in
418
+ * the same folder. The secret is never on a command line or in an environment variable another process can read.
419
+ *
420
+ * Protection: on macOS and Linux the folder is `0700` and the file `0600`, so only the OS user who started the app can read it. On
421
+ * Windows file modes mean nothing; the file is protected by where it lives, the app folder in the user's profile, whose ACL gives access
422
+ * to that user, SYSTEM and Administrators only. The door does not widen it. The folder also carries its own `.gitignore` of `*`, so
423
+ * `git add -A` never stages the secret even in an app whose `.gitignore` lacks the `.acryl/` line.
424
+ */
425
+ /** The folder of the door's state below the app folder. */
426
+ const STATE_DIR = ".acryl";
427
+ const DOOR_FILE = "door.json";
428
+ function stateDir(appRoot) {
429
+ return join(appRoot, STATE_DIR);
430
+ }
431
+ function doorFilePath(appRoot) {
432
+ return join(stateDir(appRoot), DOOR_FILE);
433
+ }
434
+ /** A fresh secret for this run; a restart rotates it. */
435
+ function newSecret() {
436
+ return randomBytes(24).toString("hex");
437
+ }
438
+ /** The state folder, private, with its own `.gitignore`. */
439
+ function ensureStateDir(appRoot) {
440
+ const dir = stateDir(appRoot);
441
+ mkdirSync(dir, {
442
+ recursive: true,
443
+ mode: 448
444
+ });
445
+ if (process.platform !== "win32") chmodSync(dir, 448);
446
+ const ignore = join(dir, ".gitignore");
447
+ if (!existsSync(ignore)) writeFileSync(ignore, "# The door's state: this run's secret and the audit. Never commit it.\n*\n");
448
+ return dir;
449
+ }
450
+ /** The connection file as written, or undefined when there is none or it does not parse. */
451
+ function readDoorFile(appRoot) {
452
+ try {
453
+ const value = JSON.parse(readFileSync(doorFilePath(appRoot), "utf8"));
454
+ if (value.version === 1 && typeof value.url === "string" && typeof value.secret === "string" && typeof value.pid === "number") return value;
455
+ } catch {}
456
+ }
457
+ /** True while the process is alive (a process of another user still counts: `EPERM`). */
458
+ function alive(pid) {
459
+ try {
460
+ process.kill(pid, 0);
461
+ return true;
462
+ } catch (cause) {
463
+ return cause.code === "EPERM";
464
+ }
465
+ }
466
+ /**
467
+ * Write this run's connection file, replacing a stale one.
468
+ * @throws when another live process already serves this folder's door: two apps from one folder would race for one file.
469
+ */
470
+ function writeDoorFile(appRoot, url, secret) {
471
+ const dir = ensureStateDir(appRoot);
472
+ const existing = readDoorFile(appRoot);
473
+ if (existing !== void 0 && existing.pid !== process.pid && alive(existing.pid)) throw new Error(`acryl-door: another process (pid ${String(existing.pid)}) serves the door of ${appRoot}; stop it first`);
474
+ const file = doorFilePath(appRoot);
475
+ const temporary = join(dir, `${DOOR_FILE}.${String(process.pid)}.tmp`);
476
+ const content = {
477
+ version: 1,
478
+ url,
479
+ secret,
480
+ pid: process.pid
481
+ };
482
+ writeFileSync(temporary, `${JSON.stringify(content)}\n`, { mode: 384 });
483
+ if (process.platform !== "win32") chmodSync(temporary, 384);
484
+ renameSync(temporary, file);
485
+ return file;
486
+ }
487
+ /** Remove the connection file, only when it is this run's: a newer run's file is left alone. */
488
+ function removeDoorFile(appRoot, secret) {
489
+ if (readDoorFile(appRoot)?.secret === secret) rmSync(doorFilePath(appRoot), { force: true });
490
+ }
491
+ //#endregion
492
+ //#region ../../plugins/acryl-door/src/host/mcp-routes.ts
493
+ /**
494
+ * The door over MCP: `POST <prefix>/mcp`, streamable HTTP in its request/response form (one JSON-RPC message or a batch in, its JSON
495
+ * answer out, 202 for notifications only; no event stream is offered). Each verb is one tool, named as the verb, with the arguments the
496
+ * operator client sends. The same guard as the operator's routes: loopback, this run's secret.
497
+ */
498
+ const SUPPORTED_PROTOCOLS = [
499
+ "2025-06-18",
500
+ "2025-03-26",
501
+ "2024-11-05"
502
+ ];
503
+ const SERVER_INFO = {
504
+ name: "acryl-door",
505
+ version: "1"
506
+ };
507
+ function isRequest(value) {
508
+ return typeof value === "object" && value !== null && value.jsonrpc === "2.0" && typeof value.method === "string";
509
+ }
510
+ const rpcError = (id, code, message) => ({
511
+ jsonrpc: "2.0",
512
+ id: id ?? null,
513
+ error: {
514
+ code,
515
+ message
516
+ }
517
+ });
518
+ const result = (id, value) => ({
519
+ jsonrpc: "2.0",
520
+ id: id ?? null,
521
+ result: value
522
+ });
523
+ /** The verbs as MCP tools. */
524
+ function mcpTools() {
525
+ return VERBS.map((verb) => ({
526
+ name: verb,
527
+ description: VERB_SPECS[verb].description,
528
+ inputSchema: VERB_SPECS[verb].arguments
529
+ }));
530
+ }
531
+ const isVerb = (name) => typeof name === "string" && VERBS.includes(name);
532
+ /** One JSON-RPC message; `undefined` for a notification (no answer owed). */
533
+ async function answer(message, door, signal) {
534
+ const isNotification = message.id === void 0;
535
+ switch (message.method) {
536
+ case "initialize": {
537
+ const asked = message.params?.protocolVersion;
538
+ const protocolVersion = SUPPORTED_PROTOCOLS.includes(asked) ? asked : SUPPORTED_PROTOCOLS[0];
539
+ return result(message.id, {
540
+ protocolVersion,
541
+ capabilities: { tools: { listChanged: false } },
542
+ serverInfo: SERVER_INFO
543
+ });
544
+ }
545
+ case "ping": return isNotification ? void 0 : result(message.id, {});
546
+ case "tools/list": return result(message.id, { tools: mcpTools() });
547
+ case "tools/call": {
548
+ const params = typeof message.params === "object" && message.params !== null ? message.params : {};
549
+ if (!isVerb(params.name)) return rpcError(message.id, -32602, `unknown tool ${JSON.stringify(params.name)}; the tools are ${VERBS.join(", ")}`);
550
+ try {
551
+ const verbAnswer = await door.run(params.name, params.arguments ?? {}, signal);
552
+ return result(message.id, {
553
+ content: [{
554
+ type: "text",
555
+ text: verbAnswer.text
556
+ }],
557
+ structuredContent: { value: verbAnswer.value },
558
+ isError: false
559
+ });
560
+ } catch (cause) {
561
+ if (cause instanceof DoorError || cause instanceof UiControlError) return result(message.id, {
562
+ content: [{
563
+ type: "text",
564
+ text: `${cause.code}: ${cause.message}`
565
+ }],
566
+ isError: true
567
+ });
568
+ throw cause;
569
+ }
570
+ }
571
+ default: return isNotification ? void 0 : rpcError(message.id, -32601, `method not found: ${message.method}`);
572
+ }
573
+ }
574
+ function mcpRoutes(prefix, secret, door, reportError) {
575
+ return new Acryl().post(`${prefix}/mcp`, operatorOnly(secret), jsonBody, async (c) => {
576
+ const body = await readJson(c);
577
+ if (body === INVALID_JSON) return c.json(rpcError(null, -32700, "parse error"), 400);
578
+ try {
579
+ const messages = Array.isArray(body) ? body : [body];
580
+ const answers = [];
581
+ for (const message of messages) {
582
+ if (!isRequest(message)) {
583
+ answers.push(rpcError(null, -32600, "invalid request"));
584
+ continue;
585
+ }
586
+ const response = await answer(message, door, c.req.raw.signal);
587
+ if (response !== void 0) answers.push(response);
588
+ }
589
+ if (answers.length === 0) return c.body(null, 202);
590
+ return c.json(Array.isArray(body) ? answers : answers[0]);
591
+ } catch (cause) {
592
+ reportError("answer an MCP request", cause);
593
+ return c.json(rpcError(null, -32603, "internal error"), 500);
594
+ }
595
+ });
596
+ }
597
+ //#endregion
598
+ //#region ../../plugins/acryl-door/src/host/operator-routes.ts
599
+ /**
600
+ * The operator's routes: an agent's shell on this machine, through `.acryl/bin/acryl`. Loopback only, with this run's secret.
601
+ *
602
+ * `POST <prefix>/call` takes `{ verb, args }` and answers {@link CallResponse}; `GET <prefix>/verbs` lists the verbs. A caller that goes
603
+ * away aborts its call through the request's signal.
604
+ */
605
+ function operatorRoutes(prefix, secret, door, reportError) {
606
+ return new Acryl().get(`${prefix}/verbs`, operatorOnly(secret), (c) => c.json({ verbs: VERBS })).post(`${prefix}/call`, operatorOnly(secret), jsonBody, async (c) => {
607
+ const body = await readJson(c);
608
+ if (body === INVALID_JSON || typeof body !== "object" || body === null || typeof body.verb !== "string") return c.json({
609
+ ok: false,
610
+ code: "invalid",
611
+ message: "the body is { verb, args }"
612
+ }, 400);
613
+ const { verb, args } = body;
614
+ try {
615
+ const answer = await door.run(verb, args, c.req.raw.signal);
616
+ return c.json({
617
+ ok: true,
618
+ value: answer.value,
619
+ text: answer.text
620
+ });
621
+ } catch (cause) {
622
+ if (cause instanceof UiControlError || cause instanceof DoorError) return c.json({
623
+ ok: false,
624
+ code: cause.code,
625
+ message: cause.message
626
+ }, cause.code === "invalid" || cause.code === "unknown-verb" ? 400 : 200);
627
+ reportError(`run the door verb ${verb}`, cause);
628
+ return c.json(error("the verb could not be run"), 500);
629
+ }
630
+ });
631
+ }
632
+ /** A word for `sh`: single-quoted, with each `'` closed, escaped and reopened. */
633
+ function shWord(value) {
634
+ return `'${value.replaceAll("'", "'\\''")}'`;
635
+ }
636
+ /** A word for `cmd.exe`: double-quoted; `%` doubled so a batch file does not expand it. A path cannot hold `"`. */
637
+ function cmdWord(value) {
638
+ return `"${value.replaceAll("%", "%%")}"`;
639
+ }
640
+ function posixShim(root, target) {
641
+ const words = [
642
+ target.node,
643
+ ...target.nodeOptions,
644
+ target.client,
645
+ "--dir",
646
+ root
647
+ ].map(shWord).join(" ");
648
+ return `#!/bin/sh\n# This app's door (acryl-door): reaches only the app running from this folder. Rewritten each time the app starts.\n${target.electron ? "export ELECTRON_RUN_AS_NODE=1\n" : ""}exec ${words} "$@"\n`;
649
+ }
650
+ function windowsShim(root, target) {
651
+ const words = [
652
+ target.node,
653
+ ...target.nodeOptions,
654
+ target.client,
655
+ "--dir",
656
+ root
657
+ ].map(cmdWord).join(" ");
658
+ return `@echo off\r\nsetlocal\r\nrem This app's door (acryl-door): reaches only the app running from this folder. Rewritten each time the app starts.\r\n${target.electron ? "set ELECTRON_RUN_AS_NODE=1\r\n" : ""}${words} %*\r\n`;
659
+ }
660
+ function writeAtomically(file, content, mode) {
661
+ const temporary = `${file}.${String(process.pid)}.tmp`;
662
+ writeFileSync(temporary, content, { mode });
663
+ if (process.platform !== "win32") chmodSync(temporary, mode);
664
+ renameSync(temporary, file);
665
+ }
666
+ /** Write `.acryl/bin/acryl` and `.acryl/bin/acryl.cmd` for the app at `root`. @returns the folder they are in. */
667
+ function writeShims(root, target) {
668
+ const bin = join(ensureStateDir(root), "bin");
669
+ mkdirSync(bin, {
670
+ recursive: true,
671
+ mode: 448
672
+ });
673
+ if (process.platform !== "win32") chmodSync(bin, 448);
674
+ writeAtomically(join(bin, "acryl"), posixShim(root, target), 448);
675
+ writeAtomically(join(bin, "acryl.cmd"), windowsShim(root, target), 384);
676
+ return bin;
677
+ }
678
+ //#endregion
679
+ //#region ../../plugins/acryl-door/src/index.ts
680
+ /**
681
+ * `acryl-door`: the Door of the five nouns, as a Cordis plugin of a framework app (spec 049 slice 9). An agent working in the app folder
682
+ * looks at the running page, acts on it, and installs plugins live, through `.acryl/bin/acryl`; everything else it does with its own
683
+ * file tools and shell.
684
+ *
685
+ * The page loads `/api/acryl-door/page.js` (one script tag) and keeps an event stream open to the Host; the Host sends it calls. An
686
+ * operator on this machine reaches `/api/acryl-door/call` with the secret from `<app>/.acryl/door.json`. One effect owns the channel and
687
+ * the audit, the routes are mounted after it, and a second effect writes the connection file last, so disposal removes the file first,
688
+ * then the routes (their in-flight calls end), then closes the channel (every pending call settles).
689
+ */
690
+ /** Stable Cordis plugin name, equal to the package name and the Loader row id. */
691
+ const name = "acryl-door";
692
+ const inject = [
693
+ "router",
694
+ "appInstance",
695
+ "nodeServer"
696
+ ];
697
+ const Config = z.object({ tools: z.object({ expose: z.array(z.string().pattern(/^(?!door_)[A-Za-z][A-Za-z0-9_-]{0,63}$/u)).description("App tools (from the tools registry) the door offers; none by default.") }) });
698
+ /** The page module, served compiled from source by the app's own TypeScript. */
699
+ const PAGE_DIR = join(import.meta.dirname, import.meta.url.endsWith(".ts") ? "page" : join("..", "src", "page"));
700
+ /** The operator client this build runs: the source next to this file, or the built `cli.js`. */
701
+ const CLIENT = import.meta.url.endsWith(".ts") ? join(import.meta.dirname, "cli", "acryl.ts") : join(import.meta.dirname, "cli.js");
702
+ /** How the folder's `.acryl/bin/acryl` starts the client: with the Node that runs the app, and its type-stripping flag when run from source. */
703
+ function shimTarget() {
704
+ const fromSource = CLIENT.endsWith(".ts");
705
+ return {
706
+ node: process.execPath,
707
+ nodeOptions: fromSource ? process.execArgv.filter((option) => /^--experimental-(strip|transform)-types$/u.test(option)) : [],
708
+ client: CLIENT,
709
+ electron: process.versions.electron !== void 0
710
+ };
711
+ }
712
+ /** A listening address as one the operator reaches: an any-address bind is reached on loopback. */
713
+ function loopbackUrl(url) {
714
+ const parsed = new URL(url);
715
+ if (parsed.hostname === "0.0.0.0" || parsed.hostname === "[::]") parsed.hostname = "127.0.0.1";
716
+ return parsed.origin;
717
+ }
718
+ function apply(ctx, rawConfig = {}) {
719
+ const config = Config(rawConfig);
720
+ const app = ctx.get("appInstance");
721
+ const server = ctx.get("nodeServer");
722
+ if (typeof app?.root !== "string") throw new Error("acryl-door: the appInstance service has no app folder (root)");
723
+ const reportError = (operation, cause) => {
724
+ ctx.logger?.error?.(`acryl-door: failed to ${operation}: ${cause instanceof Error ? cause.message : String(cause)}`);
725
+ };
726
+ const secret = newSecret();
727
+ const channel = new UiChannel();
728
+ const door = new Door({
729
+ channel,
730
+ audit: new AuditLog(auditPath(stateDir(app.root))),
731
+ app: {
732
+ name: app.name,
733
+ root: app.root
734
+ },
735
+ extensions: () => ctx.get("extensions"),
736
+ tools: () => ctx.get("tools"),
737
+ expose: config.tools?.expose ?? []
738
+ });
739
+ ctx.effect(() => () => {
740
+ channel.close();
741
+ }, "acryl-door: page channel");
742
+ const pageModule = new Acryl().get(`${DOOR_PREFIX}/page.js`, (c) => c.body(`import '${DOOR_PREFIX}/page/main.ts'\n`, 200, { "content-type": "text/javascript; charset=utf-8" })).route(`${DOOR_PREFIX}/page`, clientModules({ root: PAGE_DIR }));
743
+ ctx.router.route("/", privateApi(DOOR_PREFIX, pageModule, pageChannelRoutes(channel, { prefix: DOOR_PREFIX }), operatorRoutes(DOOR_PREFIX, secret, door, reportError), mcpRoutes(DOOR_PREFIX, secret, door, reportError)));
744
+ ctx.inject(["tools"], (inner) => {
745
+ inner.effect(() => registerChatTools(inner.get("tools"), door), "acryl-door: the verbs in the tools registry");
746
+ });
747
+ ctx.effect(function* () {
748
+ yield ctx.reflect.provide("door", door);
749
+ writeDoorFile(app.root, loopbackUrl(server.url), secret);
750
+ writeShims(app.root, shimTarget());
751
+ yield () => {
752
+ removeDoorFile(app.root, secret);
753
+ };
754
+ }, "acryl-door: the door service, .acryl/door.json and .acryl/bin/acryl");
755
+ }
756
+ //#endregion
757
+ export { Config, DOOR_PREFIX, Door, DoorError, STATE_DIR, VERBS, VERB_SPECS, apply, chatToolName, doorFilePath, inject, mcpTools, name, readDoorFile };