acrylblends 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -1
- package/dist/index.js +28 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/plugins/acryl-inspect/plugin.json +2 -1
- package/plugins/acryl-inspect/src/report.ts +16 -13
- package/plugins/acryl-inspect/src/service.ts +9 -7
- package/plugins/acryl-inspect/tests/report.spec.ts +7 -8
- package/plugins/acryl-inspect/tests/service.spec.ts +11 -10
- package/plugins/acryl-inspect/upstream.json +2 -2
- package/plugins/acryl-node-server/LICENSE +26 -0
- package/plugins/acryl-node-server/plugin.json +2 -1
- package/plugins/acryl-node-server/src/adapter/conninfo.ts +23 -0
- package/plugins/acryl-node-server/src/adapter/early-hints.ts +55 -0
- package/plugins/acryl-node-server/src/adapter/error.ts +11 -0
- package/plugins/acryl-node-server/src/adapter/headers.ts +249 -0
- package/plugins/acryl-node-server/src/adapter/index.ts +6 -0
- package/plugins/acryl-node-server/src/adapter/listener.ts +540 -0
- package/plugins/acryl-node-server/src/adapter/request.ts +797 -0
- package/plugins/acryl-node-server/src/adapter/response.ts +186 -0
- package/plugins/acryl-node-server/src/adapter/serve-static.ts +238 -0
- package/plugins/acryl-node-server/src/adapter/server.ts +36 -0
- package/plugins/acryl-node-server/src/adapter/types.ts +82 -0
- package/plugins/acryl-node-server/src/adapter/url.ts +42 -0
- package/plugins/acryl-node-server/src/adapter/utils/response/constants.ts +1 -0
- package/plugins/acryl-node-server/src/adapter/utils/response.ts +4 -0
- package/plugins/acryl-node-server/src/adapter/utils/stream.ts +88 -0
- package/plugins/acryl-node-server/src/adapter/utils.ts +96 -0
- package/plugins/acryl-node-server/src/adapter/websocket-types.ts +45 -0
- package/plugins/acryl-node-server/src/adapter/websocket.ts +375 -0
- package/plugins/acryl-node-server/src/index.ts +19 -6
- package/plugins/acryl-node-server/tests/adapter/app.ts +76 -0
- package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +5 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +1 -0
- package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +34 -0
- package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +248 -0
- package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +457 -0
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +23 -0
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +27 -0
- package/plugins/acryl-node-server/tests/adapter/headers.test.ts +252 -0
- package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +190 -0
- package/plugins/acryl-node-server/tests/adapter/listener.test.ts +727 -0
- package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +189 -0
- package/plugins/acryl-node-server/tests/adapter/request.test.ts +1230 -0
- package/plugins/acryl-node-server/tests/adapter/response.test.ts +285 -0
- package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +849 -0
- package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +790 -0
- package/plugins/acryl-node-server/tests/adapter/server.test.ts +1302 -0
- package/plugins/acryl-node-server/tests/adapter/setup.ts +16 -0
- package/plugins/acryl-node-server/tests/adapter/url.test.ts +76 -0
- package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +23 -0
- package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +75 -0
- package/plugins/acryl-node-server/tests/adapter/utils.test.ts +235 -0
- package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +260 -0
- package/plugins/acryl-node-server/tests/server.spec.ts +44 -8
- package/plugins/acryl-node-server/upstream.json +3 -3
- package/plugins/acryl-router/LICENSE +26 -0
- package/plugins/acryl-router/plugin.json +2 -1
- package/plugins/acryl-router/src/client.ts +2 -0
- package/plugins/acryl-router/src/connection.ts +42 -0
- package/plugins/acryl-router/src/core/app-base.ts +544 -0
- package/plugins/acryl-router/src/core/app.test.ts +3833 -0
- package/plugins/acryl-router/src/core/app.ts +34 -0
- package/plugins/acryl-router/src/core/client/client.test.ts +2313 -0
- package/plugins/acryl-router/src/core/client/client.ts +249 -0
- package/plugins/acryl-router/src/core/client/fetch-result-please.ts +95 -0
- package/plugins/acryl-router/src/core/client/index.ts +17 -0
- package/plugins/acryl-router/src/core/client/types.test.ts +149 -0
- package/plugins/acryl-router/src/core/client/types.ts +393 -0
- package/plugins/acryl-router/src/core/client/utils.test.ts +405 -0
- package/plugins/acryl-router/src/core/client/utils.ts +117 -0
- package/plugins/acryl-router/src/core/compose.test.ts +751 -0
- package/plugins/acryl-router/src/core/compose.ts +73 -0
- package/plugins/acryl-router/src/core/context.test.ts +629 -0
- package/plugins/acryl-router/src/core/context.ts +781 -0
- package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +344 -0
- package/plugins/acryl-router/src/core/helper/accepts/accepts.ts +96 -0
- package/plugins/acryl-router/src/core/helper/accepts/index.ts +6 -0
- package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +55 -0
- package/plugins/acryl-router/src/core/helper/adapter/index.ts +90 -0
- package/plugins/acryl-router/src/core/helper/conninfo/index.ts +6 -0
- package/plugins/acryl-router/src/core/helper/conninfo/types.ts +45 -0
- package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +510 -0
- package/plugins/acryl-router/src/core/helper/cookie/index.ts +146 -0
- package/plugins/acryl-router/src/core/helper/factory/index.test.ts +434 -0
- package/plugins/acryl-router/src/core/helper/factory/index.ts +375 -0
- package/plugins/acryl-router/src/core/helper/html/index.test.ts +78 -0
- package/plugins/acryl-router/src/core/helper/html/index.ts +57 -0
- package/plugins/acryl-router/src/core/helper/route/index.test.ts +259 -0
- package/plugins/acryl-router/src/core/helper/route/index.ts +144 -0
- package/plugins/acryl-router/src/core/helper/streaming/index.ts +9 -0
- package/plugins/acryl-router/src/core/helper/streaming/sse.ts +101 -0
- package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +128 -0
- package/plugins/acryl-router/src/core/helper/streaming/stream.ts +45 -0
- package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +34 -0
- package/plugins/acryl-router/src/core/helper/streaming/text.ts +15 -0
- package/plugins/acryl-router/src/core/helper/streaming/utils.ts +11 -0
- package/plugins/acryl-router/src/core/helper/testing/index.test.ts +42 -0
- package/plugins/acryl-router/src/core/helper/testing/index.ts +28 -0
- package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +124 -0
- package/plugins/acryl-router/src/core/helper/websocket/index.ts +140 -0
- package/plugins/acryl-router/src/core/http-exception.test.ts +45 -0
- package/plugins/acryl-router/src/core/http-exception.ts +77 -0
- package/plugins/acryl-router/src/core/index.ts +53 -0
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +434 -0
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +152 -0
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +1018 -0
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +220 -0
- package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +273 -0
- package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +110 -0
- package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +355 -0
- package/plugins/acryl-router/src/core/middleware/combine/index.ts +171 -0
- package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +535 -0
- package/plugins/acryl-router/src/core/middleware/compress/index.ts +142 -0
- package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +43 -0
- package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +58 -0
- package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +532 -0
- package/plugins/acryl-router/src/core/middleware/cors/index.ts +163 -0
- package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +534 -0
- package/plugins/acryl-router/src/core/middleware/csrf/index.ts +150 -0
- package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +21 -0
- package/plugins/acryl-router/src/core/middleware/etag/digest.ts +95 -0
- package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +503 -0
- package/plugins/acryl-router/src/core/middleware/etag/index.ts +135 -0
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +217 -0
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +278 -0
- package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +1135 -0
- package/plugins/acryl-router/src/core/middleware/jwk/index.ts +1 -0
- package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +195 -0
- package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +48 -0
- package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +802 -0
- package/plugins/acryl-router/src/core/middleware/jwt/index.ts +9 -0
- package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +189 -0
- package/plugins/acryl-router/src/core/middleware/language/index.test.ts +552 -0
- package/plugins/acryl-router/src/core/middleware/language/index.ts +12 -0
- package/plugins/acryl-router/src/core/middleware/language/language.ts +314 -0
- package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +204 -0
- package/plugins/acryl-router/src/core/middleware/logger/index.ts +95 -0
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +292 -0
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +141 -0
- package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +221 -0
- package/plugins/acryl-router/src/core/middleware/method-override/index.ts +147 -0
- package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +37 -0
- package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +35 -0
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +218 -0
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +67 -0
- package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +168 -0
- package/plugins/acryl-router/src/core/middleware/request-id/index.ts +7 -0
- package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +59 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +671 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/index.ts +8 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/permissions-policy.ts +101 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +340 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +450 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +135 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +50 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/path.ts +25 -0
- package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +70 -0
- package/plugins/acryl-router/src/core/middleware/timeout/index.ts +58 -0
- package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +310 -0
- package/plugins/acryl-router/src/core/middleware/timing/index.ts +8 -0
- package/plugins/acryl-router/src/core/middleware/timing/timing.ts +256 -0
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +381 -0
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +156 -0
- package/plugins/acryl-router/src/core/request/constants.ts +1 -0
- package/plugins/acryl-router/src/core/request.test.ts +751 -0
- package/plugins/acryl-router/src/core/request.ts +502 -0
- package/plugins/acryl-router/src/core/router/common.case.test.ts +1132 -0
- package/plugins/acryl-router/src/core/router/linear-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +80 -0
- package/plugins/acryl-router/src/core/router/linear-router/router.ts +152 -0
- package/plugins/acryl-router/src/core/router/pattern-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +46 -0
- package/plugins/acryl-router/src/core/router/pattern-router/router.ts +54 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +7 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.ts +33 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/node.ts +170 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +154 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.ts +165 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +265 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/router.ts +174 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/trie.ts +84 -0
- package/plugins/acryl-router/src/core/router/smart-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +13 -0
- package/plugins/acryl-router/src/core/router/smart-router/router.ts +70 -0
- package/plugins/acryl-router/src/core/router/trie-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +896 -0
- package/plugins/acryl-router/src/core/router/trie-router/node.ts +221 -0
- package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +8 -0
- package/plugins/acryl-router/src/core/router/trie-router/router.ts +18 -0
- package/plugins/acryl-router/src/core/router/utils.ts +1 -0
- package/plugins/acryl-router/src/core/router.ts +103 -0
- package/plugins/acryl-router/src/core/types.test.ts +4134 -0
- package/plugins/acryl-router/src/core/types.ts +2782 -0
- package/plugins/acryl-router/src/core/utils/accept.test.ts +299 -0
- package/plugins/acryl-router/src/core/utils/accept.ts +268 -0
- package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +56 -0
- package/plugins/acryl-router/src/core/utils/basic-auth.ts +26 -0
- package/plugins/acryl-router/src/core/utils/body.test.ts +496 -0
- package/plugins/acryl-router/src/core/utils/body.ts +263 -0
- package/plugins/acryl-router/src/core/utils/buffer.test.ts +147 -0
- package/plugins/acryl-router/src/core/utils/buffer.ts +117 -0
- package/plugins/acryl-router/src/core/utils/color.test.ts +23 -0
- package/plugins/acryl-router/src/core/utils/color.ts +56 -0
- package/plugins/acryl-router/src/core/utils/compress.ts +10 -0
- package/plugins/acryl-router/src/core/utils/concurrent.test.ts +69 -0
- package/plugins/acryl-router/src/core/utils/concurrent.ts +55 -0
- package/plugins/acryl-router/src/core/utils/constants.ts +4 -0
- package/plugins/acryl-router/src/core/utils/cookie.test.ts +460 -0
- package/plugins/acryl-router/src/core/utils/cookie.ts +281 -0
- package/plugins/acryl-router/src/core/utils/crypto.test.ts +43 -0
- package/plugins/acryl-router/src/core/utils/crypto.ts +58 -0
- package/plugins/acryl-router/src/core/utils/encode.test.ts +61 -0
- package/plugins/acryl-router/src/core/utils/encode.ts +34 -0
- package/plugins/acryl-router/src/core/utils/filepath.test.ts +78 -0
- package/plugins/acryl-router/src/core/utils/filepath.ts +60 -0
- package/plugins/acryl-router/src/core/utils/handler.ts +15 -0
- package/plugins/acryl-router/src/core/utils/headers.ts +355 -0
- package/plugins/acryl-router/src/core/utils/html.test.ts +16 -0
- package/plugins/acryl-router/src/core/utils/html.ts +182 -0
- package/plugins/acryl-router/src/core/utils/http-status.ts +72 -0
- package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +149 -0
- package/plugins/acryl-router/src/core/utils/ipaddr.ts +374 -0
- package/plugins/acryl-router/src/core/utils/jwt/index.ts +7 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +86 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwa.ts +38 -0
- package/plugins/acryl-router/src/core/utils/jwt/jws.ts +235 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +1788 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +292 -0
- package/plugins/acryl-router/src/core/utils/jwt/types.ts +157 -0
- package/plugins/acryl-router/src/core/utils/jwt/utf8.ts +7 -0
- package/plugins/acryl-router/src/core/utils/mime.test.ts +64 -0
- package/plugins/acryl-router/src/core/utils/mime.ts +94 -0
- package/plugins/acryl-router/src/core/utils/stream.test.ts +212 -0
- package/plugins/acryl-router/src/core/utils/stream.ts +107 -0
- package/plugins/acryl-router/src/core/utils/types.test.ts +295 -0
- package/plugins/acryl-router/src/core/utils/types.ts +123 -0
- package/plugins/acryl-router/src/core/utils/url.test.ts +384 -0
- package/plugins/acryl-router/src/core/utils/url.ts +337 -0
- package/plugins/acryl-router/src/core/validator/index.ts +8 -0
- package/plugins/acryl-router/src/core/validator/utils.test.ts +82 -0
- package/plugins/acryl-router/src/core/validator/utils.ts +69 -0
- package/plugins/acryl-router/src/core/validator/validator.test.ts +1488 -0
- package/plugins/acryl-router/src/core/validator/validator.ts +185 -0
- package/plugins/acryl-router/src/helpers/index.ts +13 -3
- package/plugins/acryl-router/src/helpers/schema-validator.ts +35 -0
- package/plugins/acryl-router/src/index.ts +6 -6
- package/plugins/acryl-router/src/middleware/index.ts +25 -6
- package/plugins/acryl-router/src/middleware/loopback-only.ts +19 -13
- package/plugins/acryl-router/src/service.ts +256 -64
- package/plugins/acryl-router/tests/docs-examples.spec.ts +152 -0
- package/plugins/acryl-router/tests/live-install.spec.ts +2 -2
- package/plugins/acryl-router/tests/loader.spec.ts +3 -2
- package/plugins/acryl-router/tests/service.spec.ts +264 -66
- package/plugins/acryl-router/tests/shadow-state.spec.ts +79 -0
- package/plugins/acryl-router/upstream.json +2 -2
- package/plugins/acryl-router-http/plugin.json +2 -1
- package/plugins/acryl-router-http/src/bridge.ts +3 -1
- package/plugins/acryl-router-http/tests/adapter.spec.ts +16 -12
- package/plugins/acryl-router-http/tests/surfaces.spec.ts +12 -10
- package/plugins/acryl-router-http/upstream.json +3 -3
- package/plugins/acryl-state/NOTICE +30 -0
- package/plugins/acryl-state/plugin.json +1 -1
- package/plugins/acryl-state/src/cell.ts +1 -1
- package/plugins/acryl-state/src/service.ts +33 -15
- package/plugins/acryl-state/upstream.json +6 -4
- package/plugins/builder/script.ts +4 -7
- package/plugins/extensions/index.ts +2 -2
- package/plugins/extensions/resolve.ts +28 -3
- package/plugins/openai/prompt.ts +9 -7
- package/plugins/tsconfig.json +5 -4
- package/templates/agent/src/routes.ts +37 -27
- package/templates/agent/test/model.test.ts +2 -1
- package/templates/agent/tsconfig.json +8 -5
- package/templates/blank/src/routes.ts +6 -4
- package/templates/blank/tsconfig.json +2 -4
- package/templates/todo/src/page.ts +1 -1
- package/templates/todo/src/todo.ts +56 -29
- package/templates/todo/tsconfig.json +2 -4
- package/templates/todo/upstream.json +2 -2
- package/plugins/acryl-router/src/compose.ts +0 -21
- package/plugins/acryl-router/src/context.ts +0 -59
- package/plugins/acryl-router/src/helpers/read-body.ts +0 -59
- package/plugins/acryl-router/src/helpers/sse.ts +0 -83
- package/plugins/acryl-router/src/helpers/validate.ts +0 -44
- package/plugins/acryl-router/src/http-error.ts +0 -21
- package/plugins/acryl-router/src/middleware/body-limit.ts +0 -25
- package/plugins/acryl-router/src/middleware/cors.ts +0 -46
- package/plugins/acryl-router/src/middleware/logger.ts +0 -21
- package/plugins/acryl-router/src/middleware/request-id.ts +0 -24
- package/plugins/acryl-router/src/middleware/secure-headers.ts +0 -23
- package/plugins/acryl-router/src/pattern.ts +0 -89
- package/plugins/acryl-router/src/registrar.ts +0 -90
- package/plugins/acryl-router/src/table.ts +0 -156
- package/plugins/acryl-router/src/types.ts +0 -73
- package/plugins/acryl-router/src/util.ts +0 -23
- package/plugins/acryl-router/tests/api.spec.ts +0 -223
- package/plugins/acryl-router/tests/conformance.ts +0 -170
- package/plugins/acryl-router/tests/helpers.spec.ts +0 -172
- package/plugins/acryl-router/tests/middleware.spec.ts +0 -183
- package/plugins/acryl-router/tests/table.spec.ts +0 -47
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
"source": {
|
|
3
3
|
"repo": "acryldev/acryl",
|
|
4
4
|
"path": "plugins/acryl-router-http",
|
|
5
|
-
"commit": "
|
|
5
|
+
"commit": "df79b188398b6d4f8cceda35a72b88300a10a9ce"
|
|
6
6
|
},
|
|
7
7
|
"vendored": [
|
|
8
8
|
"src",
|
|
9
9
|
"tests",
|
|
10
10
|
"LICENSE"
|
|
11
11
|
],
|
|
12
|
-
"rewrittenSpecifiers":
|
|
13
|
-
"digest": "sha256:
|
|
12
|
+
"rewrittenSpecifiers": 7,
|
|
13
|
+
"digest": "sha256:29ba7834cf9d29e9af2f4fac265c7e5e220e06da3346e65c29bcbae41fdfaf9e",
|
|
14
14
|
"testsNotRun": {
|
|
15
15
|
"tests/adapter.spec.ts": "needs @deepseek-ai/dsh-host-webserver (the DSH web server); acryl-node-server has its own tests",
|
|
16
16
|
"tests/stream.spec.ts": "needs @deepseek-ai/dsh-host-webserver (the DSH web server)",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
acryl-state
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WebBoxes s.r.o.
|
|
4
|
+
Licensed under the MIT License (see LICENSE).
|
|
5
|
+
|
|
6
|
+
The cell and derived-cell shape follows VanJS (https://vanjs.org). The code is a new implementation; the notice of that work is kept here.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
VanJS
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2023 VanJS (Vanilla JavaScript)
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Observable cells: the reactive core of the state service, with no Cordis and no DOM. A cell holds one value, tells its
|
|
3
|
-
* subscribers when it changes, and can be derived from other cells. The shape
|
|
3
|
+
* subscribers when it changes, and can be derived from other cells. The shape follows VanJS's `state` and `derive`
|
|
4
4
|
* (spec 046, M5): small enough to read in one sitting, and the same cell serves a React hook, a terminal component and a
|
|
5
5
|
* server-sent stream.
|
|
6
6
|
*/
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Context, Service } from '@deepseek-ai/cordis'
|
|
9
|
-
import type
|
|
9
|
+
import { App, type Context as RequestContext } from '../../acryl-router/src/index.ts'
|
|
10
10
|
import { streamSSE } from '../../acryl-router/src/helpers/index.ts'
|
|
11
|
-
import { loopbackOnly } from '../../acryl-router/src/middleware/index.ts'
|
|
11
|
+
import { loopbackOnly, methodNotAllowed } from '../../acryl-router/src/middleware/index.ts'
|
|
12
12
|
import { createCell, createDerived, type Cell, type ReadonlyCell } from './cell.ts'
|
|
13
13
|
|
|
14
14
|
declare module '@deepseek-ai/cordis' {
|
|
@@ -17,6 +17,8 @@ declare module '@deepseek-ai/cordis' {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
const KEEP_ALIVE_MS = 15_000
|
|
21
|
+
|
|
20
22
|
/** Stable Cordis plugin name, equal to the package name and the Loader row id. */
|
|
21
23
|
export const name = 'acryl-state'
|
|
22
24
|
|
|
@@ -70,15 +72,29 @@ export class State extends Service {
|
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
/**
|
|
73
|
-
* The current value of `key` as a server-sent event stream: the value now, then one event per change, until the
|
|
74
|
-
*
|
|
75
|
+
* The current value of `key` as a server-sent event stream: the value now, then one event per change, until the client cancels or the router
|
|
76
|
+
* is disposed (both abort the stream). A comment line goes out every 15 seconds so proxies and browsers keep an idle stream open.
|
|
75
77
|
*/
|
|
76
|
-
stream(key: string, c:
|
|
78
|
+
stream(key: string, c: RequestContext): Response | undefined {
|
|
77
79
|
const cell = this.find(key)
|
|
78
80
|
if (cell === undefined) return undefined
|
|
79
|
-
return streamSSE(c, stream => {
|
|
80
|
-
stream.
|
|
81
|
-
|
|
81
|
+
return streamSSE(c, async stream => {
|
|
82
|
+
const ended = new Promise<void>(resolve => { stream.onAbort(resolve) })
|
|
83
|
+
// Writes are asynchronous; chaining them keeps events in the order the cell changed.
|
|
84
|
+
let queue: Promise<unknown> = Promise.resolve()
|
|
85
|
+
const send = (value: unknown): void => {
|
|
86
|
+
queue = queue.then(() => stream.writeSSE({ event: 'value', data: typeof value === 'string' ? value : JSON.stringify(value) })).catch(() => {})
|
|
87
|
+
}
|
|
88
|
+
send(cell.get())
|
|
89
|
+
const unsubscribe = cell.subscribe(send)
|
|
90
|
+
const keepAlive = setInterval(() => { queue = queue.then(() => stream.write(': keepalive\n\n')).catch(() => {}) }, KEEP_ALIVE_MS)
|
|
91
|
+
keepAlive.unref()
|
|
92
|
+
try {
|
|
93
|
+
await ended
|
|
94
|
+
} finally {
|
|
95
|
+
clearInterval(keepAlive)
|
|
96
|
+
unsubscribe()
|
|
97
|
+
}
|
|
82
98
|
})
|
|
83
99
|
}
|
|
84
100
|
}
|
|
@@ -88,13 +104,15 @@ export function apply(ctx: Context): void {
|
|
|
88
104
|
// The same cells on the router, read-only, for any surface; present only where a router is. A request over a socket must come from this machine to this
|
|
89
105
|
// app; the terminal calls the router in process and is the host's own code.
|
|
90
106
|
ctx.inject(['router'], inner => {
|
|
91
|
-
const api =
|
|
92
|
-
api.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
const api = new App()
|
|
108
|
+
api.use(loopbackOnly({ inProcess: 'allow' }), methodNotAllowed({ app: api }))
|
|
109
|
+
.get('/', c => c.json({ keys: state.keys() }))
|
|
110
|
+
.get('/:key', c => {
|
|
111
|
+
const cell = state.find(c.req.param('key'))
|
|
112
|
+
return cell === undefined ? c.json({ error: 'no such state' }, 404) : c.json({ key: cell.key, value: cell.get() })
|
|
113
|
+
})
|
|
114
|
+
.get('/:key/events', c => state.stream(c.req.param('key'), c) ?? c.json({ error: 'no such state' }, 404))
|
|
115
|
+
inner.router.route('/api/state', api)
|
|
98
116
|
// Over HTTP only where an adapter exists (Web and Desktop); the terminal needs none.
|
|
99
117
|
inner.inject(['routerHttp'], http => { http.routerHttp.mount('/api/state') })
|
|
100
118
|
})
|
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
"source": {
|
|
3
3
|
"repo": "acryldev/acryl",
|
|
4
4
|
"path": "plugins/acryl-state",
|
|
5
|
-
"commit": "
|
|
5
|
+
"commit": "df79b188398b6d4f8cceda35a72b88300a10a9ce"
|
|
6
6
|
},
|
|
7
7
|
"vendored": [
|
|
8
8
|
"src",
|
|
9
9
|
"tests",
|
|
10
|
-
"LICENSE"
|
|
10
|
+
"LICENSE",
|
|
11
|
+
"NOTICE"
|
|
11
12
|
],
|
|
12
13
|
"rewrittenSpecifiers": 4,
|
|
13
|
-
"digest": "sha256:
|
|
14
|
+
"digest": "sha256:8feb067a67ea2628ff31a9fd2201361be4be5a8c217be3b7169277c0ce472b69",
|
|
14
15
|
"testsNotRun": {
|
|
15
16
|
"tests/react.spec.ts": "needs react and @testing-library/react, which generated apps do not install"
|
|
16
17
|
},
|
|
17
18
|
"notice": [
|
|
18
|
-
"LICENSE"
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"NOTICE"
|
|
19
21
|
]
|
|
20
22
|
}
|
|
@@ -18,22 +18,19 @@ const PATH = /^\/[a-z0-9/-]*$/
|
|
|
18
18
|
|
|
19
19
|
export function routeSource(name: string, path: string, text: string): string {
|
|
20
20
|
return `import type { Context } from '@deepseek-ai/cordis'
|
|
21
|
+
import { App } from 'acryl-router'
|
|
21
22
|
|
|
22
23
|
/** Written by the builder: answers GET ${path}, and checks that it does before the install counts. */
|
|
23
24
|
export const name = '${name}'
|
|
24
25
|
export const inject = ['router']
|
|
25
26
|
|
|
26
|
-
interface Router {
|
|
27
|
-
get(path: string, handler: (c: { json(body: unknown, status?: number): Response }) => Response): unknown
|
|
28
|
-
request(path: string): Promise<Response>
|
|
29
|
-
}
|
|
30
27
|
export function apply(ctx: Context) {
|
|
31
|
-
//
|
|
32
|
-
|
|
28
|
+
// The app is mounted through this plugin's context, so the route is removed when the plugin is unloaded.
|
|
29
|
+
ctx.router.route('/', new App().get(${JSON.stringify(path)}, (c) => c.json({ text: ${JSON.stringify(text)} })))
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
export async function check(ctx: Context) {
|
|
36
|
-
const reply = await
|
|
33
|
+
const reply = await ctx.get('router')!.request(${JSON.stringify(path)})
|
|
37
34
|
if (!reply.ok) throw new Error(\`GET ${path} answered \${reply.status}\`)
|
|
38
35
|
}
|
|
39
36
|
`
|
|
@@ -32,7 +32,7 @@ import { type Context, type Fiber, Service } from '@deepseek-ai/cordis'
|
|
|
32
32
|
import type {} from '../instance/index.ts'
|
|
33
33
|
import type { Sandbox } from '../sandbox/index.ts'
|
|
34
34
|
import { appendLedger, type LedgerEntry, readLedger } from './ledger.ts'
|
|
35
|
-
import { resolveFromApp } from './resolve.ts'
|
|
35
|
+
import { resolveFromApp, routerAliases } from './resolve.ts'
|
|
36
36
|
import { hashFolder, prune, stage } from './stage.ts'
|
|
37
37
|
|
|
38
38
|
/** A fiber's lifecycle state, by name (Cordis's own enum is a `const enum`, which does not exist at run time). */
|
|
@@ -221,7 +221,7 @@ export class Extensions extends Service {
|
|
|
221
221
|
this.ledgerFile = join(home, 'extensions-ledger.jsonl')
|
|
222
222
|
// Effects run their disposers newest first: settle pending operations, then dispose every mounted extension, then drop the hook.
|
|
223
223
|
ctx.effect(
|
|
224
|
-
() => resolveFromApp(this.stageRoot, root),
|
|
224
|
+
() => resolveFromApp(this.stageRoot, root, routerAliases()),
|
|
225
225
|
'extensions: resolve staged imports from the app',
|
|
226
226
|
)
|
|
227
227
|
ctx.effect(
|
|
@@ -3,20 +3,45 @@
|
|
|
3
3
|
* `@deepseek-ai/cordis` would not find the app's `node_modules`. This resolve hook leaves resolution alone, and only when it fails for a
|
|
4
4
|
* module inside the staging folder, resolves the same name from the app's project folder. Uses Node's public `module.registerHooks`
|
|
5
5
|
* (ACRYL needed the same fallback on Deno); the returned function removes the hook.
|
|
6
|
+
*
|
|
7
|
+
* The framework plugins are not packages in an app (they are source under `src/plugins/`), so `aliases` names the files a framework specifier such as
|
|
8
|
+
* `acryl-router` stands for. A staged plugin then imports the same module the app itself runs, so `App` is the very class the router mounts.
|
|
6
9
|
*/
|
|
7
|
-
import { mkdirSync, realpathSync } from 'node:fs'
|
|
10
|
+
import { existsSync, mkdirSync, realpathSync } from 'node:fs'
|
|
8
11
|
import { registerHooks } from 'node:module'
|
|
9
12
|
import { join } from 'node:path'
|
|
10
|
-
import { pathToFileURL } from 'node:url'
|
|
13
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
11
14
|
|
|
12
15
|
const isBare = (specifier: string) => !/^[./]/.test(specifier) && !specifier.includes(':')
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
/** The router's specifiers, as files of the vendored plugin that sits beside this one; empty when the app has no router. */
|
|
18
|
+
export function routerAliases(): Record<string, string> {
|
|
19
|
+
const src = new URL('../acryl-router/src/', import.meta.url)
|
|
20
|
+
const files = {
|
|
21
|
+
'acryl-router': 'index.ts',
|
|
22
|
+
'acryl-router/middleware': 'middleware/index.ts',
|
|
23
|
+
'acryl-router/helpers': 'helpers/index.ts',
|
|
24
|
+
}
|
|
25
|
+
return Object.fromEntries(
|
|
26
|
+
Object.entries(files)
|
|
27
|
+
.map(([specifier, file]) => [specifier, fileURLToPath(new URL(file, src))] as const)
|
|
28
|
+
.filter(([, path]) => existsSync(path)),
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function resolveFromApp(
|
|
33
|
+
stageRoot: string,
|
|
34
|
+
appRoot: string,
|
|
35
|
+
aliases: Record<string, string> = {},
|
|
36
|
+
): () => void {
|
|
15
37
|
mkdirSync(stageRoot, { recursive: true })
|
|
16
38
|
const staged = pathToFileURL(`${realpathSync(stageRoot)}/`).href
|
|
17
39
|
const parentURL = pathToFileURL(join(realpathSync(appRoot), 'package.json')).href
|
|
18
40
|
const hook = registerHooks({
|
|
19
41
|
resolve(specifier, context, nextResolve) {
|
|
42
|
+
const alias = aliases[specifier]
|
|
43
|
+
if (alias !== undefined && context.parentURL?.startsWith(staged))
|
|
44
|
+
return nextResolve(pathToFileURL(alias).href, context)
|
|
20
45
|
try {
|
|
21
46
|
return nextResolve(specifier, context)
|
|
22
47
|
} catch (error) {
|
package/plugins/openai/prompt.ts
CHANGED
|
@@ -10,26 +10,28 @@ To add a capability:
|
|
|
10
10
|
A plugin's index.ts is TypeScript that Node runs directly (erasable syntax only: no enums, no parameter properties):
|
|
11
11
|
|
|
12
12
|
import type { Context } from '@deepseek-ai/cordis'
|
|
13
|
+
import { App } from 'acryl-router'
|
|
13
14
|
export const name = 'hello' // must equal the folder name
|
|
14
15
|
export const inject = ['router'] // services it needs; it waits (pending) until they exist
|
|
15
|
-
interface Router { get(path: string, handler: (c: { json(body: unknown, status?: number): Response }) => Response): unknown; request(path: string): Promise<Response> }
|
|
16
16
|
export function apply(ctx: Context) {
|
|
17
|
-
|
|
17
|
+
ctx.router.route('/', new App().get('/hello', (c) => c.json({ text: 'Hello' }))) // removed when the plugin is unloaded
|
|
18
18
|
}
|
|
19
19
|
export async function check(ctx: Context) { // optional: must pass, or the install is rolled back
|
|
20
|
-
const reply = await
|
|
20
|
+
const reply = await ctx.get('router')!.request('/hello')
|
|
21
21
|
if (!reply.ok) throw new Error('GET /hello failed')
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
The router: router.get/post/put/delete(path, handler)
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
The router: build an App (import { App } from 'acryl-router'), register routes on it with app.get/post/put/patch/delete(path, handler) and mount it with
|
|
25
|
+
ctx.router.route(prefix, app). A path parameter is :id and is read with c.req.param('id'); c.json(body, status) answers JSON, c.text(...) answers text,
|
|
26
|
+
await c.req.json() reads a JSON body, throw new HTTPException(404, { message }) (also from 'acryl-router') answers an error. Routes added to an app
|
|
27
|
+
after route() are not served: build the whole app first. ctx.get('router')!.request('/path', { method: 'POST', body: ... }) calls a route in process (use it in
|
|
28
|
+
check). The mount goes away when the plugin is unloaded.
|
|
27
29
|
|
|
28
30
|
For a capability that is a pure function of data (parse, convert, compute), write a sandboxed TOOL instead of a plugin. It cannot touch the
|
|
29
31
|
host: it runs in a separate process that can read only its own folder. Files: index.ts exporting run(input) and tool.json:
|
|
30
32
|
{ "description": "what it does and its input", "examples": [ { "input": {...}, "output": {...} } ] }
|
|
31
33
|
The examples are run in the sandbox at install and must match exactly, or the install is refused. Use write_plugin and install_plugin for tools too.
|
|
32
34
|
|
|
33
|
-
Rules: import only relative files, node: built-ins
|
|
35
|
+
Rules: import only relative files, node: built-ins, @deepseek-ai/cordis and acryl-router. Provide services with ctx.provide(name, value) and
|
|
34
36
|
use others with ctx.get(name). Put timers, listeners and routes inside ctx.effect(() => cleanup). Use list_plugins and
|
|
35
37
|
read_plugin to see what exists. When you are done, answer in one or two plain sentences saying what you built.`
|
package/plugins/tsconfig.json
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
4
|
-
"lib": ["
|
|
3
|
+
"target": "ES2024",
|
|
4
|
+
"lib": ["ES2024", "DOM", "DOM.Iterable"],
|
|
5
5
|
"module": "NodeNext",
|
|
6
6
|
"moduleResolution": "NodeNext",
|
|
7
7
|
"strict": true,
|
|
8
|
-
"noUncheckedIndexedAccess": true,
|
|
9
|
-
"exactOptionalPropertyTypes": true,
|
|
10
8
|
"noImplicitOverride": true,
|
|
11
9
|
"verbatimModuleSyntax": true,
|
|
12
10
|
"erasableSyntaxOnly": true,
|
|
@@ -17,6 +15,9 @@
|
|
|
17
15
|
},
|
|
18
16
|
"include": ["*/**/*.ts"],
|
|
19
17
|
"exclude": [
|
|
18
|
+
"acryl-*/src/**/*.test.ts",
|
|
19
|
+
"acryl-*/src/**/*.case.ts",
|
|
20
|
+
"acryl-node-server/tests/adapter",
|
|
20
21
|
"acryl-router/tests/loader.spec.ts",
|
|
21
22
|
"acryl-state/src/react.ts",
|
|
22
23
|
"acryl-state/tests/react.spec.ts",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { App } from './plugins/acryl-router/src/index.ts'
|
|
3
|
+
import { bodyLimit } from './plugins/acryl-router/src/middleware/index.ts'
|
|
4
4
|
import type {} from './plugins/agent/index.ts'
|
|
5
5
|
import type {} from './plugins/extensions/index.ts'
|
|
6
6
|
import type {} from './plugins/tasks/index.ts'
|
|
@@ -10,44 +10,54 @@ export const APP_NAME = '__APP_NAME__'
|
|
|
10
10
|
/**
|
|
11
11
|
* The app's own routes on the router: `POST /ask` for the agent; `GET /extensions` and `GET /tasks` to observe what it did (every run is a
|
|
12
12
|
* durable task owning one task per tool call); `POST /tasks/cancel`. What changes is also streamed: `GET /api/state/tasks/events` and
|
|
13
|
-
* `GET /api/state/extensions/events`.
|
|
13
|
+
* `GET /api/state/extensions/events`. The mount is removed when the plugin is disposed.
|
|
14
14
|
*/
|
|
15
15
|
export const routes = {
|
|
16
16
|
name: 'routes',
|
|
17
17
|
inject: ['router', 'agent', 'extensions', 'tasks'],
|
|
18
18
|
apply(ctx: Context) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
c.json(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
})
|
|
28
|
-
|
|
19
|
+
// A body here is a name, an id or a question; anything bigger is refused before it is read.
|
|
20
|
+
const json = async (c: {
|
|
21
|
+
req: { json(): Promise<unknown> }
|
|
22
|
+
}): Promise<Record<string, unknown>> => {
|
|
23
|
+
const body = await c.req.json().catch(() => undefined)
|
|
24
|
+
return typeof body === 'object' && body !== null ? (body as Record<string, unknown>) : {}
|
|
25
|
+
}
|
|
26
|
+
const app = new App()
|
|
27
|
+
.use(bodyLimit({ maxSize: 64 * 1024 }))
|
|
28
|
+
.get('/', (c) => c.json({ app: APP_NAME, ok: true }))
|
|
29
|
+
.get('/health', (c) => c.json({ ok: true }))
|
|
30
|
+
.get('/extensions', (c) =>
|
|
31
|
+
c.json({
|
|
32
|
+
installed: ctx.extensions.list(),
|
|
33
|
+
notInstalled: ctx.extensions.pending(),
|
|
34
|
+
waitingForApproval: ctx.extensions.approvals(),
|
|
35
|
+
}),
|
|
36
|
+
)
|
|
29
37
|
// The owner's decision on an install that is waiting (the `approval` parameter in blend.yaml).
|
|
30
38
|
for (const [path, decide] of [
|
|
31
39
|
['/extensions/approve', (name: string) => ctx.extensions.approve(name)],
|
|
32
40
|
['/extensions/deny', (name: string) => ctx.extensions.deny(name)],
|
|
33
41
|
] as const) {
|
|
34
|
-
|
|
35
|
-
const name =
|
|
42
|
+
app.post(path, async (c) => {
|
|
43
|
+
const { name } = await json(c)
|
|
36
44
|
if (typeof name !== 'string') return c.json({ error: 'name must be a string' }, 400)
|
|
37
45
|
return c.json(await decide(name))
|
|
38
46
|
})
|
|
39
47
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
app
|
|
49
|
+
.get('/tasks', (c) => c.json(ctx.tasks.list().slice(0, 100)))
|
|
50
|
+
.post('/tasks/cancel', async (c) => {
|
|
51
|
+
const { id } = await json(c)
|
|
52
|
+
if (typeof id !== 'string' || !ctx.tasks.get(id))
|
|
53
|
+
return c.json({ error: 'no such task' }, 404)
|
|
54
|
+
return c.json({ cancelled: ctx.tasks.cancel(id) })
|
|
55
|
+
})
|
|
56
|
+
.post('/ask', async (c) => {
|
|
57
|
+
const { question } = await json(c)
|
|
58
|
+
if (typeof question !== 'string') return c.json({ error: 'question must be a string' }, 400)
|
|
59
|
+
return c.json(await ctx.agent.run(question))
|
|
60
|
+
})
|
|
61
|
+
ctx.router.route('/', app)
|
|
52
62
|
},
|
|
53
63
|
}
|
|
@@ -10,11 +10,12 @@ import { pinnedInstance, withPort } from '../src/plugins/instance/index.ts'
|
|
|
10
10
|
|
|
11
11
|
/** A plugin no scripted command knows: the model writes it from scratch. */
|
|
12
12
|
const COUNTER = `import type { Context } from '@deepseek-ai/cordis'
|
|
13
|
+
import { App } from 'acryl-router'
|
|
13
14
|
export const name = 'counter'
|
|
14
15
|
export const inject = ['router']
|
|
15
16
|
export function apply(ctx: Context) {
|
|
16
17
|
let count = 0
|
|
17
|
-
ctx.router.get('/count', (c) => c.json({ count: ++count }))
|
|
18
|
+
ctx.router.route('/', new App().get('/count', (c) => c.json({ count: ++count })))
|
|
18
19
|
}
|
|
19
20
|
`
|
|
20
21
|
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
4
|
-
"lib": ["
|
|
3
|
+
"target": "ES2024",
|
|
4
|
+
"lib": ["ES2024", "DOM", "DOM.Iterable"],
|
|
5
5
|
"module": "NodeNext",
|
|
6
6
|
"moduleResolution": "NodeNext",
|
|
7
7
|
"strict": true,
|
|
8
|
-
"noUncheckedIndexedAccess": true,
|
|
9
|
-
"exactOptionalPropertyTypes": true,
|
|
10
8
|
"noImplicitOverride": true,
|
|
11
9
|
"verbatimModuleSyntax": true,
|
|
12
10
|
"erasableSyntaxOnly": true,
|
|
13
11
|
"allowImportingTsExtensions": true,
|
|
14
12
|
"noEmit": true,
|
|
15
13
|
"skipLibCheck": true,
|
|
16
|
-
"types": ["node"]
|
|
14
|
+
"types": ["node"],
|
|
15
|
+
"paths": {
|
|
16
|
+
"acryl-router": ["./src/plugins/acryl-router/src/index.ts"],
|
|
17
|
+
"acryl-router/middleware": ["./src/plugins/acryl-router/src/middleware/index.ts"],
|
|
18
|
+
"acryl-router/helpers": ["./src/plugins/acryl-router/src/helpers/index.ts"]
|
|
19
|
+
}
|
|
17
20
|
},
|
|
18
21
|
"include": ["src", "test", "extensions"]
|
|
19
22
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
-
import
|
|
2
|
+
import { App } from './plugins/acryl-router/src/index.ts'
|
|
3
3
|
|
|
4
4
|
export const APP_NAME = '__APP_NAME__'
|
|
5
5
|
|
|
6
|
-
/** The app's own routes on the router.
|
|
6
|
+
/** The app's own routes: an `App` mounted on the router. The mount is removed when this plugin is disposed. */
|
|
7
7
|
export const routes = {
|
|
8
8
|
name: 'routes',
|
|
9
9
|
inject: ['router'],
|
|
10
10
|
apply(ctx: Context) {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const app = new App()
|
|
12
|
+
.get('/', (c) => c.json({ app: APP_NAME, ok: true }))
|
|
13
|
+
.get('/health', (c) => c.json({ ok: true }))
|
|
14
|
+
ctx.router.route('/', app)
|
|
13
15
|
},
|
|
14
16
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
4
|
-
"lib": ["
|
|
3
|
+
"target": "ES2024",
|
|
4
|
+
"lib": ["ES2024", "DOM", "DOM.Iterable"],
|
|
5
5
|
"module": "NodeNext",
|
|
6
6
|
"moduleResolution": "NodeNext",
|
|
7
7
|
"strict": true,
|
|
8
|
-
"noUncheckedIndexedAccess": true,
|
|
9
|
-
"exactOptionalPropertyTypes": true,
|
|
10
8
|
"noImplicitOverride": true,
|
|
11
9
|
"verbatimModuleSyntax": true,
|
|
12
10
|
"erasableSyntaxOnly": true,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The page: plain HTML and a few lines of script; it follows the list through the server-sent event stream. */
|
|
2
2
|
export const PAGE = `<!doctype html>
|
|
3
3
|
<meta charset="utf-8"><title>Todo</title>
|
|
4
|
-
<style>body{font:16px system-ui;max-width:32rem;margin:2rem auto}li{display:flex;gap:.5rem;padding:.25rem 0}.done span{text-decoration:line-through;opacity:.6}</style>
|
|
4
|
+
<style>body{font:16px system-ui;max-width:32rem;margin:2rem auto}ul{list-style:none;padding:0}li{display:flex;gap:.5rem;padding:.25rem 0}.done span{text-decoration:line-through;opacity:.6}</style>
|
|
5
5
|
<h1>Todo</h1>
|
|
6
6
|
<form id="add"><input id="title" placeholder="What needs doing?" autofocus> <button>Add</button></form>
|
|
7
7
|
<ul id="list"></ul>
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
|
|
7
7
|
import { dirname } from 'node:path'
|
|
8
8
|
import type { Context } from '@deepseek-ai/cordis'
|
|
9
|
-
import {
|
|
10
|
-
import { loopbackOnly } from './plugins/acryl-router/src/middleware/index.ts'
|
|
11
|
-
import {
|
|
9
|
+
import { App, HTTPException } from './plugins/acryl-router/src/index.ts'
|
|
10
|
+
import { bodyLimit, loopbackOnly } from './plugins/acryl-router/src/middleware/index.ts'
|
|
11
|
+
import { schemaValidator, streamSSE, type StandardSchema } from './plugins/acryl-router/src/helpers/index.ts'
|
|
12
12
|
import type {} from './plugins/acryl-state/src/index.ts'
|
|
13
13
|
import { PAGE } from './page.ts'
|
|
14
14
|
|
|
@@ -31,6 +31,19 @@ const NewTodo: StandardSchema<{ title: string }> = {
|
|
|
31
31
|
},
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/** Standard Schema for a change to a todo. */
|
|
35
|
+
const TodoChange: StandardSchema<{ done: boolean }> = {
|
|
36
|
+
'~standard': {
|
|
37
|
+
validate: value => {
|
|
38
|
+
const done = (value as { done?: unknown } | null)?.done
|
|
39
|
+
return typeof done === 'boolean' ? { value: { done } } : { issues: [{ message: 'done must be true or false', path: ['done'] }] }
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A todo body is small; anything bigger is refused before it is read. */
|
|
45
|
+
const MAX_BODY_BYTES = 16 * 1024
|
|
46
|
+
|
|
34
47
|
/** A missing file is a new list. Any other failure (unreadable, corrupt, half-written) refuses to start: starting empty would overwrite the user's real list on the first save. */
|
|
35
48
|
function load(file: string | undefined): Todo[] {
|
|
36
49
|
if (file === undefined) return []
|
|
@@ -65,32 +78,46 @@ export function apply(ctx: Context, config: Config = {}): void {
|
|
|
65
78
|
}
|
|
66
79
|
const nextId = (): number => todos.get().reduce((max, todo) => Math.max(max, todo.id), 0) + 1
|
|
67
80
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
81
|
+
const find = (id: number): Todo => {
|
|
82
|
+
const todo = todos.get().find(item => item.id === id)
|
|
83
|
+
if (todo === undefined) throw new HTTPException(404, { message: 'no such todo' })
|
|
84
|
+
return todo
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const api = new App()
|
|
88
|
+
.use(loopbackOnly({ inProcess: 'allow' }), bodyLimit({ maxSize: MAX_BODY_BYTES }))
|
|
89
|
+
.get('/', c => c.html(PAGE))
|
|
90
|
+
.get('/api', c => c.json(todos.get()))
|
|
91
|
+
.post('/api', schemaValidator('json', NewTodo), c => {
|
|
92
|
+
const todo: Todo = { id: nextId(), title: c.req.valid('json').title, done: false }
|
|
93
|
+
todos.update(list => [...list, todo])
|
|
94
|
+
return c.json(todo, 201)
|
|
95
|
+
})
|
|
96
|
+
.patch('/api/:id', schemaValidator('json', TodoChange), c => {
|
|
97
|
+
const { id } = find(Number(c.req.param('id')))
|
|
98
|
+
const { done } = c.req.valid('json')
|
|
99
|
+
todos.update(list => list.map(todo => todo.id === id ? { ...todo, done } : todo))
|
|
100
|
+
return c.json(find(id))
|
|
101
|
+
})
|
|
102
|
+
.delete('/api/:id', c => {
|
|
103
|
+
const { id } = find(Number(c.req.param('id')))
|
|
104
|
+
todos.update(list => list.filter(todo => todo.id !== id))
|
|
105
|
+
return c.body(null, 204)
|
|
106
|
+
})
|
|
107
|
+
.get('/events', c => streamSSE(c, async stream => {
|
|
108
|
+
const ended = new Promise<void>(resolve => { stream.onAbort(resolve) })
|
|
109
|
+
// Writes are asynchronous; chaining them keeps events in the order the list changed.
|
|
110
|
+
let queue: Promise<unknown> = Promise.resolve()
|
|
111
|
+
const send = (list: readonly Todo[]): void => { queue = queue.then(() => stream.writeSSE({ data: JSON.stringify(list) })).catch(() => {}) }
|
|
112
|
+
send(todos.get())
|
|
113
|
+
const unsubscribe = todos.subscribe(send)
|
|
114
|
+
try {
|
|
115
|
+
await ended
|
|
116
|
+
} finally {
|
|
117
|
+
unsubscribe()
|
|
118
|
+
}
|
|
119
|
+
}))
|
|
120
|
+
ctx.router.route('/todo', api)
|
|
94
121
|
|
|
95
122
|
ctx.inject(['routerHttp'], inner => { inner.routerHttp.mount('/todo') })
|
|
96
123
|
}
|