acrylblends 1.1.0 → 1.3.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 +47 -2
- package/dist/index.js +2017 -32
- 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 +5 -3
- package/docs/framework/workshop/README.md +139 -153
- 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 +87 -9
- package/lib/cordis/door.js +324 -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/plugin.json +3 -0
- package/plugins/acryl-door/src/cli/acryl.ts +63 -6
- 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 +128 -24
- package/plugins/acryl-door/src/host/operator-routes.ts +21 -1
- 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 +14 -1
- package/plugins/acryl-door/src/index.ts +168 -30
- 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/builder/index.ts +13 -35
- package/plugins/builder/package.ts +186 -0
- package/plugins/builder/plugin.json +3 -1
- 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 +47 -18
- package/plugins/extensions/plugin-store.ts +27 -0
- package/plugins/extensions/plugin.json +1 -1
- package/plugins/extensions/resolve.ts +13 -0
- package/plugins/extensions/stage.ts +15 -5
- 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/plugin.json +1 -1
- package/plugins/manifest/snapshot.ts +275 -0
- package/plugins/openai/index.ts +64 -5
- package/plugins/openai/prompt.ts +23 -0
- package/plugins/plugin-share/README.md +51 -0
- package/plugins/plugin-share/index.ts +467 -0
- package/plugins/plugin-share/plugin.json +15 -0
- package/plugins/plugin-share/publish.ts +65 -0
- package/plugins/plugin-share/pull-check.ts +129 -0
- package/plugins/plugin-share/pulled-records.ts +70 -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/.gitattributes +2 -0
- package/templates/agent/AGENTS.md +1 -1
- package/templates/agent/blend.yaml +6 -0
- package/templates/agent/gitignore +1 -0
- package/templates/agent/package.json +1 -0
- package/templates/agent/src/app.ts +154 -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 +3 -0
- package/templates/agent/test/app.test.ts +45 -3
- package/templates/blank/.gitattributes +2 -0
- 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/.gitattributes +2 -0
- 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/.gitattributes +2 -0
- 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/.gitattributes +2 -0
- 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/.gitattributes +2 -0
- 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
package/lib/cordis/door.js
CHANGED
|
@@ -2,16 +2,31 @@ import { t as Acryl } from "../app-CQcGWjrk.js";
|
|
|
2
2
|
import { r as streamSSE } from "../streaming-BlDaph8U.js";
|
|
3
3
|
import { r as loopbackAuthority } from "../loopback-only-BbasoF7R.js";
|
|
4
4
|
import "../helpers-BEjcdbgw.js";
|
|
5
|
-
import { n as clientModules } from "../client-modules-
|
|
5
|
+
import { n as clientModules } from "../client-modules-Bkdr83jO.js";
|
|
6
6
|
import { a as MAX_WAIT_MS, d as parseChannelToHost, l as UiControlError, p as parseUiRequest, r as MAX_SNAPSHOT_NODES, u as WINDOW_ID } from "../protocol-CQ_if7Dg.js";
|
|
7
|
-
import { a as INVALID_JSON, c as operatorOnly, d as AuditLog, f as auditPath, i as DOOR_PREFIX, l as privateApi, o as error, p as UiChannel, r as pageChannelRoutes, s as jsonBody, t as renderSnapshot, u as readJson } from "../snapshot-text-
|
|
8
|
-
import "../src-
|
|
7
|
+
import { a as INVALID_JSON, c as operatorOnly, d as AuditLog, f as auditPath, i as DOOR_PREFIX, l as privateApi, o as error, p as UiChannel, r as pageChannelRoutes, s as jsonBody, t as renderSnapshot, u as readJson } from "../snapshot-text-CXTnjxGx.js";
|
|
8
|
+
import "../src-iBJ2ysV9.js";
|
|
9
9
|
import "../middleware-BfG_fp8P.js";
|
|
10
10
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
11
11
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
12
12
|
import z from "@deepseek-ai/schemastery";
|
|
13
13
|
import { randomBytes } from "node:crypto";
|
|
14
14
|
import { spawnSync } from "node:child_process";
|
|
15
|
+
//#region ../../plugins/acryl-door/src/page/base.ts
|
|
16
|
+
/**
|
|
17
|
+
* `path` (an app path, optionally with a query) under `base`: '/board' under '/app' is '/app/board'; the app's root is the base itself, so '/' is
|
|
18
|
+
* '/app' (its mount) and not '/app/'; without a base, the path as written.
|
|
19
|
+
*/
|
|
20
|
+
function underAppBase(base, path) {
|
|
21
|
+
if (base === "") return path;
|
|
22
|
+
const at = path.search(/[?#]/u);
|
|
23
|
+
const route = at === -1 ? path : path.slice(0, at);
|
|
24
|
+
const rest = at === -1 ? "" : path.slice(at);
|
|
25
|
+
if (route === base || route.startsWith(`${base}/`)) return path;
|
|
26
|
+
if (route === "/" || route === "") return `${base}${rest}`;
|
|
27
|
+
return `${base}${route.startsWith("/") ? "" : "/"}${route}${rest}`;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
15
30
|
//#region ../../plugins/acryl-door/src/shell/protocol.ts
|
|
16
31
|
var ShellError = class extends Error {
|
|
17
32
|
code;
|
|
@@ -283,12 +298,16 @@ function shellRoutes(prefix, secret, channel, heartbeatMs = SHELL_HEARTBEAT_MS)
|
|
|
283
298
|
* MCP (`tools/list`) and the built-in chat (the `tools` registry). A verb added here is in all three, or the comparison test fails.
|
|
284
299
|
*/
|
|
285
300
|
/** Every verb, in the order `brief` lists them. */
|
|
301
|
+
/** The verbs that only use the page: what a page-only door (a production assistant) offers. */
|
|
302
|
+
const PAGE_VERBS = ["look", "act"];
|
|
286
303
|
const VERBS = [
|
|
287
304
|
"brief",
|
|
288
305
|
"look",
|
|
289
306
|
"act",
|
|
290
307
|
"plugin-list",
|
|
291
308
|
"plugin-add",
|
|
309
|
+
"plugin-pull",
|
|
310
|
+
"plugin-publish",
|
|
292
311
|
"plugin-remove",
|
|
293
312
|
"plugin-reload",
|
|
294
313
|
"tool-list",
|
|
@@ -444,6 +463,42 @@ const VERB_SPECS = {
|
|
|
444
463
|
additionalProperties: false
|
|
445
464
|
}
|
|
446
465
|
},
|
|
466
|
+
"plugin-pull": {
|
|
467
|
+
usage: "plugin pull <source>: fetch a plugin someone released (github:<owner>/<repo>[@<version>][@sha256:<digest>], or a .tgz), check it and start it, live",
|
|
468
|
+
description: "Fetch a plugin someone released, without running anything; check its digest, that it works on every OS and that this app has the services it needs; then install and start it without a restart. A refusal says why and changes nothing.",
|
|
469
|
+
arguments: {
|
|
470
|
+
type: "object",
|
|
471
|
+
properties: { source: {
|
|
472
|
+
type: "string",
|
|
473
|
+
description: "github:<owner>/<repo>[@<version>][@sha256:<digest>], or a .tgz path or https address"
|
|
474
|
+
} },
|
|
475
|
+
required: ["source"],
|
|
476
|
+
additionalProperties: false
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
"plugin-publish": {
|
|
480
|
+
usage: "plugin publish <name> [--owner <account>] [--license <id>]: show what publishing this plugin would send; in a person's own terminal, ask them",
|
|
481
|
+
description: "Show what publishing a plugin would send: the account, repository, version, license, files and the line a friend pulls it with. Sends nothing. Only a person can say yes, in their own terminal or with Share in the frame; tell them the plan and how.",
|
|
482
|
+
arguments: {
|
|
483
|
+
type: "object",
|
|
484
|
+
properties: {
|
|
485
|
+
name: {
|
|
486
|
+
type: "string",
|
|
487
|
+
description: "a plugin of plugins/"
|
|
488
|
+
},
|
|
489
|
+
owner: {
|
|
490
|
+
type: "string",
|
|
491
|
+
description: "the GitHub account or organization; the signed-in one when absent"
|
|
492
|
+
},
|
|
493
|
+
license: {
|
|
494
|
+
type: "string",
|
|
495
|
+
description: "an SPDX license id; MIT when absent"
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
required: ["name"],
|
|
499
|
+
additionalProperties: false
|
|
500
|
+
}
|
|
501
|
+
},
|
|
447
502
|
"plugin-remove": {
|
|
448
503
|
usage: "plugin remove <name>: stop and remove an installed plugin",
|
|
449
504
|
description: "Stop and remove an installed plugin.",
|
|
@@ -598,6 +653,14 @@ var DoorError = class extends Error {
|
|
|
598
653
|
this.name = "DoorError";
|
|
599
654
|
}
|
|
600
655
|
};
|
|
656
|
+
/** What a plan says, for a person deciding. */
|
|
657
|
+
function renderPublishPlan(plan) {
|
|
658
|
+
return [
|
|
659
|
+
`Publish to ${plan.owner}/${plan.repo}${plan.createsRepository ? " (a new repository)" : ""}, ${plan.visibility}, version ${plan.version}, license ${plan.license}.`,
|
|
660
|
+
`Files: ${plan.files.join(", ")}.`,
|
|
661
|
+
`A friend gets exactly these bytes with: ${plan.pullLine}`
|
|
662
|
+
].join("\n");
|
|
663
|
+
}
|
|
601
664
|
/** A shell answer as text an agent reads: windows and menu items one to a line. */
|
|
602
665
|
function renderShell(result) {
|
|
603
666
|
if ("windows" in result) return result.windows.length === 0 ? "no windows are open" : result.windows.map((window) => `${String(window.id)} ${JSON.stringify(window.title)} ${window.path} ${String(window.width)}x${String(window.height)}${window.x === void 0 || window.y === void 0 ? "" : `@${String(window.x)},${String(window.y)}`}${window.focused ? " focused" : ""}`).join("\n");
|
|
@@ -625,19 +688,19 @@ var Door = class {
|
|
|
625
688
|
return this.deps.channel.driveFor(windowId);
|
|
626
689
|
}
|
|
627
690
|
/** Run one verb. @throws DoorError or UiControlError. */
|
|
628
|
-
async run(verb, args, signal) {
|
|
691
|
+
async run(verb, args, signal, scope = {}) {
|
|
629
692
|
const input = typeof args === "object" && args !== null && !Array.isArray(args) ? args : {};
|
|
630
693
|
switch (verb) {
|
|
631
694
|
case "brief": return this.brief();
|
|
632
695
|
case "look": {
|
|
633
|
-
const snapshot = await this.look(input, signal);
|
|
696
|
+
const snapshot = await this.look(input, signal, scope);
|
|
634
697
|
return {
|
|
635
698
|
value: snapshot,
|
|
636
699
|
text: renderSnapshot(snapshot, (cursor) => `run look --cursor ${String(cursor)}`)
|
|
637
700
|
};
|
|
638
701
|
}
|
|
639
702
|
case "act": {
|
|
640
|
-
const result = await this.act(input, signal);
|
|
703
|
+
const result = await this.act(input, signal, scope);
|
|
641
704
|
return {
|
|
642
705
|
value: result,
|
|
643
706
|
text: `done${result.target === void 0 ? "" : `: ${result.target.role} "${result.target.name}"`}${result.detail === void 0 ? "" : ` (${result.detail})`}; look again before the next act`
|
|
@@ -645,6 +708,8 @@ var Door = class {
|
|
|
645
708
|
}
|
|
646
709
|
case "plugin-list": return this.plugin((extensions) => extensions.list(), false);
|
|
647
710
|
case "plugin-add": return this.plugin((extensions) => extensions.install(this.insideApp(input.folder)));
|
|
711
|
+
case "plugin-pull": return this.pull(input.source);
|
|
712
|
+
case "plugin-publish": return this.planPublish(input);
|
|
648
713
|
case "plugin-remove": {
|
|
649
714
|
const name = input.name;
|
|
650
715
|
if (typeof name !== "string" || name === "") throw new DoorError("invalid", "plugin remove needs a plugin name");
|
|
@@ -694,33 +759,47 @@ var Door = class {
|
|
|
694
759
|
].join("\n")
|
|
695
760
|
};
|
|
696
761
|
}
|
|
697
|
-
look(input, signal) {
|
|
698
|
-
const { window, rest } = this.windowOf(input);
|
|
762
|
+
look(input, signal, scope = {}) {
|
|
763
|
+
const { window, rest } = this.windowOf(input, scope);
|
|
699
764
|
const request = this.parse({
|
|
700
765
|
...rest,
|
|
701
766
|
op: "snapshot"
|
|
702
767
|
});
|
|
703
|
-
return this.callPage("look", request, signal, window);
|
|
768
|
+
return this.callPage("look", request, signal, window, scope.page);
|
|
704
769
|
}
|
|
705
|
-
async act(input, signal) {
|
|
770
|
+
async act(input, signal, scope = {}) {
|
|
706
771
|
if (input.op === "snapshot") throw new DoorError("invalid", "use look to see the page");
|
|
707
|
-
const { window, rest } = this.windowOf(input);
|
|
772
|
+
const { window, rest } = this.windowOf(input, scope);
|
|
708
773
|
const request = this.parse(rest);
|
|
709
|
-
|
|
710
|
-
const
|
|
774
|
+
const page = scope.page;
|
|
775
|
+
const moves = request.op === "reload" || request.op === "navigate";
|
|
776
|
+
if (!moves && request.op !== "click" && request.op !== "press" && request.op !== "type") return await this.callPage(`act ${String(input.op)}`, request, signal, window, page);
|
|
777
|
+
const backMs = this.deps.pageBackMs ?? PAGE_BACK_MS;
|
|
778
|
+
const stay = new AbortController();
|
|
779
|
+
const back = page === void 0 ? this.deps.channel.expectNewPage(backMs, stay.signal) : this.deps.channel.expectPageBack(page, backMs, stay.signal);
|
|
711
780
|
let answer;
|
|
781
|
+
let left = moves;
|
|
712
782
|
try {
|
|
713
|
-
answer = await this.callPage(`act ${request.op}`, request, signal, window);
|
|
783
|
+
answer = await this.callPage(`act ${request.op}`, request, signal, window, page);
|
|
784
|
+
left ||= answer.leaving === true;
|
|
714
785
|
} catch (cause) {
|
|
715
|
-
if (!(cause instanceof UiControlError && cause.code === "disconnected"))
|
|
786
|
+
if (!(cause instanceof UiControlError && cause.code === "disconnected")) {
|
|
787
|
+
stay.abort();
|
|
788
|
+
throw cause;
|
|
789
|
+
}
|
|
716
790
|
answer = {
|
|
717
791
|
ok: true,
|
|
718
|
-
detail: request.op === "reload" ? "reloading" : `going to ${request.path}`
|
|
792
|
+
detail: request.op === "reload" ? "reloading" : request.op === "navigate" ? `going to ${request.path}` : "the page is going to another address"
|
|
719
793
|
};
|
|
794
|
+
left = true;
|
|
795
|
+
}
|
|
796
|
+
if (!left) {
|
|
797
|
+
stay.abort();
|
|
798
|
+
return answer;
|
|
720
799
|
}
|
|
721
800
|
const came = await back;
|
|
722
|
-
if (came !== void 0) this.deps.channel.prefer(came);
|
|
723
|
-
if (came === void 0) throw new UiControlError("timeout", request.op === "reload" ? "the page did not come back after the reload" : `the page did not come back after going to ${request.path}: is there a page at that path, and does it load the door's script?`);
|
|
801
|
+
if (came !== void 0 && page === void 0) this.deps.channel.prefer(came);
|
|
802
|
+
if (came === void 0) throw new UiControlError("timeout", request.op === "reload" ? "the page did not come back after the reload" : request.op === "navigate" ? `the page did not come back after going to ${request.path}: is there a page at that path, and does it load the door's script?` : `the ${request.op} took the page to another address, and no page came back: does the page there load the door's script?`);
|
|
724
803
|
const landed = request.op === "navigate" ? await this.statusAt(request.path) : void 0;
|
|
725
804
|
return {
|
|
726
805
|
...answer,
|
|
@@ -735,7 +814,8 @@ var Door = class {
|
|
|
735
814
|
const base = this.deps.app.url;
|
|
736
815
|
if (base === void 0) return void 0;
|
|
737
816
|
try {
|
|
738
|
-
const
|
|
817
|
+
const target = underAppBase(this.deps.appBase ?? "", path);
|
|
818
|
+
const response = await fetch(new URL(target, base), {
|
|
739
819
|
headers: { accept: "text/html" },
|
|
740
820
|
signal: AbortSignal.timeout(3e3)
|
|
741
821
|
});
|
|
@@ -800,12 +880,13 @@ var Door = class {
|
|
|
800
880
|
}
|
|
801
881
|
}
|
|
802
882
|
/** `window` on look and act: the desktop window (the number `window list` prints) whose page the call is for; without it, the page used last. */
|
|
803
|
-
windowOf(input) {
|
|
883
|
+
windowOf(input, scope = {}) {
|
|
804
884
|
const { window, ...rest } = input;
|
|
805
885
|
if (window === void 0) return {
|
|
806
886
|
window: void 0,
|
|
807
887
|
rest
|
|
808
888
|
};
|
|
889
|
+
if (scope.page !== void 0) throw new DoorError("invalid", "this run drives only the page it was asked from; window does not apply");
|
|
809
890
|
if (typeof window !== "number" || !Number.isSafeInteger(window) || window < 1) throw new DoorError("invalid", "window must be the number `window list` prints for the window");
|
|
810
891
|
return {
|
|
811
892
|
window,
|
|
@@ -820,12 +901,13 @@ var Door = class {
|
|
|
820
901
|
}
|
|
821
902
|
}
|
|
822
903
|
/** Run one call on the page and record it. The audit holds which control was touched and how it ended, never what was typed. */
|
|
823
|
-
async callPage(verb, request, signal, window) {
|
|
904
|
+
async callPage(verb, request, signal, window, page) {
|
|
824
905
|
const approval = "token";
|
|
825
906
|
try {
|
|
826
907
|
const value = await this.deps.channel.call(request, {
|
|
827
908
|
signal,
|
|
828
909
|
...window === void 0 ? {} : { window },
|
|
910
|
+
...page === void 0 ? {} : { page },
|
|
829
911
|
...request.op === "wait" ? { timeoutMs: (request.timeoutMs ?? 3e3) + 5e3 } : {}
|
|
830
912
|
});
|
|
831
913
|
const target = "target" in value ? value.target : void 0;
|
|
@@ -862,7 +944,7 @@ var Door = class {
|
|
|
862
944
|
}
|
|
863
945
|
async plugin(run, mutates = true) {
|
|
864
946
|
const extensions = this.deps.extensions();
|
|
865
|
-
if (extensions === void 0) throw new DoorError("unavailable", "this
|
|
947
|
+
if (extensions === void 0) throw new DoorError("unavailable", "this door has no extensions plugin to drive. In ACRYL's frame none is needed: a saved change in src/ or plugins/ reloads the app by itself, so do not restart it. Elsewhere, the app has no way to install plugins while it runs");
|
|
866
948
|
const value = await run(extensions);
|
|
867
949
|
const unchanged = typeof value === "object" && value !== null && value.action === "unchanged";
|
|
868
950
|
if (mutates && !unchanged && this.deps.reloadOnChange !== false) await this.deps.channel.reloadAll();
|
|
@@ -871,6 +953,50 @@ var Door = class {
|
|
|
871
953
|
text: JSON.stringify(value, null, 2)
|
|
872
954
|
};
|
|
873
955
|
}
|
|
956
|
+
share() {
|
|
957
|
+
const share = this.deps.pluginShare?.();
|
|
958
|
+
if (share === void 0) throw new DoorError("unavailable", "plugin-share is not running in this app. If the app just changed it is restarting, so try again in a moment. An app made by an older version does not have it, and upgrade does not add it: run `npx acrylblends add plugin-share` and follow its README");
|
|
959
|
+
return share;
|
|
960
|
+
}
|
|
961
|
+
/** Only a plan: no argument of any verb can publish, so an agent never says yes for a person. */
|
|
962
|
+
async planPublish(input) {
|
|
963
|
+
const { name, owner, license } = input;
|
|
964
|
+
if (typeof name !== "string" || name === "") throw new DoorError("invalid", "plugin publish needs a plugin name, e.g. plugin publish pomodoro");
|
|
965
|
+
const value = await this.share().planPublish(name, {
|
|
966
|
+
...typeof owner === "string" && owner !== "" ? { owner } : {},
|
|
967
|
+
...typeof license === "string" && license !== "" ? { license } : {}
|
|
968
|
+
});
|
|
969
|
+
if ("ok" in value) throw new DoorError("refused", `${value.reason}. ${value.next}.`);
|
|
970
|
+
return {
|
|
971
|
+
value,
|
|
972
|
+
text: `${renderPublishPlan(value)}\nNothing was sent. Only a person can say yes: they run \`npm run acryl -- plugin publish ${name}\` in their own terminal, or press Share in the frame. \`--license <id>\` changes the license before the yes.`
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
/** The person's yes, from their own terminal. Not a verb: MCP and the chat do not have it. */
|
|
976
|
+
async publishConfirmed(name, confirmed, options) {
|
|
977
|
+
const value = await this.share().publish(name, confirmed, options);
|
|
978
|
+
this.deps.audit.record({
|
|
979
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
980
|
+
tool: "plugin-publish",
|
|
981
|
+
target: {
|
|
982
|
+
role: "plugin",
|
|
983
|
+
name: `${confirmed.owner}/${confirmed.repo}@${confirmed.version}`
|
|
984
|
+
},
|
|
985
|
+
outcome: value.ok ? "ok" : "refused",
|
|
986
|
+
approval: "token"
|
|
987
|
+
});
|
|
988
|
+
return value;
|
|
989
|
+
}
|
|
990
|
+
async pull(source) {
|
|
991
|
+
if (typeof source !== "string" || source === "") throw new DoorError("invalid", "plugin pull needs a source, e.g. plugin pull github:someone/pomodoro@0.1.0");
|
|
992
|
+
const value = await this.share().pull(source);
|
|
993
|
+
if (!value.ok) throw new DoorError("refused", `${value.reason ?? "the pull was refused"}. ${value.next ?? "nothing changed"}.`);
|
|
994
|
+
if (value.action !== "unchanged" && this.deps.reloadOnChange !== false) await this.deps.channel.reloadAll();
|
|
995
|
+
return {
|
|
996
|
+
value,
|
|
997
|
+
text: JSON.stringify(value, null, 2)
|
|
998
|
+
};
|
|
999
|
+
}
|
|
874
1000
|
/** A folder named by an agent, resolved from the app folder; it must be inside the app. */
|
|
875
1001
|
insideApp(folder) {
|
|
876
1002
|
if (typeof folder !== "string" || folder === "") throw new DoorError("invalid", "plugin add needs a folder, e.g. plugins/pomodoro");
|
|
@@ -914,8 +1040,8 @@ var Door = class {
|
|
|
914
1040
|
* `door_<verb>`. A tool answers the verb's text, what an agent reads in a terminal; a failed verb throws with its code, as the operator
|
|
915
1041
|
* client prints it.
|
|
916
1042
|
*/
|
|
917
|
-
/** Register every
|
|
918
|
-
function registerChatTools(tools, door) {
|
|
1043
|
+
/** Register the verbs (default: every one). @returns the function that removes them all; it also aborts the calls still running. */
|
|
1044
|
+
function registerChatTools(tools, door, verbs = VERBS) {
|
|
919
1045
|
const running = new AbortController();
|
|
920
1046
|
const removers = [];
|
|
921
1047
|
const removeAll = () => {
|
|
@@ -923,12 +1049,13 @@ function registerChatTools(tools, door) {
|
|
|
923
1049
|
for (const remove of removers.splice(0).reverse()) remove();
|
|
924
1050
|
};
|
|
925
1051
|
try {
|
|
926
|
-
for (const verb of
|
|
1052
|
+
for (const verb of verbs) removers.push(tools.register(chatToolName(verb), {
|
|
927
1053
|
description: VERB_SPECS[verb].description,
|
|
928
1054
|
parameters: VERB_SPECS[verb].arguments,
|
|
929
|
-
run: async (input) => {
|
|
1055
|
+
run: async (input, options) => {
|
|
1056
|
+
const signal = options?.signal === void 0 ? running.signal : AbortSignal.any([running.signal, options.signal]);
|
|
930
1057
|
try {
|
|
931
|
-
return (await door.run(verb, input ?? {},
|
|
1058
|
+
return (await door.run(verb, input ?? {}, signal, options?.page === void 0 ? {} : { page: options.page })).text;
|
|
932
1059
|
} catch (cause) {
|
|
933
1060
|
if (cause instanceof DoorError || cause instanceof UiControlError) throw new Error(`${cause.code}: ${cause.message}`, { cause });
|
|
934
1061
|
throw cause;
|
|
@@ -997,10 +1124,14 @@ function alive(pid) {
|
|
|
997
1124
|
* Write this run's connection file, replacing a stale one.
|
|
998
1125
|
* @throws when another live process already serves this folder's door: two apps from one folder would race for one file.
|
|
999
1126
|
*/
|
|
1000
|
-
|
|
1001
|
-
|
|
1127
|
+
/** @throws when another live process already serves this folder's door: two apps from one folder would race for one file. */
|
|
1128
|
+
function assertDoorIsFree(appRoot) {
|
|
1002
1129
|
const existing = readDoorFile(appRoot);
|
|
1003
1130
|
if (existing !== void 0 && existing.pid !== process.pid && alive(existing.pid)) throw new Error(`acryl-door: another process (pid ${String(existing.pid)}) serves the door of ${appRoot}; stop it first`);
|
|
1131
|
+
}
|
|
1132
|
+
function writeDoorFile(appRoot, url, secret) {
|
|
1133
|
+
const dir = ensureStateDir(appRoot);
|
|
1134
|
+
assertDoorIsFree(appRoot);
|
|
1004
1135
|
const file = doorFilePath(appRoot);
|
|
1005
1136
|
const temporary = join(dir, `${DOOR_FILE}.${String(process.pid)}.tmp`);
|
|
1006
1137
|
const content = {
|
|
@@ -1157,6 +1288,33 @@ function operatorRoutes(prefix, secret, door, reportError) {
|
|
|
1157
1288
|
reportError(`run the door verb ${verb}`, cause);
|
|
1158
1289
|
return c.json(error("the verb could not be run"), 500);
|
|
1159
1290
|
}
|
|
1291
|
+
}).post(`${prefix}/publish-confirmed`, operatorOnly(secret), jsonBody, async (c) => {
|
|
1292
|
+
const body = await readJson(c);
|
|
1293
|
+
const { name, confirm, owner, license } = body === INVALID_JSON || typeof body !== "object" || body === null ? {} : body;
|
|
1294
|
+
if (typeof name !== "string" || typeof confirm !== "object" || confirm === null) return c.json({
|
|
1295
|
+
ok: false,
|
|
1296
|
+
code: "invalid",
|
|
1297
|
+
message: "the body is { name, confirm }"
|
|
1298
|
+
}, 400);
|
|
1299
|
+
try {
|
|
1300
|
+
const value = await door.publishConfirmed(name, confirm, {
|
|
1301
|
+
...typeof owner === "string" ? { owner } : {},
|
|
1302
|
+
...typeof license === "string" ? { license } : {}
|
|
1303
|
+
});
|
|
1304
|
+
return c.json({
|
|
1305
|
+
ok: true,
|
|
1306
|
+
value,
|
|
1307
|
+
text: JSON.stringify(value, null, 2)
|
|
1308
|
+
});
|
|
1309
|
+
} catch (cause) {
|
|
1310
|
+
if (cause instanceof DoorError) return c.json({
|
|
1311
|
+
ok: false,
|
|
1312
|
+
code: cause.code,
|
|
1313
|
+
message: cause.message
|
|
1314
|
+
}, 409);
|
|
1315
|
+
reportError("publish a plugin", cause);
|
|
1316
|
+
return c.json(error("the plugin could not be published"), 500);
|
|
1317
|
+
}
|
|
1160
1318
|
});
|
|
1161
1319
|
}
|
|
1162
1320
|
/** The upgrade request comes from this app's own page: a loopback peer on this port, and an `Origin` that is exactly this app's origin. Nothing else is read as proof. */
|
|
@@ -1171,11 +1329,13 @@ const ownOrigin = async (c, next) => {
|
|
|
1171
1329
|
};
|
|
1172
1330
|
function pageSocketRoutes(channel, options) {
|
|
1173
1331
|
const heartbeatMs = options.heartbeatMs ?? 15e3;
|
|
1174
|
-
|
|
1332
|
+
const events = (c) => {
|
|
1175
1333
|
const windowId = c.req.query("window");
|
|
1176
1334
|
const focused = c.req.query("focused") === "1";
|
|
1177
1335
|
const shellQuery = Number(c.req.query("shell"));
|
|
1178
1336
|
const shellWindow = Number.isSafeInteger(shellQuery) && shellQuery > 0 ? shellQuery : void 0;
|
|
1337
|
+
const tabQuery = c.req.query("tab");
|
|
1338
|
+
const tab = tabQuery !== void 0 && WINDOW_ID.test(tabQuery) ? tabQuery : void 0;
|
|
1179
1339
|
let detach;
|
|
1180
1340
|
let beat;
|
|
1181
1341
|
return {
|
|
@@ -1199,7 +1359,7 @@ function pageSocketRoutes(channel, options) {
|
|
|
1199
1359
|
close: () => {
|
|
1200
1360
|
ws.close(1e3, "replaced");
|
|
1201
1361
|
}
|
|
1202
|
-
}, shellWindow);
|
|
1362
|
+
}, shellWindow, tab);
|
|
1203
1363
|
write({ e: "ready" });
|
|
1204
1364
|
beat = setInterval(() => {
|
|
1205
1365
|
try {
|
|
@@ -1223,7 +1383,12 @@ function pageSocketRoutes(channel, options) {
|
|
|
1223
1383
|
detach?.();
|
|
1224
1384
|
}
|
|
1225
1385
|
};
|
|
1226
|
-
}
|
|
1386
|
+
};
|
|
1387
|
+
return new Acryl().get(`${options.prefix}/channel/ws`, ownOrigin, async (c, next) => {
|
|
1388
|
+
const allow = options.upgrade();
|
|
1389
|
+
if (allow === void 0) return c.json(error("the page channel is not ready yet"), 503);
|
|
1390
|
+
return allow(events, { textOnly: true })(c, next);
|
|
1391
|
+
});
|
|
1227
1392
|
}
|
|
1228
1393
|
/** The oldest Node the client runs on (the framework's own floor). */
|
|
1229
1394
|
const MIN_NODE = [22, 19];
|
|
@@ -1343,14 +1508,13 @@ function writeShims(root, target) {
|
|
|
1343
1508
|
*/
|
|
1344
1509
|
/** Stable Cordis plugin name, equal to the package name and the Loader row id. */
|
|
1345
1510
|
const name = "acryl-door";
|
|
1346
|
-
const inject = [
|
|
1347
|
-
"router",
|
|
1348
|
-
"appInstance",
|
|
1349
|
-
"nodeServer"
|
|
1350
|
-
];
|
|
1511
|
+
const inject = ["router", "appInstance"];
|
|
1351
1512
|
const Config = z.object({
|
|
1352
1513
|
allowInProduction: z.boolean().default(false).description("Run in production too (NODE_ENV=production); ACRYL_DOOR=off still turns it off."),
|
|
1514
|
+
pageOnly: z.boolean().default(false).description("Only the page and the look and act chat tools (a production assistant); runs in production."),
|
|
1353
1515
|
reloadOnChange: z.boolean().default(true).description("Reload the open pages after a plugin was added, removed or reloaded while the app runs."),
|
|
1516
|
+
hostIndex: z.boolean().default(true).description("Put the page script in the host's own index page too; off when that index is a frame around the app."),
|
|
1517
|
+
appBase: z.string().default("").description("The prefix the app pages are served under (the frame canvas), without a trailing slash; empty at the root."),
|
|
1354
1518
|
tools: z.object({ expose: z.array(z.string().pattern(/^(?!door_)[A-Za-z][A-Za-z0-9_-]{0,63}$/u)).description("App tools (from the tools registry) the door offers; none by default.") })
|
|
1355
1519
|
});
|
|
1356
1520
|
/** The page module, served compiled from source by the app's own TypeScript. */
|
|
@@ -1382,26 +1546,53 @@ function notFoundPage(path, scriptTag) {
|
|
|
1382
1546
|
}
|
|
1383
1547
|
function apply(ctx, rawConfig = {}) {
|
|
1384
1548
|
const config = Config(rawConfig);
|
|
1385
|
-
|
|
1549
|
+
const pageOnly = config.pageOnly === true;
|
|
1550
|
+
if (process.env.ACRYL_DOOR === "off" || process.env.NODE_ENV === "production" && config.allowInProduction !== true && !pageOnly) return;
|
|
1386
1551
|
const app = ctx.get("appInstance");
|
|
1387
|
-
const
|
|
1552
|
+
const carrier = () => {
|
|
1553
|
+
const web = ctx.get("acrylWeb");
|
|
1554
|
+
if (web !== void 0) return web;
|
|
1555
|
+
const node = ctx.get("nodeServer");
|
|
1556
|
+
if (typeof node?.url !== "string") return void 0;
|
|
1557
|
+
try {
|
|
1558
|
+
const parsed = new URL(node.url);
|
|
1559
|
+
return {
|
|
1560
|
+
host: parsed.hostname,
|
|
1561
|
+
port: Number(parsed.port)
|
|
1562
|
+
};
|
|
1563
|
+
} catch {
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1388
1567
|
if (typeof app?.root !== "string") throw new Error("acryl-door: the appInstance service has no app folder (root)");
|
|
1389
1568
|
const reportError = (operation, cause) => {
|
|
1390
1569
|
ctx.logger?.error?.(`acryl-door: failed to ${operation}: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1391
1570
|
};
|
|
1571
|
+
const address = { url: "" };
|
|
1572
|
+
if (!pageOnly) assertDoorIsFree(app.root);
|
|
1392
1573
|
const secret = newSecret();
|
|
1393
|
-
const channel = new UiChannel({
|
|
1574
|
+
const channel = new UiChannel({
|
|
1575
|
+
attachWaitMs: 2e3,
|
|
1576
|
+
requirePage: pageOnly
|
|
1577
|
+
});
|
|
1394
1578
|
const shell = new ShellChannel();
|
|
1579
|
+
const audit = new AuditLog(auditPath(stateDir(app.root)));
|
|
1580
|
+
const rawBase = typeof config.appBase === "string" ? config.appBase.trim() : "";
|
|
1581
|
+
const appBase = rawBase.startsWith("/") && rawBase !== "/" ? rawBase.replace(/\/+$/u, "") : "";
|
|
1395
1582
|
const door = new Door({
|
|
1396
1583
|
channel,
|
|
1397
1584
|
shell,
|
|
1398
|
-
audit
|
|
1585
|
+
audit,
|
|
1399
1586
|
app: {
|
|
1400
1587
|
name: app.name,
|
|
1401
1588
|
root: app.root,
|
|
1402
|
-
url
|
|
1589
|
+
get url() {
|
|
1590
|
+
return address.url;
|
|
1591
|
+
}
|
|
1403
1592
|
},
|
|
1593
|
+
...appBase === "" ? {} : { appBase },
|
|
1404
1594
|
extensions: () => ctx.get("extensions"),
|
|
1595
|
+
pluginShare: () => ctx.get("pluginShare"),
|
|
1405
1596
|
tools: () => ctx.get("tools"),
|
|
1406
1597
|
expose: config.tools?.expose ?? [],
|
|
1407
1598
|
reloadOnChange: config.reloadOnChange !== false
|
|
@@ -1412,8 +1603,12 @@ function apply(ctx, rawConfig = {}) {
|
|
|
1412
1603
|
ctx.effect(() => () => {
|
|
1413
1604
|
shell.close();
|
|
1414
1605
|
}, "acryl-door: desktop shell channel");
|
|
1415
|
-
const pageModule = new Acryl().get(`${DOOR_PREFIX}/page.js`, (c) => c.body(`import '${DOOR_PREFIX}/page/main.ts'\n`, 200, { "content-type": "text/javascript; charset=utf-8" })).route(`${DOOR_PREFIX}/page`, clientModules({
|
|
1606
|
+
const pageModule = new Acryl().get(`${DOOR_PREFIX}/page.js`, (c) => c.body(`import '${DOOR_PREFIX}/page/main.ts'\n`, 200, { "content-type": "text/javascript; charset=utf-8" })).route(`${DOOR_PREFIX}/page`, clientModules({
|
|
1607
|
+
root: PAGE_DIR,
|
|
1608
|
+
mount: `${DOOR_PREFIX}/page`
|
|
1609
|
+
}));
|
|
1416
1610
|
const tag = `<script type="module" src="${DOOR_PREFIX}/page.js"><\/script>`;
|
|
1611
|
+
const meta = appBase === "" ? "" : `<meta name="acryl-app-base" content="${appBase}">`;
|
|
1417
1612
|
const addScript = async (c, next) => {
|
|
1418
1613
|
await next();
|
|
1419
1614
|
if (!(c.res.headers.get("content-type") ?? "").includes("text/html")) {
|
|
@@ -1423,11 +1618,12 @@ function apply(ctx, rawConfig = {}) {
|
|
|
1423
1618
|
});
|
|
1424
1619
|
return;
|
|
1425
1620
|
}
|
|
1426
|
-
|
|
1621
|
+
let html = await c.res.text();
|
|
1427
1622
|
const headers = new Headers(c.res.headers);
|
|
1428
1623
|
headers.delete("content-length");
|
|
1429
|
-
|
|
1430
|
-
|
|
1624
|
+
if (meta !== "" && !html.includes("name=\"acryl-app-base\"")) html = html.includes("</head>") ? html.replace("</head>", `${meta}</head>`) : meta + html;
|
|
1625
|
+
if (!html.includes(`/api/acryl-door/page.js`)) html = html.includes("</body>") ? html.replace("</body>", `${tag}</body>`) : html + tag;
|
|
1626
|
+
c.res = new Response(html, {
|
|
1431
1627
|
status: c.res.status,
|
|
1432
1628
|
headers
|
|
1433
1629
|
});
|
|
@@ -1435,31 +1631,99 @@ function apply(ctx, rawConfig = {}) {
|
|
|
1435
1631
|
const router = ctx.router;
|
|
1436
1632
|
if (typeof router.wrap === "function") router.wrap(addScript);
|
|
1437
1633
|
else ctx.router.route("/", new Acryl().use("*", addScript));
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1634
|
+
if (config.hostIndex !== false) ctx.inject(["webServer"], (inner) => {
|
|
1635
|
+
inner.effect(() => {
|
|
1636
|
+
const taps = inner.get("webServer");
|
|
1637
|
+
if (typeof taps?.tapIndex !== "function") return () => {};
|
|
1638
|
+
return taps.tapIndex((html) => {
|
|
1639
|
+
if (html.includes(`/api/acryl-door/page.js`)) return html;
|
|
1640
|
+
return html.includes("</body>") ? html.replace("</body>", `${tag}</body>`) : html + tag;
|
|
1641
|
+
});
|
|
1642
|
+
}, "acryl-door: the page script in the host index");
|
|
1643
|
+
});
|
|
1644
|
+
let pageUpgrade;
|
|
1645
|
+
let nodeUpgrade;
|
|
1646
|
+
const endNodeOwner = () => {
|
|
1647
|
+
if (pageUpgrade === nodeUpgrade) pageUpgrade = void 0;
|
|
1648
|
+
nodeUpgrade = void 0;
|
|
1649
|
+
};
|
|
1650
|
+
const nodeFallback = () => {
|
|
1651
|
+
if (ctx.get("routerHttp") !== void 0 || nodeUpgrade !== void 0) return;
|
|
1652
|
+
try {
|
|
1653
|
+
const allow = ctx.get("nodeServer")?.upgrades?.();
|
|
1654
|
+
if (allow !== void 0) {
|
|
1655
|
+
nodeUpgrade = allow;
|
|
1656
|
+
pageUpgrade = allow;
|
|
1657
|
+
}
|
|
1658
|
+
} catch (cause) {
|
|
1659
|
+
reportError("open the page channel over a WebSocket (the pages use the event stream)", cause);
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
ctx.inject(["routerHttp"], (inner) => {
|
|
1663
|
+
endNodeOwner();
|
|
1664
|
+
const http = inner.get("routerHttp");
|
|
1665
|
+
try {
|
|
1666
|
+
const allow = http?.upgrades?.();
|
|
1667
|
+
if (allow === void 0 || http === void 0 || typeof http.mount !== "function") return;
|
|
1668
|
+
pageUpgrade = allow;
|
|
1669
|
+
const remove = http.mount(DOOR_PREFIX, { upgrade: [`${DOOR_PREFIX}/channel/ws`] });
|
|
1670
|
+
inner.effect(() => () => {
|
|
1671
|
+
remove();
|
|
1672
|
+
if (pageUpgrade === allow) pageUpgrade = void 0;
|
|
1673
|
+
nodeFallback();
|
|
1674
|
+
}, "acryl-door: the page socket via routerHttp");
|
|
1675
|
+
} catch (cause) {
|
|
1676
|
+
reportError("open the page channel over a WebSocket (the pages use the event stream)", cause);
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
ctx.inject(["nodeServer"], (inner) => {
|
|
1680
|
+
inner.effect(() => {
|
|
1681
|
+
nodeFallback();
|
|
1682
|
+
const onService = inner.on;
|
|
1683
|
+
const off = onService("internal/service", () => {
|
|
1684
|
+
if (ctx.get("routerHttp") !== void 0) endNodeOwner();
|
|
1685
|
+
else nodeFallback();
|
|
1686
|
+
});
|
|
1687
|
+
return () => {
|
|
1688
|
+
off();
|
|
1689
|
+
endNodeOwner();
|
|
1690
|
+
};
|
|
1691
|
+
}, "acryl-door: the page socket on the node server");
|
|
1692
|
+
});
|
|
1693
|
+
ctx.router.route("/", pageSocketRoutes(channel, {
|
|
1445
1694
|
prefix: DOOR_PREFIX,
|
|
1446
|
-
upgrade
|
|
1447
|
-
})
|
|
1695
|
+
upgrade: () => pageUpgrade
|
|
1696
|
+
}));
|
|
1697
|
+
ctx.router.route("/", privateApi(DOOR_PREFIX, pageModule, pageChannelRoutes(channel, { prefix: DOOR_PREFIX }), ...pageOnly ? [] : [
|
|
1698
|
+
shellRoutes(DOOR_PREFIX, secret, shell),
|
|
1699
|
+
operatorRoutes(DOOR_PREFIX, secret, door, reportError),
|
|
1700
|
+
mcpRoutes(DOOR_PREFIX, secret, door, reportError)
|
|
1701
|
+
]));
|
|
1448
1702
|
ctx.inject(["tools"], (inner) => {
|
|
1449
|
-
inner.effect(() => registerChatTools(inner.get("tools"), door), "acryl-door: the verbs in the tools registry");
|
|
1703
|
+
inner.effect(() => registerChatTools(inner.get("tools"), door, pageOnly ? PAGE_VERBS : VERBS), "acryl-door: the verbs in the tools registry");
|
|
1450
1704
|
});
|
|
1451
1705
|
ctx.effect(function* () {
|
|
1452
1706
|
yield ctx.reflect.provide("door", door);
|
|
1453
|
-
|
|
1707
|
+
}, "acryl-door: the door service");
|
|
1708
|
+
if (pageOnly) return;
|
|
1709
|
+
ctx.effect(async () => {
|
|
1710
|
+
let bound = carrier();
|
|
1711
|
+
for (let at = 0; at < 500 && (bound === void 0 || !(bound.port > 0)); at += 1) {
|
|
1712
|
+
await new Promise((done) => setTimeout(done, 10));
|
|
1713
|
+
bound = carrier();
|
|
1714
|
+
}
|
|
1715
|
+
if (bound === void 0) throw new Error("acryl-door: no carrier bound the app (neither acrylWeb nor nodeServer answered)");
|
|
1716
|
+
address.url = loopbackUrl(`http://${bound.host}:${String(bound.port)}`);
|
|
1717
|
+
writeDoorFile(app.root, address.url, secret);
|
|
1454
1718
|
try {
|
|
1455
1719
|
writeShims(app.root, shimTarget());
|
|
1456
1720
|
} catch (cause) {
|
|
1457
1721
|
reportError("write .acryl/bin", cause);
|
|
1458
1722
|
}
|
|
1459
|
-
|
|
1723
|
+
return () => {
|
|
1460
1724
|
removeDoorFile(app.root, secret);
|
|
1461
1725
|
};
|
|
1462
|
-
}, "acryl-door:
|
|
1726
|
+
}, "acryl-door: .acryl/door.json and .acryl/bin/acryl");
|
|
1463
1727
|
}
|
|
1464
1728
|
//#endregion
|
|
1465
|
-
export { Config, DOOR_PREFIX, Door, DoorError, STATE_DIR, VERBS, VERB_SPECS, apply, chatToolName, doorFilePath, inject, mcpTools, name, notFoundPage, readDoorFile };
|
|
1729
|
+
export { Config, DOOR_PREFIX, Door, DoorError, PAGE_VERBS, STATE_DIR, VERBS, VERB_SPECS, apply, assertDoorIsFree, chatToolName, doorFilePath, inject, mcpTools, name, notFoundPage, readDoorFile };
|
package/lib/cordis/inspect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as Acryl } from "../app-CQcGWjrk.js";
|
|
2
2
|
import { i as loopbackOnly } from "../loopback-only-BbasoF7R.js";
|
|
3
|
-
import "../src-
|
|
3
|
+
import "../src-iBJ2ysV9.js";
|
|
4
4
|
import "../middleware-BfG_fp8P.js";
|
|
5
5
|
import { Service } from "@deepseek-ai/cordis";
|
|
6
6
|
//#region ../../plugins/acryl-inspect/src/report.ts
|