acrylblends 1.0.5 → 1.0.6
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/docs/framework/agents/index.md +1 -1
- package/lib/{audit-D5z9uwOP.d.ts → audit-GcLtvNLV.d.ts} +4 -2
- package/lib/cordis/door.d.ts +90 -4
- package/lib/cordis/door.js +524 -13
- package/lib/door/host.d.ts +2 -2
- package/lib/door/host.js +1 -1
- package/lib/door/page.d.ts +3 -1
- package/lib/door/page.js +1 -1
- package/lib/door/protocol.d.ts +1 -1
- package/lib/{protocol-C37gPePD.d.ts → protocol-CR31M7K_.d.ts} +2 -0
- package/lib/{snapshot-text-Bbx43mN0.js → snapshot-text-CtQm3n8n.js} +14 -5
- package/package.json +1 -1
- package/plugins/acryl-door/src/cli/acryl.ts +36 -5
- package/plugins/acryl-door/src/host/channel.ts +14 -6
- package/plugins/acryl-door/src/host/door.ts +78 -10
- package/plugins/acryl-door/src/host/page-routes.ts +3 -1
- package/plugins/acryl-door/src/host/shell.ts +147 -0
- package/plugins/acryl-door/src/host/shim.ts +39 -3
- package/plugins/acryl-door/src/host/verbs.ts +39 -2
- package/plugins/acryl-door/src/index.ts +7 -1
- package/plugins/acryl-door/src/page/channel.ts +3 -1
- package/plugins/acryl-door/src/page/index.ts +2 -1
- package/plugins/acryl-door/src/page/protocol.ts +2 -0
- package/plugins/acryl-door/src/shell/protocol.ts +130 -0
- package/plugins/agent-panel/headless.ts +29 -8
- package/plugins/agent-panel/launch.ts +67 -0
- package/plugins/desktop/door-shell.ts +379 -0
- package/plugins/desktop/main.ts +66 -12
- package/templates/agent/AGENTS.md +2 -0
- package/plugins/acryl-router/src/connection.d.ts +0 -25
- package/plugins/acryl-router/src/core/app-base.d.ts +0 -215
- package/plugins/acryl-router/src/core/app.d.ts +0 -19
- package/plugins/acryl-router/src/core/client/client.d.ts +0 -4
- package/plugins/acryl-router/src/core/client/fetch-result-please.d.ts +0 -35
- package/plugins/acryl-router/src/core/client/index.d.ts +0 -7
- package/plugins/acryl-router/src/core/client/types.d.ts +0 -229
- package/plugins/acryl-router/src/core/client/utils.d.ts +0 -18
- package/plugins/acryl-router/src/core/compose.d.ts +0 -14
- package/plugins/acryl-router/src/core/context.d.ts +0 -443
- package/plugins/acryl-router/src/core/helper/accepts/accepts.d.ts +0 -30
- package/plugins/acryl-router/src/core/helper/accepts/index.d.ts +0 -5
- package/plugins/acryl-router/src/core/helper/conninfo/index.d.ts +0 -5
- package/plugins/acryl-router/src/core/helper/conninfo/types.d.ts +0 -36
- package/plugins/acryl-router/src/core/helper/cookie/index.d.ts +0 -25
- package/plugins/acryl-router/src/core/helper/css/common.d.ts +0 -59
- package/plugins/acryl-router/src/core/helper/css/index.d.ts +0 -80
- package/plugins/acryl-router/src/core/helper/dev/index.d.ts +0 -20
- package/plugins/acryl-router/src/core/helper/factory/index.d.ts +0 -82
- package/plugins/acryl-router/src/core/helper/html/index.d.ts +0 -8
- package/plugins/acryl-router/src/core/helper/proxy/index.d.ts +0 -73
- package/plugins/acryl-router/src/core/helper/route/index.d.ts +0 -75
- package/plugins/acryl-router/src/core/helper/ssg/index.d.ts +0 -7
- package/plugins/acryl-router/src/core/helper/ssg/middleware.d.ts +0 -44
- package/plugins/acryl-router/src/core/helper/ssg/plugins.d.ts +0 -27
- package/plugins/acryl-router/src/core/helper/ssg/ssg.d.ts +0 -91
- package/plugins/acryl-router/src/core/helper/ssg/utils.d.ts +0 -16
- package/plugins/acryl-router/src/core/helper/streaming/index.d.ts +0 -8
- package/plugins/acryl-router/src/core/helper/streaming/sse.d.ts +0 -13
- package/plugins/acryl-router/src/core/helper/streaming/stream.d.ts +0 -3
- package/plugins/acryl-router/src/core/helper/streaming/text.d.ts +0 -3
- package/plugins/acryl-router/src/core/helper/streaming/utils.d.ts +0 -1
- package/plugins/acryl-router/src/core/helper/testing/index.d.ts +0 -13
- package/plugins/acryl-router/src/core/helper/websocket/index.d.ts +0 -70
- package/plugins/acryl-router/src/core/http-exception.d.ts +0 -58
- package/plugins/acryl-router/src/core/index.d.ts +0 -37
- package/plugins/acryl-router/src/core/jsx/base.d.ts +0 -61
- package/plugins/acryl-router/src/core/jsx/children.d.ts +0 -9
- package/plugins/acryl-router/src/core/jsx/components.d.ts +0 -15
- package/plugins/acryl-router/src/core/jsx/constants.d.ts +0 -6
- package/plugins/acryl-router/src/core/jsx/context.d.ts +0 -51
- package/plugins/acryl-router/src/core/jsx/dom/components.d.ts +0 -10
- package/plugins/acryl-router/src/core/jsx/dom/context.d.ts +0 -3
- package/plugins/acryl-router/src/core/jsx/dom/css.d.ts +0 -83
- package/plugins/acryl-router/src/core/jsx/dom/hooks/index.d.ts +0 -39
- package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.d.ts +0 -16
- package/plugins/acryl-router/src/core/jsx/dom/jsx-dev-runtime.d.ts +0 -8
- package/plugins/acryl-router/src/core/jsx/dom/jsx-runtime.d.ts +0 -7
- package/plugins/acryl-router/src/core/jsx/dom/render.d.ts +0 -73
- package/plugins/acryl-router/src/core/jsx/dom/utils.d.ts +0 -1
- package/plugins/acryl-router/src/core/jsx/hooks/index.d.ts +0 -47
- package/plugins/acryl-router/src/core/jsx/index.d.ts +0 -76
- package/plugins/acryl-router/src/core/jsx/intrinsic-element/common.d.ts +0 -6
- package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.d.ts +0 -13
- package/plugins/acryl-router/src/core/jsx/intrinsic-elements.d.ts +0 -735
- package/plugins/acryl-router/src/core/jsx/streaming.d.ts +0 -36
- package/plugins/acryl-router/src/core/jsx/types.d.ts +0 -36
- package/plugins/acryl-router/src/core/jsx/utils.d.ts +0 -4
- package/plugins/acryl-router/src/core/request/constants.d.ts +0 -1
- package/plugins/acryl-router/src/core/request.d.ts +0 -306
- package/plugins/acryl-router/src/core/router/reg-exp-router/index.d.ts +0 -6
- package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.d.ts +0 -7
- package/plugins/acryl-router/src/core/router/reg-exp-router/node.d.ts +0 -13
- package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.d.ts +0 -17
- package/plugins/acryl-router/src/core/router/reg-exp-router/router.d.ts +0 -11
- package/plugins/acryl-router/src/core/router/reg-exp-router/trie.d.ts +0 -8
- package/plugins/acryl-router/src/core/router/smart-router/index.d.ts +0 -5
- package/plugins/acryl-router/src/core/router/smart-router/router.d.ts +0 -11
- package/plugins/acryl-router/src/core/router/trie-router/index.d.ts +0 -5
- package/plugins/acryl-router/src/core/router/trie-router/node.d.ts +0 -6
- package/plugins/acryl-router/src/core/router/trie-router/router.d.ts +0 -7
- package/plugins/acryl-router/src/core/router/utils.d.ts +0 -1
- package/plugins/acryl-router/src/core/router.d.ts +0 -97
- package/plugins/acryl-router/src/core/types.d.ts +0 -573
- package/plugins/acryl-router/src/core/utils/accept.d.ts +0 -6
- package/plugins/acryl-router/src/core/utils/body.d.ts +0 -79
- package/plugins/acryl-router/src/core/utils/buffer.d.ts +0 -17
- package/plugins/acryl-router/src/core/utils/color.d.ts +0 -21
- package/plugins/acryl-router/src/core/utils/concurrent.d.ts +0 -11
- package/plugins/acryl-router/src/core/utils/constants.d.ts +0 -4
- package/plugins/acryl-router/src/core/utils/cookie.d.ts +0 -41
- package/plugins/acryl-router/src/core/utils/crypto.d.ts +0 -15
- package/plugins/acryl-router/src/core/utils/handler.d.ts +0 -6
- package/plugins/acryl-router/src/core/utils/headers.d.ts +0 -8
- package/plugins/acryl-router/src/core/utils/html.d.ts +0 -44
- package/plugins/acryl-router/src/core/utils/http-status.d.ts +0 -32
- package/plugins/acryl-router/src/core/utils/mime.d.ts +0 -70
- package/plugins/acryl-router/src/core/utils/stream.d.ts +0 -31
- package/plugins/acryl-router/src/core/utils/types.d.ts +0 -128
- package/plugins/acryl-router/src/core/utils/url.d.ts +0 -44
- package/plugins/acryl-router/src/core/validator/index.d.ts +0 -7
- package/plugins/acryl-router/src/core/validator/utils.d.ts +0 -29
- package/plugins/acryl-router/src/core/validator/validator.d.ts +0 -16
- package/plugins/acryl-router/src/helpers/index.d.ts +0 -17
- package/plugins/acryl-router/src/helpers/schema-validator.d.ts +0 -54
package/lib/cordis/door.js
CHANGED
|
@@ -1,13 +1,280 @@
|
|
|
1
1
|
import { t as Acryl } from "../app-CQcGWjrk.js";
|
|
2
|
+
import { r as streamSSE } from "../streaming-BlDaph8U.js";
|
|
3
|
+
import "../helpers-BEjcdbgw.js";
|
|
2
4
|
import { n as clientModules } from "../client-modules-drj4mjhj.js";
|
|
3
5
|
import { a as MAX_WAIT_MS, l as UiControlError, p as parseUiRequest, r as MAX_SNAPSHOT_NODES } from "../protocol-Dt0AZBOz.js";
|
|
4
|
-
import { a as INVALID_JSON, c as operatorOnly, d as AuditLog, f as auditPath, i as DOOR_PREFIX, l as privateApi, o as error, p as UiChannel, r as pageChannelRoutes, s as jsonBody, t as renderSnapshot, u as readJson } from "../snapshot-text-
|
|
6
|
+
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-CtQm3n8n.js";
|
|
5
7
|
import "../src-BTBm4OPD.js";
|
|
6
8
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
7
9
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
8
10
|
import z from "@deepseek-ai/schemastery";
|
|
9
11
|
import { randomBytes } from "node:crypto";
|
|
10
12
|
import { spawnSync } from "node:child_process";
|
|
13
|
+
//#region ../../plugins/acryl-door/src/shell/protocol.ts
|
|
14
|
+
var ShellError = class extends Error {
|
|
15
|
+
code;
|
|
16
|
+
constructor(code, message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.name = "ShellError";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
/** A path of the app, as `act navigate` takes one: one leading `/`, and no other site. */
|
|
23
|
+
const APP_PATH = /^\/(?![/\\])[^\s\\]*$/u;
|
|
24
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
|
+
function only(value, allowed) {
|
|
26
|
+
const extra = Object.keys(value).filter((key) => key !== "op" && !allowed.includes(key));
|
|
27
|
+
if (extra.length > 0) throw new ShellError("invalid", `unknown field ${extra.join(", ")}`);
|
|
28
|
+
}
|
|
29
|
+
function windowId(value) {
|
|
30
|
+
if (typeof value.id !== "number" || !Number.isSafeInteger(value.id) || value.id < 1) throw new ShellError("invalid", "id must be the number `window list` prints for the window");
|
|
31
|
+
return value.id;
|
|
32
|
+
}
|
|
33
|
+
function size(value, key) {
|
|
34
|
+
const number = value[key];
|
|
35
|
+
if (typeof number !== "number" || !Number.isInteger(number) || number < 200 || number > 1e4) throw new ShellError("invalid", `${key} must be a whole number of pixels from 200 to 10000`);
|
|
36
|
+
return number;
|
|
37
|
+
}
|
|
38
|
+
/** @throws ShellError `invalid`. */
|
|
39
|
+
function parseShellRequest(op, input) {
|
|
40
|
+
const value = isRecord(input) ? input : {};
|
|
41
|
+
switch (op) {
|
|
42
|
+
case "window-list":
|
|
43
|
+
only(value, []);
|
|
44
|
+
return { op };
|
|
45
|
+
case "window-open":
|
|
46
|
+
only(value, ["path"]);
|
|
47
|
+
if (value.path === void 0) return { op };
|
|
48
|
+
if (typeof value.path !== "string" || !APP_PATH.test(value.path)) throw new ShellError("invalid", "path must be a path of this app, such as /settings (it starts with one /, and names no other site)");
|
|
49
|
+
return {
|
|
50
|
+
op,
|
|
51
|
+
path: value.path
|
|
52
|
+
};
|
|
53
|
+
case "window-focus":
|
|
54
|
+
only(value, ["id"]);
|
|
55
|
+
return {
|
|
56
|
+
op,
|
|
57
|
+
id: windowId(value)
|
|
58
|
+
};
|
|
59
|
+
case "window-close":
|
|
60
|
+
only(value, ["id", "force"]);
|
|
61
|
+
if (value.force !== void 0 && typeof value.force !== "boolean") throw new ShellError("invalid", "force must be true or false");
|
|
62
|
+
return {
|
|
63
|
+
op,
|
|
64
|
+
id: windowId(value),
|
|
65
|
+
...value.force === true ? { force: true } : {}
|
|
66
|
+
};
|
|
67
|
+
case "window-resize":
|
|
68
|
+
only(value, [
|
|
69
|
+
"id",
|
|
70
|
+
"width",
|
|
71
|
+
"height"
|
|
72
|
+
]);
|
|
73
|
+
return {
|
|
74
|
+
op,
|
|
75
|
+
id: windowId(value),
|
|
76
|
+
width: size(value, "width"),
|
|
77
|
+
height: size(value, "height")
|
|
78
|
+
};
|
|
79
|
+
case "menu-list":
|
|
80
|
+
only(value, []);
|
|
81
|
+
return { op };
|
|
82
|
+
case "menu-click":
|
|
83
|
+
only(value, ["path", "force"]);
|
|
84
|
+
if (typeof value.path !== "string" || value.path.trim() === "" || value.path.length > 300) throw new ShellError("invalid", "path is the labels from the top of the menu joined with >, e.g. \"File>New Note\" (`menu list` prints them)");
|
|
85
|
+
if (value.force !== void 0 && typeof value.force !== "boolean") throw new ShellError("invalid", "force must be true or false");
|
|
86
|
+
return {
|
|
87
|
+
op,
|
|
88
|
+
path: value.path,
|
|
89
|
+
...value.force === true ? { force: true } : {}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** @param value - one parsed POST body from the shell; null when it is not a well-formed answer. */
|
|
94
|
+
function parseShellAnswer(value) {
|
|
95
|
+
if (!isRecord(value) || typeof value.id !== "number" || !Number.isSafeInteger(value.id)) return null;
|
|
96
|
+
if (value.t === "result" && isRecord(value.value)) return {
|
|
97
|
+
t: "result",
|
|
98
|
+
id: value.id,
|
|
99
|
+
value: value.value
|
|
100
|
+
};
|
|
101
|
+
if (value.t === "error" && typeof value.code === "string" && typeof value.message === "string") return {
|
|
102
|
+
t: "error",
|
|
103
|
+
id: value.id,
|
|
104
|
+
code: value.code.slice(0, 40),
|
|
105
|
+
message: value.message.slice(0, 500)
|
|
106
|
+
};
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
/** Whatever code the shell used, as one the door knows (an unknown code is `failed`). */
|
|
110
|
+
function shellErrorCode(code) {
|
|
111
|
+
return [
|
|
112
|
+
"invalid",
|
|
113
|
+
"no-such-window",
|
|
114
|
+
"last-window",
|
|
115
|
+
"no-such-menu-item",
|
|
116
|
+
"disabled",
|
|
117
|
+
"quits-app"
|
|
118
|
+
].find((known) => known === code) ?? "failed";
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region ../../plugins/acryl-door/src/host/shell.ts
|
|
122
|
+
/**
|
|
123
|
+
* The desktop shell's channel: the Electron main process of `npm run desktop`, which reaches the door as a page does (an event stream down, a POST up) and answers calls about
|
|
124
|
+
* the app's windows and menu. One shell at a time: a new one replaces the old (the dev server restarted and the shell came back). It needs this run's secret, so no page can open it.
|
|
125
|
+
*/
|
|
126
|
+
const CALL_TIMEOUT_MS = 1e4;
|
|
127
|
+
/** What a verb says when no desktop shell is attached: one line a model can act on. */
|
|
128
|
+
const NO_DESKTOP = "no desktop window is attached; run npm run desktop";
|
|
129
|
+
var ShellChannel = class {
|
|
130
|
+
current;
|
|
131
|
+
pending = /* @__PURE__ */ new Map();
|
|
132
|
+
nextId = 1;
|
|
133
|
+
closed = false;
|
|
134
|
+
attachWaiters = /* @__PURE__ */ new Set();
|
|
135
|
+
get connected() {
|
|
136
|
+
return this.current !== void 0;
|
|
137
|
+
}
|
|
138
|
+
/** @returns the function to call when this stream ends. */
|
|
139
|
+
attach(stream) {
|
|
140
|
+
const previous = this.current;
|
|
141
|
+
const connection = { stream };
|
|
142
|
+
this.current = connection;
|
|
143
|
+
if (previous !== void 0) {
|
|
144
|
+
this.failAll(new ShellError("disconnected", "the desktop shell reconnected before it answered"));
|
|
145
|
+
previous.stream.close();
|
|
146
|
+
}
|
|
147
|
+
for (const waiter of [...this.attachWaiters]) waiter();
|
|
148
|
+
return () => {
|
|
149
|
+
if (this.current !== connection) return;
|
|
150
|
+
this.current = void 0;
|
|
151
|
+
this.failAll(new ShellError("disconnected", "the desktop shell went away before it answered"));
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/** A promise for the shell connecting (it reconnects after the dev server restarted). */
|
|
155
|
+
waitForShell(timeoutMs) {
|
|
156
|
+
if (this.current !== void 0) return Promise.resolve(true);
|
|
157
|
+
return new Promise((resolve) => {
|
|
158
|
+
const done = (value) => {
|
|
159
|
+
clearTimeout(timer);
|
|
160
|
+
this.attachWaiters.delete(waiter);
|
|
161
|
+
resolve(value);
|
|
162
|
+
};
|
|
163
|
+
const waiter = () => {
|
|
164
|
+
done(true);
|
|
165
|
+
};
|
|
166
|
+
const timer = setTimeout(() => {
|
|
167
|
+
done(false);
|
|
168
|
+
}, timeoutMs);
|
|
169
|
+
this.attachWaiters.add(waiter);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
receive(body) {
|
|
173
|
+
const answer = parseShellAnswer(body);
|
|
174
|
+
if (answer === null) return;
|
|
175
|
+
if (answer.t === "result") this.settle(answer.id, answer.value);
|
|
176
|
+
else this.settle(answer.id, void 0, new ShellError(shellErrorCode(answer.code), answer.message));
|
|
177
|
+
}
|
|
178
|
+
/** @throws ShellError `no-desktop` when no shell is attached, else what the shell refused with. */
|
|
179
|
+
call(request, options = {}) {
|
|
180
|
+
if (this.closed) return Promise.reject(new ShellError("unloaded", "the door was unloaded"));
|
|
181
|
+
const target = this.current;
|
|
182
|
+
if (target === void 0) return Promise.reject(new ShellError("no-desktop", NO_DESKTOP));
|
|
183
|
+
const signal = options.signal;
|
|
184
|
+
if (signal?.aborted === true) return Promise.reject(new ShellError("aborted", "the call was cancelled"));
|
|
185
|
+
const id = this.nextId;
|
|
186
|
+
this.nextId += 1;
|
|
187
|
+
return new Promise((resolve, reject) => {
|
|
188
|
+
const timer = setTimeout(() => {
|
|
189
|
+
this.settle(id, void 0, new ShellError("timeout", "the desktop shell did not answer in time"));
|
|
190
|
+
}, options.timeoutMs ?? CALL_TIMEOUT_MS);
|
|
191
|
+
const onAbort = () => {
|
|
192
|
+
this.settle(id, void 0, new ShellError("aborted", "the call was cancelled"));
|
|
193
|
+
};
|
|
194
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
195
|
+
this.pending.set(id, {
|
|
196
|
+
resolve,
|
|
197
|
+
reject,
|
|
198
|
+
timer,
|
|
199
|
+
cleanup: () => {
|
|
200
|
+
signal?.removeEventListener("abort", onAbort);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
try {
|
|
204
|
+
target.stream.send(JSON.stringify({
|
|
205
|
+
t: "call",
|
|
206
|
+
id,
|
|
207
|
+
request
|
|
208
|
+
}));
|
|
209
|
+
} catch {
|
|
210
|
+
this.settle(id, void 0, new ShellError("disconnected", "the desktop shell could not be reached"));
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
close() {
|
|
215
|
+
this.closed = true;
|
|
216
|
+
this.failAll(new ShellError("unloaded", "the door was unloaded"));
|
|
217
|
+
this.current?.stream.close();
|
|
218
|
+
this.current = void 0;
|
|
219
|
+
}
|
|
220
|
+
failAll(failure) {
|
|
221
|
+
for (const id of [...this.pending.keys()]) this.settle(id, void 0, failure);
|
|
222
|
+
}
|
|
223
|
+
settle(id, value, failure) {
|
|
224
|
+
const entry = this.pending.get(id);
|
|
225
|
+
if (entry === void 0) return;
|
|
226
|
+
this.pending.delete(id);
|
|
227
|
+
clearTimeout(entry.timer);
|
|
228
|
+
entry.cleanup();
|
|
229
|
+
if (failure !== void 0) entry.reject(failure);
|
|
230
|
+
else entry.resolve(value);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
/** How often a `ping` is written, so the shell knows a silent stream died and reconnects. */
|
|
234
|
+
const SHELL_HEARTBEAT_MS = 15e3;
|
|
235
|
+
/** `GET <prefix>/shell/events` is the shell's stream and `POST <prefix>/shell/answer` carries its answers: both need this run's secret. */
|
|
236
|
+
function shellRoutes(prefix, secret, channel, heartbeatMs = SHELL_HEARTBEAT_MS) {
|
|
237
|
+
return new Acryl().get(`${prefix}/shell/events`, operatorOnly(secret), (c) => {
|
|
238
|
+
const signal = c.req.raw.signal;
|
|
239
|
+
return streamSSE(c, async (stream) => {
|
|
240
|
+
let end = () => {};
|
|
241
|
+
const ended = new Promise((resolve) => {
|
|
242
|
+
end = resolve;
|
|
243
|
+
});
|
|
244
|
+
stream.onAbort(end);
|
|
245
|
+
signal.addEventListener("abort", end, { once: true });
|
|
246
|
+
const write = (event, data) => {
|
|
247
|
+
stream.writeSSE({
|
|
248
|
+
event,
|
|
249
|
+
data
|
|
250
|
+
}).catch(end);
|
|
251
|
+
};
|
|
252
|
+
const detach = channel.attach({
|
|
253
|
+
send: (data) => {
|
|
254
|
+
write("call", data);
|
|
255
|
+
},
|
|
256
|
+
close: end
|
|
257
|
+
});
|
|
258
|
+
const beat = setInterval(() => {
|
|
259
|
+
write("ping", "");
|
|
260
|
+
}, heartbeatMs);
|
|
261
|
+
try {
|
|
262
|
+
write("ready", "");
|
|
263
|
+
await ended;
|
|
264
|
+
} finally {
|
|
265
|
+
clearInterval(beat);
|
|
266
|
+
signal.removeEventListener("abort", end);
|
|
267
|
+
detach();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}).post(`${prefix}/shell/answer`, operatorOnly(secret), jsonBody, async (c) => {
|
|
271
|
+
const body = await readJson(c);
|
|
272
|
+
if (body === INVALID_JSON || typeof body !== "object" || body === null) return c.json(error("invalid request body"), 400);
|
|
273
|
+
channel.receive(body);
|
|
274
|
+
return c.json({ ok: true });
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
//#endregion
|
|
11
278
|
//#region ../../plugins/acryl-door/src/host/verbs.ts
|
|
12
279
|
/**
|
|
13
280
|
* The one verb list (spec 049 slice 13). Every face of the door reads it: the operator client (`acryl <verb>`, its `verbs` and `brief`),
|
|
@@ -23,7 +290,14 @@ const VERBS = [
|
|
|
23
290
|
"plugin-remove",
|
|
24
291
|
"plugin-reload",
|
|
25
292
|
"tool-list",
|
|
26
|
-
"tool-call"
|
|
293
|
+
"tool-call",
|
|
294
|
+
"window-list",
|
|
295
|
+
"window-open",
|
|
296
|
+
"window-focus",
|
|
297
|
+
"window-close",
|
|
298
|
+
"window-resize",
|
|
299
|
+
"menu-list",
|
|
300
|
+
"menu-click"
|
|
27
301
|
];
|
|
28
302
|
const none = {
|
|
29
303
|
type: "object",
|
|
@@ -37,7 +311,7 @@ const VERB_SPECS = {
|
|
|
37
311
|
arguments: none
|
|
38
312
|
},
|
|
39
313
|
"look": {
|
|
40
|
-
usage: "look [--cursor <n>] [--max-nodes <n>]: the page as text, with refs",
|
|
314
|
+
usage: "look [--window <n>] [--cursor <n>] [--max-nodes <n>]: the page as text, with refs",
|
|
41
315
|
description: "The open page as text: its roles, names, values and visible text, each with a ref to act on. A long page comes in parts (cursor).",
|
|
42
316
|
arguments: {
|
|
43
317
|
type: "object",
|
|
@@ -51,6 +325,11 @@ const VERB_SPECS = {
|
|
|
51
325
|
type: "integer",
|
|
52
326
|
minimum: 1,
|
|
53
327
|
maximum: MAX_SNAPSHOT_NODES
|
|
328
|
+
},
|
|
329
|
+
window: {
|
|
330
|
+
type: "integer",
|
|
331
|
+
minimum: 1,
|
|
332
|
+
description: "a desktop window (the id window-list prints); default: the page used last"
|
|
54
333
|
}
|
|
55
334
|
},
|
|
56
335
|
additionalProperties: false
|
|
@@ -134,6 +413,11 @@ const VERB_SPECS = {
|
|
|
134
413
|
type: "integer",
|
|
135
414
|
minimum: 0,
|
|
136
415
|
maximum: MAX_WAIT_MS
|
|
416
|
+
},
|
|
417
|
+
window: {
|
|
418
|
+
type: "integer",
|
|
419
|
+
minimum: 1,
|
|
420
|
+
description: "a desktop window (the id window-list prints); default: the page used last"
|
|
137
421
|
}
|
|
138
422
|
},
|
|
139
423
|
required: ["op"],
|
|
@@ -190,6 +474,109 @@ const VERB_SPECS = {
|
|
|
190
474
|
required: ["tool"],
|
|
191
475
|
additionalProperties: false
|
|
192
476
|
}
|
|
477
|
+
},
|
|
478
|
+
"window-list": {
|
|
479
|
+
usage: "window list: the windows of the desktop app",
|
|
480
|
+
description: "The windows of the desktop app: id, title, path, size, and which one is focused. Needs the desktop app (npm run desktop).",
|
|
481
|
+
arguments: none
|
|
482
|
+
},
|
|
483
|
+
"window-open": {
|
|
484
|
+
usage: "window open [--path <path of the app>]: open a new window",
|
|
485
|
+
description: "Open a new window of the desktop app on a path of the app (default: its start page). Answers with the new window's id; look and act take it as window.",
|
|
486
|
+
arguments: {
|
|
487
|
+
type: "object",
|
|
488
|
+
properties: { path: {
|
|
489
|
+
type: "string",
|
|
490
|
+
description: "a path of this app, e.g. /settings"
|
|
491
|
+
} },
|
|
492
|
+
additionalProperties: false
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
"window-focus": {
|
|
496
|
+
usage: "window focus --id <n>: bring a window to the front",
|
|
497
|
+
description: "Bring a window of the desktop app to the front. Look and act without a window then drive it.",
|
|
498
|
+
arguments: {
|
|
499
|
+
type: "object",
|
|
500
|
+
properties: { id: {
|
|
501
|
+
type: "integer",
|
|
502
|
+
minimum: 1,
|
|
503
|
+
description: "the id window-list prints"
|
|
504
|
+
} },
|
|
505
|
+
required: ["id"],
|
|
506
|
+
additionalProperties: false
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
"window-close": {
|
|
510
|
+
usage: "window close --id <n> [--force]: close a window",
|
|
511
|
+
description: "Close a window of the desktop app. The last window cannot be closed without force, because that ends the desktop app.",
|
|
512
|
+
arguments: {
|
|
513
|
+
type: "object",
|
|
514
|
+
properties: {
|
|
515
|
+
id: {
|
|
516
|
+
type: "integer",
|
|
517
|
+
minimum: 1
|
|
518
|
+
},
|
|
519
|
+
force: {
|
|
520
|
+
type: "boolean",
|
|
521
|
+
description: "allow closing the last window, which ends the desktop app"
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
required: ["id"],
|
|
525
|
+
additionalProperties: false
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
"window-resize": {
|
|
529
|
+
usage: "window resize --id <n> --width <px> --height <px>",
|
|
530
|
+
description: "Resize a window of the desktop app.",
|
|
531
|
+
arguments: {
|
|
532
|
+
type: "object",
|
|
533
|
+
properties: {
|
|
534
|
+
id: {
|
|
535
|
+
type: "integer",
|
|
536
|
+
minimum: 1
|
|
537
|
+
},
|
|
538
|
+
width: {
|
|
539
|
+
type: "integer",
|
|
540
|
+
minimum: 200,
|
|
541
|
+
maximum: 1e4
|
|
542
|
+
},
|
|
543
|
+
height: {
|
|
544
|
+
type: "integer",
|
|
545
|
+
minimum: 200,
|
|
546
|
+
maximum: 1e4
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
required: [
|
|
550
|
+
"id",
|
|
551
|
+
"width",
|
|
552
|
+
"height"
|
|
553
|
+
],
|
|
554
|
+
additionalProperties: false
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
"menu-list": {
|
|
558
|
+
usage: "menu list: the application menu",
|
|
559
|
+
description: "The desktop app's application menu as paths such as File>New Note, with the disabled items marked.",
|
|
560
|
+
arguments: none
|
|
561
|
+
},
|
|
562
|
+
"menu-click": {
|
|
563
|
+
usage: "menu click --path \"File>New Note\" [--force]: press a menu item",
|
|
564
|
+
description: "Press an item of the desktop app's application menu by its path (labels joined with >, as menu-list prints them). A disabled item is refused, and so is one that ends the app (Quit, Close Window on the last window) unless force is set.",
|
|
565
|
+
arguments: {
|
|
566
|
+
type: "object",
|
|
567
|
+
properties: {
|
|
568
|
+
path: {
|
|
569
|
+
type: "string",
|
|
570
|
+
description: "e.g. File>New Note"
|
|
571
|
+
},
|
|
572
|
+
force: {
|
|
573
|
+
type: "boolean",
|
|
574
|
+
description: "allow an item that ends the desktop app"
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
required: ["path"],
|
|
578
|
+
additionalProperties: false
|
|
579
|
+
}
|
|
193
580
|
}
|
|
194
581
|
};
|
|
195
582
|
/** A verb's name in the `tools` registry the built-in chat reads: prefixed, so it never meets an app tool's name. */
|
|
@@ -209,6 +596,12 @@ var DoorError = class extends Error {
|
|
|
209
596
|
this.name = "DoorError";
|
|
210
597
|
}
|
|
211
598
|
};
|
|
599
|
+
/** A shell answer as text an agent reads: windows and menu items one to a line. */
|
|
600
|
+
function renderShell(result) {
|
|
601
|
+
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.focused ? " focused" : ""}`).join("\n");
|
|
602
|
+
if ("items" in result) return result.items.length === 0 ? "the app has no menu" : result.items.map((item) => `${item.path}${item.enabled ? "" : " (disabled)"}${item.ends === true ? " (ends the app; needs force)" : ""}`).join("\n");
|
|
603
|
+
return result.detail;
|
|
604
|
+
}
|
|
212
605
|
/** The files an agent should read first, when the app has them. */
|
|
213
606
|
const SELF_KNOWLEDGE = [
|
|
214
607
|
"AGENTS.md",
|
|
@@ -260,6 +653,13 @@ var Door = class {
|
|
|
260
653
|
};
|
|
261
654
|
}
|
|
262
655
|
case "tool-call": return this.callTool(input);
|
|
656
|
+
case "window-list":
|
|
657
|
+
case "window-open":
|
|
658
|
+
case "window-focus":
|
|
659
|
+
case "window-close":
|
|
660
|
+
case "window-resize":
|
|
661
|
+
case "menu-list":
|
|
662
|
+
case "menu-click": return this.desktop(verb, input, signal);
|
|
263
663
|
default: throw new DoorError("unknown-verb", `unknown verb ${JSON.stringify(verb)}; the verbs are ${VERBS.join(", ")}`);
|
|
264
664
|
}
|
|
265
665
|
}
|
|
@@ -289,20 +689,22 @@ var Door = class {
|
|
|
289
689
|
};
|
|
290
690
|
}
|
|
291
691
|
look(input, signal) {
|
|
692
|
+
const { window, rest } = this.windowOf(input);
|
|
292
693
|
const request = this.parse({
|
|
293
|
-
...
|
|
694
|
+
...rest,
|
|
294
695
|
op: "snapshot"
|
|
295
696
|
});
|
|
296
|
-
return this.callPage("look", request, signal);
|
|
697
|
+
return this.callPage("look", request, signal, window);
|
|
297
698
|
}
|
|
298
699
|
async act(input, signal) {
|
|
299
700
|
if (input.op === "snapshot") throw new DoorError("invalid", "use look to see the page");
|
|
300
|
-
const
|
|
301
|
-
|
|
701
|
+
const { window, rest } = this.windowOf(input);
|
|
702
|
+
const request = this.parse(rest);
|
|
703
|
+
if (request.op !== "reload" && request.op !== "navigate") return await this.callPage(`act ${String(input.op)}`, request, signal, window);
|
|
302
704
|
const back = this.deps.channel.expectAttach(this.deps.pageBackMs ?? PAGE_BACK_MS);
|
|
303
705
|
let answer;
|
|
304
706
|
try {
|
|
305
|
-
answer = await this.callPage(`act ${request.op}`, request, signal);
|
|
707
|
+
answer = await this.callPage(`act ${request.op}`, request, signal, window);
|
|
306
708
|
} catch (cause) {
|
|
307
709
|
if (!(cause instanceof UiControlError && cause.code === "disconnected")) throw cause;
|
|
308
710
|
answer = {
|
|
@@ -335,6 +737,73 @@ var Door = class {
|
|
|
335
737
|
return;
|
|
336
738
|
}
|
|
337
739
|
}
|
|
740
|
+
/**
|
|
741
|
+
* The windows and the menu of the desktop app, through the shell. Every call is audited with the window it was about; a call with no shell attached answers one line that says what to run;
|
|
742
|
+
* closing the last window is refused unless the call says `force` (it ends the desktop app, and the session an agent works in with it).
|
|
743
|
+
*/
|
|
744
|
+
async desktop(verb, input, signal) {
|
|
745
|
+
const shell = this.deps.shell;
|
|
746
|
+
let request;
|
|
747
|
+
try {
|
|
748
|
+
request = parseShellRequest(verb, input);
|
|
749
|
+
} catch (cause) {
|
|
750
|
+
throw new DoorError("invalid", cause instanceof Error ? cause.message : "invalid request");
|
|
751
|
+
}
|
|
752
|
+
const windowId = "id" in request ? request.id : void 0;
|
|
753
|
+
const record = (outcome, detail, windowOf) => {
|
|
754
|
+
const id = windowOf ?? windowId;
|
|
755
|
+
this.deps.audit.record({
|
|
756
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
757
|
+
tool: verb,
|
|
758
|
+
...request.op === "menu-click" ? { target: {
|
|
759
|
+
role: "menuitem",
|
|
760
|
+
name: request.path
|
|
761
|
+
} } : {},
|
|
762
|
+
...id === void 0 ? {} : { window: id },
|
|
763
|
+
outcome,
|
|
764
|
+
...detail === void 0 ? {} : { detail },
|
|
765
|
+
approval: "token"
|
|
766
|
+
});
|
|
767
|
+
};
|
|
768
|
+
try {
|
|
769
|
+
if (shell === void 0 || !shell.connected) throw new ShellError("no-desktop", NO_DESKTOP);
|
|
770
|
+
if (request.op === "window-close" && request.force !== true) {
|
|
771
|
+
const { windows } = await shell.call({ op: "window-list" }, { signal });
|
|
772
|
+
if (windows.length === 1 && windows[0]?.id === request.id) throw new ShellError("last-window", `window ${String(request.id)} is the app's last window: closing it ends the desktop app. Open another window first, or pass force to end it on purpose`);
|
|
773
|
+
}
|
|
774
|
+
const result = await shell.call(request, { signal });
|
|
775
|
+
record("ok", void 0, "id" in result ? result.id : void 0);
|
|
776
|
+
return {
|
|
777
|
+
value: result,
|
|
778
|
+
text: renderShell(result)
|
|
779
|
+
};
|
|
780
|
+
} catch (cause) {
|
|
781
|
+
if (!(cause instanceof ShellError)) throw cause;
|
|
782
|
+
record([
|
|
783
|
+
"invalid",
|
|
784
|
+
"no-such-window",
|
|
785
|
+
"last-window",
|
|
786
|
+
"no-such-menu-item",
|
|
787
|
+
"disabled",
|
|
788
|
+
"quits-app",
|
|
789
|
+
"no-desktop"
|
|
790
|
+
].includes(cause.code) ? "refused" : "failed", cause.code);
|
|
791
|
+
throw new DoorError(cause.code, cause.message);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
/** `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. */
|
|
795
|
+
windowOf(input) {
|
|
796
|
+
const { window, ...rest } = input;
|
|
797
|
+
if (window === void 0) return {
|
|
798
|
+
window: void 0,
|
|
799
|
+
rest
|
|
800
|
+
};
|
|
801
|
+
if (typeof window !== "number" || !Number.isSafeInteger(window) || window < 1) throw new DoorError("invalid", "window must be the number `window list` prints for the window");
|
|
802
|
+
return {
|
|
803
|
+
window,
|
|
804
|
+
rest
|
|
805
|
+
};
|
|
806
|
+
}
|
|
338
807
|
parse(input) {
|
|
339
808
|
try {
|
|
340
809
|
return parseUiRequest(input);
|
|
@@ -343,11 +812,12 @@ var Door = class {
|
|
|
343
812
|
}
|
|
344
813
|
}
|
|
345
814
|
/** Run one call on the page and record it. The audit holds which control was touched and how it ended, never what was typed. */
|
|
346
|
-
async callPage(verb, request, signal) {
|
|
815
|
+
async callPage(verb, request, signal, window) {
|
|
347
816
|
const approval = "token";
|
|
348
817
|
try {
|
|
349
818
|
const value = await this.deps.channel.call(request, {
|
|
350
819
|
signal,
|
|
820
|
+
...window === void 0 ? {} : { window },
|
|
351
821
|
...request.op === "wait" ? { timeoutMs: (request.timeoutMs ?? 3e3) + 5e3 } : {}
|
|
352
822
|
});
|
|
353
823
|
const target = "target" in value ? value.target : void 0;
|
|
@@ -355,6 +825,7 @@ var Door = class {
|
|
|
355
825
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
356
826
|
tool: verb,
|
|
357
827
|
...target === void 0 ? {} : { target },
|
|
828
|
+
...window === void 0 ? {} : { window },
|
|
358
829
|
outcome: "ok",
|
|
359
830
|
approval
|
|
360
831
|
});
|
|
@@ -373,6 +844,7 @@ var Door = class {
|
|
|
373
844
|
this.deps.audit.record({
|
|
374
845
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
375
846
|
tool: verb,
|
|
847
|
+
...window === void 0 ? {} : { window },
|
|
376
848
|
outcome: refused ? "refused" : "failed",
|
|
377
849
|
detail: code,
|
|
378
850
|
approval
|
|
@@ -735,11 +1207,41 @@ function windowsShim(root, target) {
|
|
|
735
1207
|
].map(cmdWord).join(" ");
|
|
736
1208
|
return `@echo off\r\nsetlocal\r\nrem This app's door (acryl-door): reaches only the app running from this folder. Rewritten each time the app starts.\r\n${target.electron ? "set ELECTRON_RUN_AS_NODE=1\r\n" : ""}${words} %*\r\n`;
|
|
737
1209
|
}
|
|
738
|
-
|
|
1210
|
+
/** The error codes with which Windows refuses to replace a file that a running process (a `cmd.exe` executing the batch file) has open. */
|
|
1211
|
+
const LOCKED = /* @__PURE__ */ new Set([
|
|
1212
|
+
"EPERM",
|
|
1213
|
+
"EBUSY",
|
|
1214
|
+
"EACCES"
|
|
1215
|
+
]);
|
|
1216
|
+
const realFs = {
|
|
1217
|
+
rename: renameSync,
|
|
1218
|
+
pause: (ms) => {
|
|
1219
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
/**
|
|
1223
|
+
* Write `content` to `file` so a reader never sees half of it. A shim that already holds this content is left alone: a restart of the app (the dev server restarts on a change)
|
|
1224
|
+
* rewrites the same text, and on Windows replacing a batch file that an agent's `acryl` call is running fails with EPERM, which stopped the app from starting. A file that
|
|
1225
|
+
* is held while its content differs is retried for about a second, and then reported: the old shim stays (it still works), and the app starts.
|
|
1226
|
+
*/
|
|
1227
|
+
function writeAtomically(file, content, mode, fs = realFs) {
|
|
1228
|
+
try {
|
|
1229
|
+
if (readFileSync(file, "utf8") === content) return;
|
|
1230
|
+
} catch {}
|
|
739
1231
|
const temporary = `${file}.${String(process.pid)}.tmp`;
|
|
740
1232
|
writeFileSync(temporary, content, { mode });
|
|
741
1233
|
if (process.platform !== "win32") chmodSync(temporary, mode);
|
|
742
|
-
|
|
1234
|
+
for (let attempt = 0;; attempt += 1) try {
|
|
1235
|
+
fs.rename(temporary, file);
|
|
1236
|
+
return;
|
|
1237
|
+
} catch (cause) {
|
|
1238
|
+
const code = cause.code;
|
|
1239
|
+
if (code === void 0 || !LOCKED.has(code) || attempt >= 10) {
|
|
1240
|
+
rmSync(temporary, { force: true });
|
|
1241
|
+
throw cause;
|
|
1242
|
+
}
|
|
1243
|
+
fs.pause(25 * (attempt + 1));
|
|
1244
|
+
}
|
|
743
1245
|
}
|
|
744
1246
|
/** Write `.acryl/bin/acryl` and `.acryl/bin/acryl.cmd` for the app at `root`. @returns the folder they are in. */
|
|
745
1247
|
function writeShims(root, target) {
|
|
@@ -815,8 +1317,10 @@ function apply(ctx, rawConfig = {}) {
|
|
|
815
1317
|
};
|
|
816
1318
|
const secret = newSecret();
|
|
817
1319
|
const channel = new UiChannel();
|
|
1320
|
+
const shell = new ShellChannel();
|
|
818
1321
|
const door = new Door({
|
|
819
1322
|
channel,
|
|
1323
|
+
shell,
|
|
820
1324
|
audit: new AuditLog(auditPath(stateDir(app.root))),
|
|
821
1325
|
app: {
|
|
822
1326
|
name: app.name,
|
|
@@ -831,6 +1335,9 @@ function apply(ctx, rawConfig = {}) {
|
|
|
831
1335
|
ctx.effect(() => () => {
|
|
832
1336
|
channel.close();
|
|
833
1337
|
}, "acryl-door: page channel");
|
|
1338
|
+
ctx.effect(() => () => {
|
|
1339
|
+
shell.close();
|
|
1340
|
+
}, "acryl-door: desktop shell channel");
|
|
834
1341
|
const pageModule = new Acryl().get(`${DOOR_PREFIX}/page.js`, (c) => c.body(`import '${DOOR_PREFIX}/page/main.ts'\n`, 200, { "content-type": "text/javascript; charset=utf-8" })).route(`${DOOR_PREFIX}/page`, clientModules({ root: PAGE_DIR }));
|
|
835
1342
|
const tag = `<script type="module" src="${DOOR_PREFIX}/page.js"><\/script>`;
|
|
836
1343
|
const addScript = async (c, next) => {
|
|
@@ -854,14 +1361,18 @@ function apply(ctx, rawConfig = {}) {
|
|
|
854
1361
|
const router = ctx.router;
|
|
855
1362
|
if (typeof router.wrap === "function") router.wrap(addScript);
|
|
856
1363
|
else ctx.router.route("/", new Acryl().use("*", addScript));
|
|
857
|
-
ctx.router.route("/", privateApi(DOOR_PREFIX, pageModule, pageChannelRoutes(channel, { prefix: DOOR_PREFIX }), operatorRoutes(DOOR_PREFIX, secret, door, reportError), mcpRoutes(DOOR_PREFIX, secret, door, reportError)));
|
|
1364
|
+
ctx.router.route("/", privateApi(DOOR_PREFIX, pageModule, pageChannelRoutes(channel, { prefix: DOOR_PREFIX }), shellRoutes(DOOR_PREFIX, secret, shell), operatorRoutes(DOOR_PREFIX, secret, door, reportError), mcpRoutes(DOOR_PREFIX, secret, door, reportError)));
|
|
858
1365
|
ctx.inject(["tools"], (inner) => {
|
|
859
1366
|
inner.effect(() => registerChatTools(inner.get("tools"), door), "acryl-door: the verbs in the tools registry");
|
|
860
1367
|
});
|
|
861
1368
|
ctx.effect(function* () {
|
|
862
1369
|
yield ctx.reflect.provide("door", door);
|
|
863
1370
|
writeDoorFile(app.root, loopbackUrl(server.url), secret);
|
|
864
|
-
|
|
1371
|
+
try {
|
|
1372
|
+
writeShims(app.root, shimTarget());
|
|
1373
|
+
} catch (cause) {
|
|
1374
|
+
reportError("write .acryl/bin", cause);
|
|
1375
|
+
}
|
|
865
1376
|
yield () => {
|
|
866
1377
|
removeDoorFile(app.root, secret);
|
|
867
1378
|
};
|
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-CR31M7K_.js";
|
|
3
|
+
import { i as UiChannel, n as auditPath, r as PageStream, t as AuditLog } from "../audit-GcLtvNLV.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-CtQm3n8n.js";
|
|
2
2
|
export { AuditLog, HEARTBEAT_MS, UiChannel, auditPath, pageChannelRoutes, renderSnapshot };
|