acrylblends 1.1.0 → 1.2.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/README.md +9 -0
- package/dist/index.d.ts +45 -1
- package/dist/index.js +1980 -27
- package/dist/index.js.map +1 -1
- package/docs/framework/agents/index.md +1 -1
- package/docs/framework/getting-started/desktop.md +23 -4
- package/docs/framework/guides/create-acrylblends.md +1 -1
- package/docs/framework/workshop/README.md +138 -152
- package/docs/framework/workshop/facilitator.md +69 -101
- package/legacy/README.md +9 -0
- package/legacy/agent/157ce3db1112/src/app.ts +98 -0
- package/legacy/agent/157ce3db1112/src/routes.ts +63 -0
- package/legacy/agent/24c681a00fca/src/app.ts +160 -0
- package/legacy/agent/24c681a00fca/src/routes.ts +83 -0
- package/legacy/agent/2e790da977b2/src/app.ts +98 -0
- package/legacy/agent/2e790da977b2/src/routes.ts +63 -0
- package/legacy/agent/6307045545dc/src/app.ts +168 -0
- package/legacy/agent/6307045545dc/src/routes.ts +83 -0
- package/legacy/agent/6a5fb9c15c53/src/app.ts +158 -0
- package/legacy/agent/6a5fb9c15c53/src/routes.ts +83 -0
- package/legacy/agent/71fb54a5a942/src/app.ts +111 -0
- package/legacy/agent/71fb54a5a942/src/routes.ts +63 -0
- package/legacy/agent/74ece148269c/src/app.ts +168 -0
- package/legacy/agent/74ece148269c/src/routes.ts +93 -0
- package/legacy/agent/855359304707/src/app.ts +168 -0
- package/legacy/agent/855359304707/src/routes.ts +126 -0
- package/legacy/agent/b5ef3c786ae4/src/app.ts +127 -0
- package/legacy/agent/b5ef3c786ae4/src/routes.ts +76 -0
- package/legacy/agent/c2f30958043b/src/app.ts +98 -0
- package/legacy/agent/c2f30958043b/src/routes.ts +53 -0
- package/legacy/blank/5a91c558af22/src/routes.ts +18 -0
- package/legacy/blank/9c650cf52bf3/src/routes.ts +14 -0
- package/legacy/blank/aea589283445/src/routes.ts +16 -0
- package/legacy/blank/b2cbb759c889/src/routes.ts +16 -0
- package/legacy/blank/d6d371c9a65e/src/routes.ts +18 -0
- package/legacy/chat/7076cc821e2c/src/app.ts +72 -0
- package/legacy/chat/7076cc821e2c/src/routes.ts +13 -0
- package/legacy/chat/7dbe6d5170df/src/app.ts +107 -0
- package/legacy/chat/7dbe6d5170df/src/routes.ts +19 -0
- package/legacy/chat/a9a9ab44c58e/src/app.ts +107 -0
- package/legacy/chat/a9a9ab44c58e/src/routes.ts +15 -0
- package/legacy/chat/f20ad9a51b06/src/app.ts +106 -0
- package/legacy/chat/f20ad9a51b06/src/routes.ts +15 -0
- package/legacy/layouts.json +524 -0
- package/legacy/nodejs/1d477f37aaf4/src/app.ts +92 -0
- package/legacy/nodejs/1d477f37aaf4/src/routes.ts +40 -0
- package/legacy/nodejs/b1bd9462496c/src/app.ts +61 -0
- package/legacy/nodejs/b1bd9462496c/src/routes.ts +37 -0
- package/legacy/nodejs/d5bab75076ef/src/app.ts +95 -0
- package/legacy/nodejs/d5bab75076ef/src/routes.ts +40 -0
- package/legacy/notes/4edc36949c15/src/app.ts +107 -0
- package/legacy/notes/4edc36949c15/src/routes.ts +14 -0
- package/legacy/notes/6cab2d09747c/src/app.ts +72 -0
- package/legacy/notes/6cab2d09747c/src/routes.ts +12 -0
- package/legacy/notes/7ca2dfed623a/src/app.ts +106 -0
- package/legacy/notes/7ca2dfed623a/src/routes.ts +14 -0
- package/legacy/todo/02e613b3625c/src/app.ts +71 -0
- package/legacy/todo/02e613b3625c/src/todo.ts +123 -0
- package/legacy/todo/45694a8d9e15/src/app.ts +71 -0
- package/legacy/todo/45694a8d9e15/src/todo.ts +96 -0
- package/legacy/todo/5923d3a6099f/src/app.ts +71 -0
- package/legacy/todo/5923d3a6099f/src/todo.ts +125 -0
- package/legacy/todo/849a18d66dc1/src/app.ts +91 -0
- package/legacy/todo/849a18d66dc1/src/todo.ts +125 -0
- package/legacy/todo/8e64211312f3/src/app.ts +71 -0
- package/legacy/todo/8e64211312f3/src/todo.ts +125 -0
- package/legacy/todo/b79078a87565/src/app.ts +71 -0
- package/legacy/todo/b79078a87565/src/todo.ts +125 -0
- package/lib/{audit-BFpULBCj.d.ts → audit-Bok_UNCn.d.ts} +26 -3
- package/lib/{client-modules-drj4mjhj.js → client-modules-Bkdr83jO.js} +1 -1
- package/lib/cordis/door.d.ts +42 -7
- package/lib/cordis/door.js +204 -60
- package/lib/cordis/inspect.js +1 -1
- package/lib/cordis/node-server.d.ts +101 -3
- package/lib/cordis/node-server.js +3 -3
- package/lib/cordis/router-http.d.ts +7 -1
- package/lib/cordis/router-http.js +44 -3
- package/lib/cordis/router.d.ts +1 -1
- package/lib/cordis/router.js +1 -1
- package/lib/cordis/state.js +1 -1
- package/lib/door/host.d.ts +2 -2
- package/lib/door/host.js +1 -1
- package/lib/door/page.d.ts +10 -1
- package/lib/door/page.js +46 -7
- package/lib/door/protocol.d.ts +1 -1
- package/lib/drivers.d.ts +10 -1
- package/lib/{index-DMOtA-NJ.d.ts → index-DGq7H0lb.d.ts} +11 -1
- package/lib/node-server.d.ts +7 -1
- package/lib/node-server.js +2 -2
- package/lib/{protocol-a2aqrp2X.d.ts → protocol-BeIYCgTo.d.ts} +4 -0
- package/lib/{snapshot-text-BwM6T2nG.js → snapshot-text-CXTnjxGx.js} +87 -7
- package/lib/src-CtkIX_Gg.js +427 -0
- package/lib/{src-BTBm4OPD.js → src-iBJ2ysV9.js} +77 -14
- package/lib/{test-websocket-BLeEvybj.js → test-websocket-CQOSvBth.js} +17 -7
- package/lib/{test-websocket-DyeS50Tr.d.ts → test-websocket-GUTAkFQT.d.ts} +3 -1
- package/package.json +2 -1
- package/plugins/acryl-door/src/host/channel.ts +86 -9
- package/plugins/acryl-door/src/host/chat-tools.ts +8 -6
- package/plugins/acryl-door/src/host/door-file.ts +7 -2
- package/plugins/acryl-door/src/host/door.ts +53 -23
- package/plugins/acryl-door/src/host/page-routes.ts +3 -1
- package/plugins/acryl-door/src/host/page-socket.ts +18 -8
- package/plugins/acryl-door/src/host/verbs.ts +3 -0
- package/plugins/acryl-door/src/index.ts +166 -29
- package/plugins/acryl-door/src/page/base.ts +32 -0
- package/plugins/acryl-door/src/page/channel.ts +3 -1
- package/plugins/acryl-door/src/page/driver/accessibility.ts +11 -0
- package/plugins/acryl-door/src/page/driver/actions.ts +4 -0
- package/plugins/acryl-door/src/page/driver/driver.ts +32 -4
- package/plugins/acryl-door/src/page/driver/snapshot.ts +5 -2
- package/plugins/acryl-door/src/page/index.ts +41 -2
- package/plugins/acryl-door/src/page/outline.ts +56 -0
- package/plugins/acryl-door/src/page/protocol.ts +4 -0
- package/plugins/acryl-door/src/page/snapshot-text.ts +2 -1
- package/plugins/acryl-node-server/src/adapter/listener.ts +4 -0
- package/plugins/acryl-node-server/src/adapter/raw.ts +18 -0
- package/plugins/acryl-node-server/src/adapter/websocket.ts +15 -9
- package/plugins/acryl-node-server/src/client-modules.ts +7 -1
- package/plugins/acryl-node-server/src/index.ts +304 -16
- package/plugins/acryl-node-server/src/upgrade-hub.ts +2 -2
- package/plugins/acryl-page/plugin.json +12 -0
- package/plugins/acryl-page/src/index.ts +163 -0
- package/plugins/acryl-router/src/service.ts +77 -14
- package/plugins/acryl-router-http/src/index.ts +15 -1
- package/plugins/acryl-router-http/src/serve.ts +31 -0
- package/plugins/acryl-web-server/plugin.json +14 -0
- package/plugins/acryl-web-server/src/index.ts +164 -0
- package/plugins/agent/agent.ts +38 -7
- package/plugins/agent/index.ts +1 -1
- package/plugins/agent/model.ts +10 -2
- package/plugins/agent/tools.ts +9 -3
- package/plugins/agent-panel/client/describe.ts +57 -0
- package/plugins/agent-panel/client/driver-pick.ts +22 -0
- package/plugins/agent-panel/client/frame.ts +51 -0
- package/plugins/agent-panel/client/main.ts +238 -26
- package/plugins/agent-panel/drivers.ts +7 -2
- package/plugins/agent-panel/headless.ts +5 -2
- package/plugins/agent-panel/index.ts +146 -57
- package/plugins/agent-panel/mount.ts +27 -0
- package/plugins/agent-panel/visitors.ts +231 -0
- package/plugins/desktop/frame.ts +231 -0
- package/plugins/desktop/launch.ts +44 -9
- package/plugins/desktop/main.ts +100 -32
- package/plugins/desktop/window.ts +21 -4
- package/plugins/driver-pty/client/terminal.ts +4 -1
- package/plugins/driver-pty/index.ts +38 -11
- package/plugins/driver-pty/spawn.ts +38 -0
- package/plugins/driver-pty/terminal.ts +2 -0
- package/plugins/extensions/index.ts +26 -1
- package/plugins/extensions/resolve.ts +13 -0
- package/plugins/feedback/client.d.ts +22 -0
- package/plugins/feedback/client.js +471 -0
- package/plugins/feedback/index.ts +143 -0
- package/plugins/feedback/plugin.json +11 -0
- package/plugins/feedback/store.ts +307 -0
- package/plugins/home/index.ts +122 -0
- package/plugins/home/plugin.json +11 -0
- package/plugins/instance/index.ts +6 -0
- package/plugins/manifest/app-plugins.ts +86 -0
- package/plugins/manifest/index.ts +12 -2
- package/plugins/manifest/snapshot.ts +275 -0
- package/plugins/openai/index.ts +64 -5
- package/plugins/openai/prompt.ts +23 -0
- package/shared/AGENTS.core.md +36 -16
- package/shared/skills/acryl-add-ui/SKILL.md +19 -0
- package/shared/skills/acryl-app/SKILL.md +13 -0
- package/shared/skills/acryl-build-plugin/SKILL.md +22 -0
- package/shared/skills/acryl-change-plugin/SKILL.md +14 -0
- package/shared/skills/acryl-diagnose-plugin/SKILL.md +13 -0
- package/shared/skills/acryl-fix-plugin/SKILL.md +13 -0
- package/shared/skills/acryl-remove-plugin/SKILL.md +12 -0
- package/shared/skills/acryl-share-plugin/SKILL.md +16 -0
- package/shared/skills/cordis-plugin/SKILL.md +26 -0
- package/templates/agent/AGENTS.md +1 -1
- package/templates/agent/blend.yaml +4 -0
- package/templates/agent/gitignore +1 -0
- package/templates/agent/package.json +1 -0
- package/templates/agent/src/app.ts +150 -33
- package/templates/agent/src/frame.ts +17 -0
- package/templates/agent/src/main.ts +5 -3
- package/templates/agent/src/routes.ts +38 -19
- package/templates/agent/template.json +2 -0
- package/templates/agent/test/app.test.ts +45 -3
- package/templates/blank/AGENTS.md +3 -2
- package/templates/blank/blend.yaml +11 -0
- package/templates/blank/gitignore +1 -0
- package/templates/blank/package.json +1 -0
- package/templates/blank/src/app.ts +100 -29
- package/templates/blank/src/door.ts +7 -0
- package/templates/blank/src/first-page.ts +5 -0
- package/templates/blank/src/frame.ts +17 -0
- package/templates/blank/src/routes.ts +65 -8
- package/templates/blank/template.json +14 -1
- package/templates/blank/test/app.test.ts +103 -16
- package/templates/chat/blend.yaml +2 -1
- package/templates/chat/gitignore +1 -0
- package/templates/chat/package.json +1 -0
- package/templates/chat/src/app.ts +72 -22
- package/templates/chat/src/chat-app.ts +11 -2
- package/templates/chat/src/client/chat.tsx +4 -2
- package/templates/chat/src/frame.ts +17 -0
- package/templates/chat/src/page.tsx +6 -4
- package/templates/chat/src/routes.ts +14 -4
- package/templates/chat/src/runtime.ts +9 -3
- package/templates/chat/template.json +3 -1
- package/templates/chat/test/chat.test.ts +46 -1
- package/templates/chat/test/server.test.ts +17 -3
- package/templates/nodejs/blend.yaml +2 -1
- package/templates/nodejs/gitignore +1 -0
- package/templates/nodejs/package.json +1 -0
- package/templates/nodejs/src/app.ts +76 -21
- package/templates/nodejs/src/frame.ts +17 -0
- package/templates/nodejs/src/page.tsx +3 -3
- package/templates/nodejs/src/routes.ts +32 -13
- package/templates/nodejs/template.json +3 -1
- package/templates/nodejs/test/app.test.ts +38 -3
- package/templates/nodejs/test/server.test.ts +17 -3
- package/templates/notes/blend.yaml +2 -1
- package/templates/notes/gitignore +1 -0
- package/templates/notes/package.json +1 -0
- package/templates/notes/src/app.ts +72 -22
- package/templates/notes/src/frame.ts +17 -0
- package/templates/notes/src/notes-app.ts +24 -17
- package/templates/notes/src/routes.ts +11 -4
- package/templates/notes/src/runtime.ts +9 -3
- package/templates/notes/src/views.tsx +26 -17
- package/templates/notes/template.json +3 -1
- package/templates/notes/test/notes.test.ts +39 -1
- package/templates/notes/test/server.test.ts +17 -3
- package/templates/todo/blend.yaml +3 -0
- package/templates/todo/gitignore +1 -0
- package/templates/todo/package.json +1 -0
- package/templates/todo/src/app.ts +81 -27
- package/templates/todo/src/door.ts +7 -0
- package/templates/todo/src/frame.ts +17 -0
- package/templates/todo/src/page.ts +13 -6
- package/templates/todo/src/todo.ts +18 -6
- package/templates/todo/template.json +11 -1
- package/templates/todo/test/todo.test.ts +67 -9
- package/lib/src-LX0E4ec4.js +0 -117
- package/shared/SKILL.md +0 -14
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as ConnectionInfo } from "../connection-ZLEso7XL.js";
|
|
2
|
+
import { c as UpgradeHubOptions, i as testWebSocket, l as UpgradeRouter, n as TestWebSocketOptions, o as UpgradeEmitter, p as WebSocketLike, r as TestWebSocketResult, s as UpgradeHub, t as TestSocket, u as RequestError } from "../test-websocket-GUTAkFQT.js";
|
|
2
3
|
import { n as UpgradeWebSocket } from "../index-C-SklY6u.js";
|
|
3
4
|
import { n as serveStatic, t as ServeStaticOptions } from "../serve-static-B7sqnftW.js";
|
|
5
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
6
|
+
import { Duplex } from "node:stream";
|
|
4
7
|
import { Context, Service } from "@deepseek-ai/cordis";
|
|
5
8
|
import z from "@deepseek-ai/schemastery";
|
|
6
9
|
|
|
@@ -8,6 +11,24 @@ import z from "@deepseek-ai/schemastery";
|
|
|
8
11
|
/** Stable Cordis plugin name, equal to the package name and the Loader row id. */
|
|
9
12
|
declare const name = "acryl-node-server";
|
|
10
13
|
declare const inject: string[];
|
|
14
|
+
/** A raw node handler: the request and response are the platform's, and the adapter writes nothing for them. */
|
|
15
|
+
interface RawRoute {
|
|
16
|
+
readonly kind: 'prefix' | 'exact';
|
|
17
|
+
readonly path: string;
|
|
18
|
+
readonly handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/** A raw upgrade handler: the socket is the handler's once the path matches; the router never sees the upgrade. */
|
|
21
|
+
interface RawUpgradeRoute {
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly handler: (req: IncomingMessage, socket: Duplex, head: Buffer) => void | Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Answer a request before raw routes and the router, or pass it on with `undefined`. The callback runs synchronously, so a request no gate answers still
|
|
27
|
+
* reaches a raw route's sentinel synchronously; a gate that needs the router (a host keeping an app's URLs in scope under a prefix, the ACRYL frame's canvas)
|
|
28
|
+
* returns its promise, and the adapter handles that like the router's own answer. `connection` carries the socket facts, which a router mount needs to check
|
|
29
|
+
* the peer and the real port.
|
|
30
|
+
*/
|
|
31
|
+
type RequestGate = (request: Request, connection: ConnectionInfo) => Response | Promise<Response> | undefined;
|
|
11
32
|
interface Config {
|
|
12
33
|
hostname: string;
|
|
13
34
|
port: number;
|
|
@@ -22,28 +43,105 @@ declare module '@deepseek-ai/cordis' {
|
|
|
22
43
|
nodeServer: NodeServer;
|
|
23
44
|
}
|
|
24
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Milliseconds a request already in flight, or an upgraded socket's own owner, may finish once the server stops taking new work. A save during a
|
|
48
|
+
* restart must not be cut; a stream that never ends must not hold the stop open.
|
|
49
|
+
*/
|
|
50
|
+
declare const STOP_GRACE_MS = 2000;
|
|
25
51
|
declare class NodeServer extends Service {
|
|
26
52
|
readonly hostname: string;
|
|
27
53
|
readonly config: Config;
|
|
28
54
|
private readonly server;
|
|
55
|
+
private readonly router;
|
|
29
56
|
private hub;
|
|
30
57
|
private bound;
|
|
58
|
+
/** The one stop this server runs: a second call (the disposer after an explicit stop) waits on the same promise. */
|
|
59
|
+
private stopping;
|
|
60
|
+
/** Requests whose response has not finished yet: the stop waits for them before their sockets are cut. */
|
|
61
|
+
private active;
|
|
62
|
+
/** Upgraded sockets this server handed out (WebSockets): neither close() nor closeAllConnections() reaches them, so the stop tracks them itself. */
|
|
63
|
+
private readonly upgraded;
|
|
64
|
+
/** Raw routes registered by the host's compatibility layer (see {@link registerRaw}). */
|
|
65
|
+
private readonly rawExact;
|
|
66
|
+
private readonly rawPrefix;
|
|
67
|
+
private readonly rawUpgrades;
|
|
68
|
+
/** Gates registered by a host that maps an app's URLs (see {@link NodeServer.gate}); they run in the order they were added. */
|
|
69
|
+
private readonly gates;
|
|
70
|
+
/** One raw dispatch per upgrade request: a second listener (or a re-added one) must never emit the same request again. */
|
|
71
|
+
private readonly rawUpgradeSeen;
|
|
31
72
|
constructor(ctx: Context, config: Config);
|
|
73
|
+
/**
|
|
74
|
+
* Stop serving, gracefully, and resolve once the port is free: no new connections; requests already in flight may finish (a stream that never ends is
|
|
75
|
+
* cut) within {@link STOP_GRACE_MS}; each upgraded socket's owner closes it with a close frame first, and what is left is destroyed. Idempotent - the
|
|
76
|
+
* disposer calls it.
|
|
77
|
+
*
|
|
78
|
+
* Calling this BEFORE the rest of the app is disposed is what makes the grace count: the router has its own disposal, and it aborts in-flight requests
|
|
79
|
+
* and ends their bodies by design, concurrently with every other disposer (Cordis runs them together). A launcher that disposes the context directly
|
|
80
|
+
* therefore cuts the request this drains; a launcher that stops the server first lets it answer, then disposes.
|
|
81
|
+
*/
|
|
82
|
+
stop(): Promise<void>;
|
|
83
|
+
private closeGracefully;
|
|
84
|
+
/**
|
|
85
|
+
* Wait, bounded, until no request is in flight and no upgraded socket is left; true when both reached zero inside the bound. Every pass sweeps idle
|
|
86
|
+
* keep-alive sockets again: the single sweep `close()` does misses one that becomes idle after its request finished, and such a socket would otherwise
|
|
87
|
+
* hold the stop to its deadline.
|
|
88
|
+
*/
|
|
89
|
+
private settleStop;
|
|
90
|
+
/**
|
|
91
|
+
* Serve a raw node handler at `path`, beside the router: `exact` matches the pathname only, `prefix` the path and everything below it. The
|
|
92
|
+
* handler gets the platform's `IncomingMessage`/`ServerResponse` and owns the response; the router is not run for it. A path already served
|
|
93
|
+
* throws. The returned remover restores the path (the server itself goes on serving the rest).
|
|
94
|
+
*/
|
|
95
|
+
registerRaw(route: RawRoute): () => void;
|
|
96
|
+
/**
|
|
97
|
+
* Answer matching requests before raw routes and the router, so a host can keep an app's URLs in scope under a prefix (the frame's canvas). A gate runs
|
|
98
|
+
* synchronously and can answer with a promise (see {@link RequestGate}); gates run in the order they were added, and the returned remover takes this one back.
|
|
99
|
+
*/
|
|
100
|
+
gate(handler: RequestGate): () => void;
|
|
101
|
+
/**
|
|
102
|
+
* A gate's asynchronous answer: run the request it was given through the router, in the adapter's request form (a gate never makes one itself), with the
|
|
103
|
+
* socket facts. The raw routes do not run for that request.
|
|
104
|
+
*/
|
|
105
|
+
throughRouter(request: Request, connection?: ConnectionInfo): Promise<Response>;
|
|
106
|
+
/**
|
|
107
|
+
* Claim an upgrade path for a raw node handler: the handler receives `(req, socket, head)` and from then on owns the socket. The router's
|
|
108
|
+
* WebSocket handling never sees a claimed upgrade. A path already claimed throws; the remover releases the claim. The transport's one raw
|
|
109
|
+
* listener exists for the server's whole lifetime and consults this registry, so claims coming and going never add or remove a listener.
|
|
110
|
+
*/
|
|
111
|
+
registerRawUpgrade(route: RawUpgradeRoute): () => void;
|
|
112
|
+
/** The raw handler for a request: an exact path first, then the longest matching prefix. */
|
|
113
|
+
private rawFor;
|
|
32
114
|
/**
|
|
33
115
|
* `upgradeWebSocket` for the plugin that asks (call it once, in `apply`): `app.get('/ws', upgrade((c) => ({ onMessage(event, ws) { ... } })))`. Checks go in ordinary middleware on the
|
|
34
116
|
* route, and a handler that returns a `Response` refuses the upgrade with that status before any handshake. Every socket it accepts belongs to the calling plugin: when the plugin
|
|
35
117
|
* unloads they are closed with 1001 within `websocket.grace` and the handler sees `close` once. Pass `{ textOnly: true }` to drop binary frames. Needs the `ws` package.
|
|
36
118
|
*/
|
|
37
119
|
upgrades(): UpgradeWebSocket<WebSocketLike>;
|
|
120
|
+
/** How many `upgrade` listeners the transport holds (raw plus router setups); a test asserts it does not grow across claim changes. */
|
|
121
|
+
get upgradeListenerCount(): number;
|
|
38
122
|
/** How many WebSocket connections are open now. */
|
|
39
123
|
get sockets(): number;
|
|
40
124
|
/** The port in use, 0 before the server listens. */
|
|
41
125
|
get port(): number;
|
|
42
126
|
/** The address clients use. */
|
|
43
127
|
get url(): string;
|
|
44
|
-
/** Bind, scanning past taken ports. */
|
|
128
|
+
/** Bind, scanning past taken ports and past the ports a browser refuses. */
|
|
45
129
|
listen(): Promise<void>;
|
|
46
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* The ports every Fetch client refuses to connect to: the Fetch standard's bad ports (https://fetch.spec.whatwg.org/#port-blocking). Chromium
|
|
133
|
+
* refuses a page on one (ERR_UNSAFE_PORT) and Node's fetch answers "bad port"; the list below is the one Node 24 reports, probed port by port
|
|
134
|
+
* on 2026-11-03 (82 ports, 1 to 10080). An app's stable port is hashed into 3100..3999, and 3659 is on the list: without this rule one app in
|
|
135
|
+
* 900 would come up on an address no browser may open, which the frame gate caught that day (verify-frame-token was refused at 3659).
|
|
136
|
+
*/
|
|
137
|
+
declare const RESTRICTED_PORTS: ReadonlySet<number>;
|
|
138
|
+
/** Whether a browser refuses to open this port. */
|
|
139
|
+
declare function isRestrictedPort(port: number): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* The ports `listen` may try, in order: `port` with at most `scan` further ones, without the ports a browser refuses. Port 0 stays alone: the
|
|
142
|
+
* system chooses it, and the ports it hands out are above the restricted range.
|
|
143
|
+
*/
|
|
144
|
+
declare function portCandidates(port: number, scan: number): number[];
|
|
47
145
|
/**
|
|
48
146
|
* Whether a failed bind means "this port, try the next one". A port another socket holds answers `EADDRINUSE` everywhere. On Windows a port
|
|
49
147
|
* inside a range the system reserves (Hyper-V, WSL and Docker exclude ranges, `netsh interface ipv4 show excludedportrange`) answers
|
|
@@ -54,4 +152,4 @@ declare function isPortUnavailable(error: unknown, platform?: NodeJS.Platform):
|
|
|
54
152
|
/** Loads once the server listens, so a plugin that injects `nodeServer` can rely on `url`. A failure to bind fails the plugin. */
|
|
55
153
|
declare function apply(ctx: Context, config?: Partial<Config>): Promise<void>;
|
|
56
154
|
//#endregion
|
|
57
|
-
export { Config, NodeServer, RequestError, type ServeStaticOptions, type TestSocket, type TestWebSocketOptions, type TestWebSocketResult, UpgradeEmitter, UpgradeHub, type UpgradeHubOptions, type UpgradeRouter, apply, inject, isPortUnavailable, name, serveStatic, testWebSocket };
|
|
155
|
+
export { Config, NodeServer, RESTRICTED_PORTS, RawRoute, RawUpgradeRoute, RequestError, RequestGate, STOP_GRACE_MS, type ServeStaticOptions, type TestSocket, type TestWebSocketOptions, type TestWebSocketResult, UpgradeEmitter, UpgradeHub, type UpgradeHubOptions, type UpgradeRouter, apply, inject, isPortUnavailable, isRestrictedPort, name, portCandidates, serveStatic, testWebSocket };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as RequestError, n as UpgradeEmitter, r as UpgradeHub, t as testWebSocket } from "../test-websocket-CQOSvBth.js";
|
|
2
2
|
import { t as serveStatic } from "../serve-static-C1Nkj_xB.js";
|
|
3
|
-
import { a as
|
|
4
|
-
export { Config, NodeServer, RequestError, UpgradeEmitter, UpgradeHub, apply, inject, isPortUnavailable, name, serveStatic, testWebSocket };
|
|
3
|
+
import { a as apply, c as isRestrictedPort, i as STOP_GRACE_MS, l as name, n as NodeServer, o as inject, r as RESTRICTED_PORTS, s as isPortUnavailable, t as Config, u as portCandidates } from "../src-CtkIX_Gg.js";
|
|
4
|
+
export { Config, NodeServer, RESTRICTED_PORTS, RequestError, STOP_GRACE_MS, UpgradeEmitter, UpgradeHub, apply, inject, isPortUnavailable, isRestrictedPort, name, portCandidates, serveStatic, testWebSocket };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as UpgradeWebSocket } from "../index-C-SklY6u.js";
|
|
2
|
-
import { i as RouterHttpService } from "../index-
|
|
2
|
+
import { i as RouterHttpService } from "../index-DGq7H0lb.js";
|
|
3
3
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
4
4
|
import { Duplex } from "node:stream";
|
|
5
5
|
import { Context, Service } from "@deepseek-ai/cordis";
|
|
@@ -52,6 +52,10 @@ declare class RouterHttp extends Service implements RouterHttpService {
|
|
|
52
52
|
private readonly router;
|
|
53
53
|
private readonly web;
|
|
54
54
|
private hub;
|
|
55
|
+
/** One upgrade helper per calling context: a fresh wrapper per call made every service event look like an owner change. */
|
|
56
|
+
private readonly upgradeHelpers;
|
|
57
|
+
/** A stable identity for this service instance's hub: a caller that must recognize the same owner across calls keys on this, not on a helper. */
|
|
58
|
+
private readonly hubIdentity;
|
|
55
59
|
/** The adapter's own context: the hub lives as long as the adapter, whichever plugin asked for it first (`this.ctx` is the caller's). */
|
|
56
60
|
private readonly home;
|
|
57
61
|
private readonly upgradesIn;
|
|
@@ -69,6 +73,8 @@ declare class RouterHttp extends Service implements RouterHttpService {
|
|
|
69
73
|
* `upgradeWebSocket` for the calling plugin: the sockets its routes accept are closed with 1001 when it unloads. Needs the `ws` package.
|
|
70
74
|
* The route must be on a path the plugin mounted with `upgrade: [path]`.
|
|
71
75
|
*/
|
|
76
|
+
/** Stable for this service's lifetime; a re-provided adapter has a new one, which is how a caller sees a new owner. */
|
|
77
|
+
hubToken(): object;
|
|
72
78
|
upgrades(): UpgradeWebSocket;
|
|
73
79
|
/** How many WebSocket connections are open now. */
|
|
74
80
|
get sockets(): number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as UpgradeEmitter, r as UpgradeHub } from "../test-websocket-
|
|
2
|
-
import "../src-
|
|
1
|
+
import { n as UpgradeEmitter, r as UpgradeHub } from "../test-websocket-CQOSvBth.js";
|
|
2
|
+
import "../src-CtkIX_Gg.js";
|
|
3
3
|
import { Readable } from "node:stream";
|
|
4
4
|
import { Service } from "@deepseek-ai/cordis";
|
|
5
5
|
import { pipeline } from "node:stream/promises";
|
|
@@ -59,6 +59,33 @@ async function sendResponse(res, response, method) {
|
|
|
59
59
|
}
|
|
60
60
|
//#endregion
|
|
61
61
|
//#region ../../plugins/acryl-router-http/src/serve.ts
|
|
62
|
+
/** How long a refusal waits for a request body the handler never read. */
|
|
63
|
+
const REQUEST_BODY_SETTLE_MS = 1e3;
|
|
64
|
+
/**
|
|
65
|
+
* Read and discard a request body the handler left unread (a guard's 403, a 415, a body over its limit), so the client can finish sending and
|
|
66
|
+
* read the answer. Ending a response while request data is still arriving makes Node close the socket with that data pending, and the peer sees a
|
|
67
|
+
* reset instead of the answer: reliably on Windows (the workspace git worktree test's read ECONNRESET) and on other systems under load. Bounded:
|
|
68
|
+
* a client that stops sending waits at most `ms`, then loses the socket as before.
|
|
69
|
+
*/
|
|
70
|
+
function settleRequestBody(req, ms = REQUEST_BODY_SETTLE_MS) {
|
|
71
|
+
if (req.readableEnded || req.destroyed) return Promise.resolve();
|
|
72
|
+
return new Promise((resolve) => {
|
|
73
|
+
let timer;
|
|
74
|
+
const done = () => {
|
|
75
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
76
|
+
req.off("end", done);
|
|
77
|
+
req.off("close", done);
|
|
78
|
+
req.off("error", done);
|
|
79
|
+
resolve();
|
|
80
|
+
};
|
|
81
|
+
timer = setTimeout(done, ms);
|
|
82
|
+
timer.unref?.();
|
|
83
|
+
req.once("end", done);
|
|
84
|
+
req.once("close", done);
|
|
85
|
+
req.once("error", done);
|
|
86
|
+
req.resume();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
62
89
|
/**
|
|
63
90
|
* Serve one node request through the router and write the result. The request's abort signal fires when the client
|
|
64
91
|
* disconnects before the response finished; the socket's peer address and local port travel as connection info, never in the request.
|
|
@@ -78,6 +105,7 @@ async function serve(router, req, res) {
|
|
|
78
105
|
} catch {
|
|
79
106
|
response = new Response("Internal Server Error", { status: 500 });
|
|
80
107
|
}
|
|
108
|
+
if (method !== "GET" && method !== "HEAD") await settleRequestBody(req);
|
|
81
109
|
await sendResponse(res, response, method);
|
|
82
110
|
}
|
|
83
111
|
//#endregion
|
|
@@ -91,6 +119,10 @@ var RouterHttp = class extends Service {
|
|
|
91
119
|
router;
|
|
92
120
|
web;
|
|
93
121
|
hub;
|
|
122
|
+
/** One upgrade helper per calling context: a fresh wrapper per call made every service event look like an owner change. */
|
|
123
|
+
upgradeHelpers = /* @__PURE__ */ new WeakMap();
|
|
124
|
+
/** A stable identity for this service instance's hub: a caller that must recognize the same owner across calls keys on this, not on a helper. */
|
|
125
|
+
hubIdentity = {};
|
|
94
126
|
/** The adapter's own context: the hub lives as long as the adapter, whichever plugin asked for it first (`this.ctx` is the caller's). */
|
|
95
127
|
home;
|
|
96
128
|
upgradesIn = new UpgradeEmitter();
|
|
@@ -141,8 +173,17 @@ var RouterHttp = class extends Service {
|
|
|
141
173
|
* `upgradeWebSocket` for the calling plugin: the sockets its routes accept are closed with 1001 when it unloads. Needs the `ws` package.
|
|
142
174
|
* The route must be on a path the plugin mounted with `upgrade: [path]`.
|
|
143
175
|
*/
|
|
176
|
+
/** Stable for this service's lifetime; a re-provided adapter has a new one, which is how a caller sees a new owner. */
|
|
177
|
+
hubToken() {
|
|
178
|
+
return this.hubIdentity;
|
|
179
|
+
}
|
|
144
180
|
upgrades() {
|
|
145
|
-
|
|
181
|
+
const owner = this.ctx;
|
|
182
|
+
const cached = this.upgradeHelpers.get(owner);
|
|
183
|
+
if (cached !== void 0) return cached;
|
|
184
|
+
const created = this.ensureHub().upgradeWebSocketFor(owner);
|
|
185
|
+
this.upgradeHelpers.set(owner, created);
|
|
186
|
+
return created;
|
|
146
187
|
}
|
|
147
188
|
/** How many WebSocket connections are open now. */
|
|
148
189
|
get sockets() {
|
package/lib/cordis/router.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ import { B as NotFoundHandler, L as MiddlewareHandler, O as Handler, Q as Valida
|
|
|
2
2
|
import { c as StatusCode, n as ContentfulStatusCode } from "../http-status-DaAwGAFL.js";
|
|
3
3
|
import { a as getConnInfo, i as connectionOf, n as RouterBindings, r as RouterEnv, t as ConnectionInfo } from "../connection-ZLEso7XL.js";
|
|
4
4
|
import { t as Acryl } from "../app-D8riatvv.js";
|
|
5
|
-
import { a as TrailingSlash, i as RouterHttpService, n as MountableApp, o as apply, r as Router, s as name, t as Config } from "../index-
|
|
5
|
+
import { a as TrailingSlash, i as RouterHttpService, n as MountableApp, o as apply, r as Router, s as name, t as Config } from "../index-DGq7H0lb.js";
|
|
6
6
|
import { t as HTTPException } from "../http-exception-kpGJkmpi.js";
|
|
7
7
|
export { Acryl, type AcrylRequest, Config, type ConnectionInfo, type ContentfulStatusCode, Context, type ContextRenderer, type ContextVariableMap, type Env, type ErrorHandler, type ExecutionContext, HTTPException, type Handler, type Input, type MiddlewareHandler, type MountableApp, type Next, type NotFoundHandler, type NotFoundResponse, Router, type RouterBindings, type RouterEnv, type RouterHttpService, type Schema, type StatusCode, type ToSchema, type TrailingSlash, type TypedResponse, type ValidationTargets, apply, connectionOf, getConnInfo, name };
|
package/lib/cordis/router.js
CHANGED
|
@@ -2,5 +2,5 @@ import { t as HTTPException } from "../http-exception-MjWkT89V.js";
|
|
|
2
2
|
import { t as Context } from "../context-CkOh7Ygv.js";
|
|
3
3
|
import { t as Acryl } from "../app-CQcGWjrk.js";
|
|
4
4
|
import { n as getConnInfo, t as connectionOf } from "../connection-C7xgP1hP.js";
|
|
5
|
-
import { i as name, n as Router, r as apply, t as Config } from "../src-
|
|
5
|
+
import { i as name, n as Router, r as apply, t as Config } from "../src-iBJ2ysV9.js";
|
|
6
6
|
export { Acryl, Config, Context, HTTPException, Router, apply, connectionOf, getConnInfo, name };
|
package/lib/cordis/state.js
CHANGED
|
@@ -3,7 +3,7 @@ import { t as methodNotAllowed } from "../method-not-allowed-TO2_hnfl.js";
|
|
|
3
3
|
import { r as streamSSE } from "../streaming-BlDaph8U.js";
|
|
4
4
|
import { i as loopbackOnly, r as loopbackAuthority } from "../loopback-only-BbasoF7R.js";
|
|
5
5
|
import "../helpers-BEjcdbgw.js";
|
|
6
|
-
import "../src-
|
|
6
|
+
import "../src-iBJ2ysV9.js";
|
|
7
7
|
import "../middleware-BfG_fp8P.js";
|
|
8
8
|
import { Service } from "@deepseek-ai/cordis";
|
|
9
9
|
//#region ../../plugins/acryl-state/src/cell.ts
|
package/lib/door/host.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as Acryl } from "../app-D8riatvv.js";
|
|
2
|
-
import { n as AuditEntry, y as UiSnapshot } from "../protocol-
|
|
3
|
-
import { i as UiChannel, n as auditPath, r as PageStream, t as AuditLog } from "../audit-
|
|
2
|
+
import { n as AuditEntry, y as UiSnapshot } from "../protocol-BeIYCgTo.js";
|
|
3
|
+
import { i as UiChannel, n as auditPath, r as PageStream, t as AuditLog } from "../audit-Bok_UNCn.js";
|
|
4
4
|
|
|
5
5
|
//#region ../../plugins/acryl-door/src/host/page-routes.d.ts
|
|
6
6
|
/** How often the Host writes a `ping`; the page gives up on a stream that is silent for three of these. */
|
package/lib/door/host.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { d as AuditLog, f as auditPath, n as HEARTBEAT_MS, p as UiChannel, r as pageChannelRoutes, t as renderSnapshot } from "../snapshot-text-
|
|
1
|
+
import { d as AuditLog, f as auditPath, n as HEARTBEAT_MS, p as UiChannel, r as pageChannelRoutes, t as renderSnapshot } from "../snapshot-text-CXTnjxGx.js";
|
|
2
2
|
export { AuditLog, HEARTBEAT_MS, UiChannel, auditPath, pageChannelRoutes, renderSnapshot };
|
package/lib/door/page.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as UiRequest, v as UiResult } from "../protocol-
|
|
1
|
+
import { _ as UiRequest, v as UiResult } from "../protocol-BeIYCgTo.js";
|
|
2
2
|
|
|
3
3
|
//#region ../../plugins/acryl-door/src/page/driver/driver.d.ts
|
|
4
4
|
interface DriverActivity {
|
|
@@ -16,6 +16,8 @@ interface UiDriverOptions {
|
|
|
16
16
|
/** Milliseconds since the user last used the page (mouse, keyboard); Infinity when never. */
|
|
17
17
|
readonly msSinceUserInput?: () => number;
|
|
18
18
|
readonly sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
|
|
19
|
+
/** Called with the element an act is about to use, for the page to show it (the door's outline on what the agent touches). */
|
|
20
|
+
readonly touched?: (element: Element) => void;
|
|
19
21
|
/** Leave the page: reload it, or go to a path of the app. Default: the document's own `location`, a moment after the answer is sent. */
|
|
20
22
|
readonly leave?: (to: {
|
|
21
23
|
readonly reload: true;
|
|
@@ -54,6 +56,11 @@ declare class UiDriver {
|
|
|
54
56
|
/** @param signal - the Host cancelled the call. @throws UiControlError. */
|
|
55
57
|
handle(request: UiRequest, signal?: AbortSignal): Promise<UiResult>;
|
|
56
58
|
private run;
|
|
59
|
+
/**
|
|
60
|
+
* Run an act that may start a navigation (a click on a link, Enter in a form) and say so: the browser asks `beforeunload` when a navigation starts, during the act or a
|
|
61
|
+
* moment after it. The answer is sent before the page goes; the Host then waits for the next page instead of reading this one again.
|
|
62
|
+
*/
|
|
63
|
+
private watchLeaving;
|
|
57
64
|
/**
|
|
58
65
|
* The page is about to be replaced, so the answer is given first and the move happens a moment later (the answer is a POST that the page's own unload would cut off).
|
|
59
66
|
* The Host does not wait for this answer to be the last word: it waits for the page to come back.
|
|
@@ -81,6 +88,8 @@ interface ChannelSource {
|
|
|
81
88
|
declare const SILENCE_MS = 45000;
|
|
82
89
|
interface PageChannelOptions {
|
|
83
90
|
readonly windowId: string;
|
|
91
|
+
/** The browser tab the page is in (kept across the tab's reloads and moves), so the Host follows a run's page into the next document of its tab. */
|
|
92
|
+
readonly tab?: string;
|
|
84
93
|
/** The desktop window this page is in (`AcrylWindow/<n>` in the user agent, set by the Electron shell), so the Host can drive it by the number `window list` prints. */
|
|
85
94
|
readonly shellWindow?: number;
|
|
86
95
|
readonly focused: () => boolean;
|
package/lib/door/page.js
CHANGED
|
@@ -84,7 +84,7 @@ var PageChannel = class {
|
|
|
84
84
|
}
|
|
85
85
|
connect() {
|
|
86
86
|
if (this.disposed || this.source !== void 0) return;
|
|
87
|
-
const query = `window=${encodeURIComponent(this.options.windowId)}&focused=${this.options.focused() ? "1" : "0"}${this.options.shellWindow === void 0 ? "" : `&shell=${String(this.options.shellWindow)}`}`;
|
|
87
|
+
const query = `window=${encodeURIComponent(this.options.windowId)}&focused=${this.options.focused() ? "1" : "0"}${this.options.shellWindow === void 0 ? "" : `&shell=${String(this.options.shellWindow)}`}${this.options.tab === void 0 ? "" : `&tab=${encodeURIComponent(this.options.tab)}`}`;
|
|
88
88
|
const viaSocket = this.socketAllowed;
|
|
89
89
|
this.socketReady = false;
|
|
90
90
|
const source = viaSocket ? (this.options.createSocket ?? ((url) => socketSource(new WebSocket(url))))(`${socketBase(this.options.base)}/channel/ws?${query}`) : (this.options.createSource ?? ((url) => new EventSource(url)))(`${this.options.base}/channel?${query}`);
|
|
@@ -497,6 +497,15 @@ function valueOf(el, role) {
|
|
|
497
497
|
else if (el.hasAttribute("contenteditable")) value = el.textContent ?? void 0;
|
|
498
498
|
return value === void 0 || value === "" ? void 0 : clip(value, 100);
|
|
499
499
|
}
|
|
500
|
+
/** Choices of a select listed at most; a longer list ends with how many more there are. */
|
|
501
|
+
const MAX_OPTIONS = 20;
|
|
502
|
+
/** The choices of a native select (what `act select` takes), or undefined when it is not one or is sensitive. */
|
|
503
|
+
function optionsOf(el) {
|
|
504
|
+
if (!(el instanceof HTMLSelectElement) || isSensitive(el)) return void 0;
|
|
505
|
+
const labels = [...el.options].filter((option) => !option.hidden).map((option) => clip((option.textContent ?? "").trim(), 60)).filter((label) => label !== "");
|
|
506
|
+
if (labels.length <= MAX_OPTIONS) return labels;
|
|
507
|
+
return [...labels.slice(0, MAX_OPTIONS), `(${String(labels.length - MAX_OPTIONS)} more)`];
|
|
508
|
+
}
|
|
500
509
|
/** Heading level, when the element is a heading. */
|
|
501
510
|
function levelOf(el) {
|
|
502
511
|
const explicit = Number(el.getAttribute("aria-level"));
|
|
@@ -583,7 +592,7 @@ var SnapshotBuilder = class {
|
|
|
583
592
|
const root = this.root();
|
|
584
593
|
const view = root.ownerDocument.defaultView;
|
|
585
594
|
let offset = options.cursor ?? 0;
|
|
586
|
-
if (
|
|
595
|
+
if (offset === 0) {
|
|
587
596
|
this.refs.begin();
|
|
588
597
|
const all = collect(root);
|
|
589
598
|
this.candidates = [...all.filter((c) => inViewport(c.element, view)), ...all.filter((c) => !inViewport(c.element, view))];
|
|
@@ -595,6 +604,7 @@ var SnapshotBuilder = class {
|
|
|
595
604
|
if (isProtected(candidate.element)) states.push("protected");
|
|
596
605
|
const value = valueOf(candidate.element, candidate.role);
|
|
597
606
|
const level = candidate.role === "heading" ? levelOf(candidate.element) : void 0;
|
|
607
|
+
const options = optionsOf(candidate.element);
|
|
598
608
|
return {
|
|
599
609
|
ref: this.refs.issue(candidate.element, candidate.role, candidate.name),
|
|
600
610
|
role: candidate.role,
|
|
@@ -602,7 +612,8 @@ var SnapshotBuilder = class {
|
|
|
602
612
|
depth: candidate.depth,
|
|
603
613
|
states,
|
|
604
614
|
...value === void 0 ? {} : { value },
|
|
605
|
-
...level === void 0 ? {} : { level }
|
|
615
|
+
...level === void 0 ? {} : { level },
|
|
616
|
+
...options === void 0 ? {} : { options }
|
|
606
617
|
};
|
|
607
618
|
});
|
|
608
619
|
const end = offset + page.length;
|
|
@@ -645,6 +656,7 @@ function prepare(env, ref) {
|
|
|
645
656
|
if (isProtected(element)) throw new UiControlError("protected", `"${resolved.name}" belongs to approval, permissions or Agent Control itself; only the user operates it`);
|
|
646
657
|
if (effectivelyHidden(element)) throw new UiControlError("not-actionable", `"${resolved.name}" is not visible`);
|
|
647
658
|
if (element.disabled === true || element.getAttribute("aria-disabled") === "true") throw new UiControlError("not-actionable", `"${resolved.name}" is disabled`);
|
|
659
|
+
env.touched?.(element);
|
|
648
660
|
return resolved;
|
|
649
661
|
}
|
|
650
662
|
function mouse(element, type) {
|
|
@@ -786,6 +798,7 @@ function scroll(env, request) {
|
|
|
786
798
|
const dy = request.direction === "up" ? -amount : request.direction === "down" ? amount : 0;
|
|
787
799
|
if (request.ref !== void 0) {
|
|
788
800
|
const { element, role, name } = env.refs.resolve(request.ref);
|
|
801
|
+
env.touched?.(element);
|
|
789
802
|
element.scrollBy?.({
|
|
790
803
|
left: dx,
|
|
791
804
|
top: dy
|
|
@@ -914,6 +927,8 @@ var RefTable = class {
|
|
|
914
927
|
/** After the user touches the page, agent actions wait this long, so the user always wins. */
|
|
915
928
|
const YIELD_MS = 400;
|
|
916
929
|
const MAX_YIELD_MS = 2e3;
|
|
930
|
+
/** How long after an act a navigation it started is still noticed (a link's navigation starts a task or two after the click). */
|
|
931
|
+
const LEAVE_WATCH_MS = 50;
|
|
917
932
|
function realSleep(ms, signal) {
|
|
918
933
|
return new Promise((resolve, reject) => {
|
|
919
934
|
if (signal.aborted) {
|
|
@@ -955,7 +970,8 @@ var UiDriver = class {
|
|
|
955
970
|
this.env = {
|
|
956
971
|
refs: this.refs,
|
|
957
972
|
document: options.document,
|
|
958
|
-
sleep: options.sleep ?? realSleep
|
|
973
|
+
sleep: options.sleep ?? realSleep,
|
|
974
|
+
...options.touched === void 0 ? {} : { touched: options.touched }
|
|
959
975
|
};
|
|
960
976
|
}
|
|
961
977
|
/** The current activity; the same object until something changes, as a store snapshot must be. */
|
|
@@ -1034,10 +1050,10 @@ var UiDriver = class {
|
|
|
1034
1050
|
...request.cursor === void 0 ? {} : { cursor: request.cursor },
|
|
1035
1051
|
...request.maxNodes === void 0 ? {} : { maxNodes: request.maxNodes }
|
|
1036
1052
|
});
|
|
1037
|
-
case "click": return click(this.env, request);
|
|
1038
|
-
case "type": return typeText(this.env, request);
|
|
1053
|
+
case "click": return await this.watchLeaving(() => click(this.env, request), signal);
|
|
1054
|
+
case "type": return await this.watchLeaving(() => typeText(this.env, request), signal);
|
|
1039
1055
|
case "select": return selectOption(this.env, request);
|
|
1040
|
-
case "press": return pressKey(this.env, request);
|
|
1056
|
+
case "press": return await this.watchLeaving(() => pressKey(this.env, request), signal);
|
|
1041
1057
|
case "scroll": return scroll(this.env, request);
|
|
1042
1058
|
case "wait": return wait(this.env, request, signal);
|
|
1043
1059
|
case "reload": return this.leave({ reload: true }, "reloading");
|
|
@@ -1045,6 +1061,29 @@ var UiDriver = class {
|
|
|
1045
1061
|
}
|
|
1046
1062
|
}
|
|
1047
1063
|
/**
|
|
1064
|
+
* Run an act that may start a navigation (a click on a link, Enter in a form) and say so: the browser asks `beforeunload` when a navigation starts, during the act or a
|
|
1065
|
+
* moment after it. The answer is sent before the page goes; the Host then waits for the next page instead of reading this one again.
|
|
1066
|
+
*/
|
|
1067
|
+
async watchLeaving(act, signal) {
|
|
1068
|
+
const view = this.options.document().defaultView;
|
|
1069
|
+
let leaving = false;
|
|
1070
|
+
const onLeave = () => {
|
|
1071
|
+
leaving = true;
|
|
1072
|
+
};
|
|
1073
|
+
view?.addEventListener("beforeunload", onLeave);
|
|
1074
|
+
try {
|
|
1075
|
+
const result = act();
|
|
1076
|
+
if (!leaving) await this.env.sleep(LEAVE_WATCH_MS, signal);
|
|
1077
|
+
return leaving ? {
|
|
1078
|
+
...result,
|
|
1079
|
+
leaving: true,
|
|
1080
|
+
detail: `${result.detail === void 0 ? "" : `${result.detail}; `}the page is going to another address`
|
|
1081
|
+
} : result;
|
|
1082
|
+
} finally {
|
|
1083
|
+
view?.removeEventListener("beforeunload", onLeave);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1048
1087
|
* The page is about to be replaced, so the answer is given first and the move happens a moment later (the answer is a POST that the page's own unload would cut off).
|
|
1049
1088
|
* The Host does not wait for this answer to be the last word: it waits for the page to come back.
|
|
1050
1089
|
*/
|
package/lib/door/protocol.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as parseUiRequest, S as parseChannelToPage, _ as UiRequest, a as DEFAULT_SNAPSHOT_NODES, b as WINDOW_ID, c as MAX_WAIT_MS, d as UI_OPS, f as UiActionResult, g as UiOp, h as UiNode, i as ChannelToPage, l as MUTATING_OPS, m as UiErrorCode, n as AuditEntry, o as MAX_SNAPSHOT_NODES, p as UiControlError, r as ChannelToHost, s as MAX_TEXT_LENGTH, t as APP_PATH, u as UI_ERROR_CODES, v as UiResult, x as parseChannelToHost, y as UiSnapshot } from "../protocol-
|
|
1
|
+
import { C as parseUiRequest, S as parseChannelToPage, _ as UiRequest, a as DEFAULT_SNAPSHOT_NODES, b as WINDOW_ID, c as MAX_WAIT_MS, d as UI_OPS, f as UiActionResult, g as UiOp, h as UiNode, i as ChannelToPage, l as MUTATING_OPS, m as UiErrorCode, n as AuditEntry, o as MAX_SNAPSHOT_NODES, p as UiControlError, r as ChannelToHost, s as MAX_TEXT_LENGTH, t as APP_PATH, u as UI_ERROR_CODES, v as UiResult, x as parseChannelToHost, y as UiSnapshot } from "../protocol-BeIYCgTo.js";
|
|
2
2
|
export { APP_PATH, AuditEntry, ChannelToHost, ChannelToPage, DEFAULT_SNAPSHOT_NODES, MAX_SNAPSHOT_NODES, MAX_TEXT_LENGTH, MAX_WAIT_MS, MUTATING_OPS, UI_ERROR_CODES, UI_OPS, UiActionResult, UiControlError, UiErrorCode, UiNode, UiOp, UiRequest, UiResult, UiSnapshot, WINDOW_ID, parseChannelToHost, parseChannelToPage, parseUiRequest };
|
package/lib/drivers.d.ts
CHANGED
|
@@ -26,10 +26,11 @@ type ChatEvent = {
|
|
|
26
26
|
interface ChatDriver {
|
|
27
27
|
readonly kind: 'chat';
|
|
28
28
|
readonly name: string;
|
|
29
|
-
/** Runs one message. Aborting `signal` stops the run. */
|
|
29
|
+
/** Runs one message. Aborting `signal` stops the run. `page` is the door's id of the page whose panel asked, when it sent one: a driver that uses the page drives that one. */
|
|
30
30
|
start(options: {
|
|
31
31
|
message: string;
|
|
32
32
|
signal: AbortSignal;
|
|
33
|
+
page?: string;
|
|
33
34
|
}): AsyncIterable<ChatEvent>;
|
|
34
35
|
}
|
|
35
36
|
/** A live pseudo-terminal session. */
|
|
@@ -45,6 +46,14 @@ interface TerminalSession {
|
|
|
45
46
|
interface TerminalDriver {
|
|
46
47
|
readonly kind: 'terminal';
|
|
47
48
|
readonly name: string;
|
|
49
|
+
/**
|
|
50
|
+
* Where the panel loads the terminal view from: the browser module that draws it, and the URL prefix the view's endpoints live under. The driver
|
|
51
|
+
* plugin serves both, so the view follows wherever the app is mounted (the root of its own server, or the prefix the ACRYL frame serves it at).
|
|
52
|
+
*/
|
|
53
|
+
readonly view: {
|
|
54
|
+
readonly module: string;
|
|
55
|
+
readonly prefix: string;
|
|
56
|
+
};
|
|
48
57
|
start(options: {
|
|
49
58
|
cwd: string;
|
|
50
59
|
env: NodeJS.ProcessEnv;
|
|
@@ -88,7 +88,17 @@ declare class Router extends Service {
|
|
|
88
88
|
* mount's code a request is in. The core's `app.route` runs first so a sub-app's own `onError` still applies to its routes.
|
|
89
89
|
*/
|
|
90
90
|
private snapshot;
|
|
91
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* The router's own disposal: let the flights already in flight finish within `disposeGraceMs` - a save being written when the app stops must not be cut
|
|
93
|
+
* by the router beneath it - then abort what is left and settle it for a moment, so disposal does not wait a second full grace on code that ignores
|
|
94
|
+
* its abort signal.
|
|
95
|
+
*/
|
|
96
|
+
private drain;
|
|
97
|
+
/** The one draining rule: let `flights` finish inside the grace, then abort and settle what is left. */
|
|
98
|
+
private drainFlights;
|
|
99
|
+
/** Resolve true when every flight settled inside `ms`, false at the deadline. */
|
|
100
|
+
private settledWithin;
|
|
101
|
+
/** Abort `flights`, cut their bodies short and wait for them to settle, at most `settleMs`. */
|
|
92
102
|
private end;
|
|
93
103
|
/** Turn errors no mounted app handled into responses. One handler per router; the calling plugin owns it. */
|
|
94
104
|
onError(handler: ErrorHandler): () => void;
|
package/lib/node-server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as Acryl } from "./app-D8riatvv.js";
|
|
2
|
-
import { a as Owner, c as UpgradeHubOptions, d as upgradeWebSocket, f as WebSocketData, i as testWebSocket, l as UpgradeRouter, m as WebSocketServerLike, n as TestWebSocketOptions, o as UpgradeEmitter, p as WebSocketLike, r as TestWebSocketResult, s as UpgradeHub, t as TestSocket, u as RequestError } from "./test-websocket-
|
|
2
|
+
import { a as Owner, c as UpgradeHubOptions, d as upgradeWebSocket, f as WebSocketData, i as testWebSocket, l as UpgradeRouter, m as WebSocketServerLike, n as TestWebSocketOptions, o as UpgradeEmitter, p as WebSocketLike, r as TestWebSocketResult, s as UpgradeHub, t as TestSocket, u as RequestError } from "./test-websocket-GUTAkFQT.js";
|
|
3
3
|
import { n as serveStatic, t as ServeStaticOptions } from "./serve-static-B7sqnftW.js";
|
|
4
4
|
import { t as getConnInfo } from "./conninfo-BCROmdnf.js";
|
|
5
5
|
import { AddressInfo } from "node:net";
|
|
@@ -90,6 +90,12 @@ declare const getRequestListener: (fetchCallback: FetchCallback, options?: {
|
|
|
90
90
|
interface ClientModulesOptions {
|
|
91
91
|
/** The folder of client modules, absolute or relative to the working directory. */
|
|
92
92
|
readonly root: string;
|
|
93
|
+
/**
|
|
94
|
+
* The URL prefix the app is mounted at, when the caller knows it. A host can compose app plugins under its own router, and under a
|
|
95
|
+
* Host whose router is a different copy of this package the request's match result is not visible here, so `basePath(c)` throws; the
|
|
96
|
+
* explicit prefix is then the only reliable way to know where the mount starts. Omitted, the router's `basePath(c)` is used.
|
|
97
|
+
*/
|
|
98
|
+
readonly mount?: string | undefined;
|
|
93
99
|
/** The folder {@link buildClientModules} wrote; with `NODE_ENV=production` it is served instead of compiling, which needs no `typescript`. */
|
|
94
100
|
readonly compiled?: string | undefined;
|
|
95
101
|
/** At most this many bytes of compiled modules are kept in memory (least recently served first out); 16 MiB when not given. */
|
package/lib/node-server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as serve,
|
|
1
|
+
import { a as serve, d as RequestError, i as createAdaptorServer, n as UpgradeEmitter, o as upgradeWebSocket, r as UpgradeHub, s as getRequestListener, t as testWebSocket } from "./test-websocket-CQOSvBth.js";
|
|
2
2
|
import { t as serveStatic } from "./serve-static-C1Nkj_xB.js";
|
|
3
3
|
import { t as getConnInfo } from "./conninfo-CWJwieh4.js";
|
|
4
|
-
import { n as clientModules, t as buildClientModules } from "./client-modules-
|
|
4
|
+
import { n as clientModules, t as buildClientModules } from "./client-modules-Bkdr83jO.js";
|
|
5
5
|
export { RequestError, UpgradeEmitter, UpgradeHub, buildClientModules, clientModules, createAdaptorServer, getConnInfo, getRequestListener, serve, serveStatic, testWebSocket, upgradeWebSocket };
|
|
@@ -36,6 +36,8 @@ interface UiNode {
|
|
|
36
36
|
readonly value?: string;
|
|
37
37
|
/** Heading level, when it is a heading. */
|
|
38
38
|
readonly level?: number;
|
|
39
|
+
/** The choices of a select, as `act select` takes them, when it is not sensitive. */
|
|
40
|
+
readonly options?: readonly string[];
|
|
39
41
|
}
|
|
40
42
|
interface UiSnapshot {
|
|
41
43
|
readonly generation: number;
|
|
@@ -93,6 +95,8 @@ interface UiActionResult {
|
|
|
93
95
|
readonly name: string;
|
|
94
96
|
};
|
|
95
97
|
readonly detail?: string;
|
|
98
|
+
/** The act started a navigation (a link, a form that submits): this page is going away, and the Host waits for the next one. */
|
|
99
|
+
readonly leaving?: true;
|
|
96
100
|
}
|
|
97
101
|
type UiResult = UiSnapshot | UiActionResult;
|
|
98
102
|
/**
|