acrylblends 1.0.10 → 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 +60 -2
- package/dist/index.js +1992 -33
- package/dist/index.js.map +1 -1
- package/docs/framework/agents/index.md +1 -1
- package/docs/framework/api/summary.md +1 -1
- package/docs/framework/getting-started/desktop.md +23 -4
- package/docs/framework/guides/create-acrylblends.md +1 -1
- package/docs/framework/guides/production.md +20 -0
- package/docs/framework/workshop/README.md +174 -285
- package/docs/framework/workshop/facilitator.md +81 -90
- 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-Dw-siYTp.d.ts → audit-Bok_UNCn.d.ts} +41 -6
- package/lib/{client-modules-drj4mjhj.js → client-modules-Bkdr83jO.js} +1 -1
- package/lib/cordis/door.d.ts +44 -7
- package/lib/cordis/door.js +214 -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.d.ts +3 -2
- package/lib/cordis/state.js +371 -7
- package/lib/door/host.d.ts +2 -2
- package/lib/door/host.js +1 -1
- package/lib/door/page.d.ts +20 -1
- package/lib/door/page.js +75 -14
- 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-BfEbEOjA.js → snapshot-text-CXTnjxGx.js} +121 -9
- 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 +124 -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 +58 -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 +11 -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 +49 -7
- package/plugins/acryl-door/src/page/driver/snapshot.ts +5 -2
- package/plugins/acryl-door/src/page/index.ts +46 -2
- package/plugins/acryl-door/src/page/indicator.ts +14 -6
- 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-state/plugin.json +4 -3
- package/plugins/acryl-state/src/client-source.ts +244 -0
- package/plugins/acryl-state/src/service.ts +33 -6
- package/plugins/acryl-state/src/socket.ts +96 -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 +343 -81
- package/plugins/agent-panel/drivers.ts +7 -2
- package/plugins/agent-panel/headless.ts +5 -2
- package/plugins/agent-panel/index.ts +187 -61
- 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 +12 -3
- 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 +4 -2
- 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 +73 -21
- package/templates/agent/template.json +2 -0
- package/templates/agent/test/app.test.ts +83 -5
- 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/main.ts +1 -6
- 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/main.ts +1 -6
- 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/main.ts +1 -6
- 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
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import { Acryl } from '../plugins/acryl-router/src/index.ts'
|
|
3
|
+
import { aboutPage } from './about.tsx'
|
|
4
|
+
|
|
5
|
+
export const APP_NAME = '__APP_NAME__'
|
|
6
|
+
|
|
7
|
+
/** The app's own routes: an `App` mounted on the router. The mount is removed when this plugin is disposed. */
|
|
8
|
+
export const routes = {
|
|
9
|
+
name: 'routes',
|
|
10
|
+
inject: ['router'],
|
|
11
|
+
apply(ctx: Context) {
|
|
12
|
+
const app = new Acryl()
|
|
13
|
+
.get('/', (c) => c.json({ app: APP_NAME, ok: true }))
|
|
14
|
+
.get('/health', (c) => c.json({ ok: true }))
|
|
15
|
+
.get('/about', (c) => c.html(aboutPage()))
|
|
16
|
+
ctx.router.route('/', app)
|
|
17
|
+
},
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import type {} from './plugins/acryl-router/src/index.ts'
|
|
3
|
+
|
|
4
|
+
export const APP_NAME = '__APP_NAME__'
|
|
5
|
+
|
|
6
|
+
/** The app's own routes on the router. A route registered from this plugin's context is removed when the plugin is disposed. */
|
|
7
|
+
export const routes = {
|
|
8
|
+
name: 'routes',
|
|
9
|
+
inject: ['router'],
|
|
10
|
+
apply(ctx: Context) {
|
|
11
|
+
ctx.router.get('/', (c) => c.json({ app: APP_NAME, ok: true }))
|
|
12
|
+
ctx.router.get('/health', (c) => c.json({ ok: true }))
|
|
13
|
+
},
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import { App } from './plugins/acryl-router/src/index.ts'
|
|
3
|
+
|
|
4
|
+
export const APP_NAME = '__APP_NAME__'
|
|
5
|
+
|
|
6
|
+
/** The app's own routes: an `App` mounted on the router. The mount is removed when this plugin is disposed. */
|
|
7
|
+
export const routes = {
|
|
8
|
+
name: 'routes',
|
|
9
|
+
inject: ['router'],
|
|
10
|
+
apply(ctx: Context) {
|
|
11
|
+
const app = new App()
|
|
12
|
+
.get('/', (c) => c.json({ app: APP_NAME, ok: true }))
|
|
13
|
+
.get('/health', (c) => c.json({ ok: true }))
|
|
14
|
+
ctx.router.route('/', app)
|
|
15
|
+
},
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import { Acryl } from './plugins/acryl-router/src/index.ts'
|
|
3
|
+
|
|
4
|
+
export const APP_NAME = '__APP_NAME__'
|
|
5
|
+
|
|
6
|
+
/** The app's own routes: an `App` mounted on the router. The mount is removed when this plugin is disposed. */
|
|
7
|
+
export const routes = {
|
|
8
|
+
name: 'routes',
|
|
9
|
+
inject: ['router'],
|
|
10
|
+
apply(ctx: Context) {
|
|
11
|
+
const app = new Acryl()
|
|
12
|
+
.get('/', (c) => c.json({ app: APP_NAME, ok: true }))
|
|
13
|
+
.get('/health', (c) => c.json({ ok: true }))
|
|
14
|
+
ctx.router.route('/', app)
|
|
15
|
+
},
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import { aboutPage } from './about.tsx'
|
|
3
|
+
import { Acryl } from './plugins/acryl-router/src/index.ts'
|
|
4
|
+
|
|
5
|
+
export const APP_NAME = '__APP_NAME__'
|
|
6
|
+
|
|
7
|
+
/** The app's own routes: an `App` mounted on the router. The mount is removed when this plugin is disposed. */
|
|
8
|
+
export const routes = {
|
|
9
|
+
name: 'routes',
|
|
10
|
+
inject: ['router'],
|
|
11
|
+
apply(ctx: Context) {
|
|
12
|
+
const app = new Acryl()
|
|
13
|
+
.get('/', (c) => c.json({ app: APP_NAME, ok: true }))
|
|
14
|
+
.get('/health', (c) => c.json({ ok: true }))
|
|
15
|
+
.get('/about', (c) => c.html(aboutPage()))
|
|
16
|
+
ctx.router.route('/', app)
|
|
17
|
+
},
|
|
18
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { isAbsolute, join, resolve } from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { Context, type Fiber } from '@deepseek-ai/cordis'
|
|
4
|
+
import * as db from '../plugins/acryl-db-sqlite/src/index.ts'
|
|
5
|
+
import * as nodeServer from '../plugins/acryl-node-server/src/index.ts'
|
|
6
|
+
import * as router from '../plugins/acryl-router/src/index.ts'
|
|
7
|
+
import { type AppInstance, instance } from '../plugins/instance/index.ts'
|
|
8
|
+
import {
|
|
9
|
+
blueprintsIn,
|
|
10
|
+
compose,
|
|
11
|
+
loadManifest,
|
|
12
|
+
type ParameterValue,
|
|
13
|
+
} from '../plugins/manifest/index.ts'
|
|
14
|
+
import { routes } from './routes.ts'
|
|
15
|
+
|
|
16
|
+
/** The plugins this app's code provides. `blend.yaml` chooses, orders and configures them. */
|
|
17
|
+
export const registry = { 'acryl-router': router, 'acryl-db-sqlite': db, 'acryl-node-server': nodeServer, routes }
|
|
18
|
+
|
|
19
|
+
const projectRoot = fileURLToPath(new URL('..', import.meta.url))
|
|
20
|
+
|
|
21
|
+
/** The manifest next to this app's source. */
|
|
22
|
+
export const MANIFEST = join(projectRoot, 'blend.yaml')
|
|
23
|
+
|
|
24
|
+
/** The manifest, resolved. `createApp` and the tests read it the same way. */
|
|
25
|
+
export function readManifest(values?: Readonly<Record<string, ParameterValue>>) {
|
|
26
|
+
return loadManifest(MANIFEST, {
|
|
27
|
+
known: Object.keys(registry),
|
|
28
|
+
getBlueprint: blueprintsIn(join(projectRoot, 'blueprints')),
|
|
29
|
+
...(values ? { values } : {}),
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface App {
|
|
34
|
+
readonly root: Context
|
|
35
|
+
readonly url: string
|
|
36
|
+
stop(): Promise<void>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface AppOptions {
|
|
40
|
+
readonly values?: Readonly<Record<string, ParameterValue>>
|
|
41
|
+
/** The SQLite file, relative to the app folder; `:memory:` for a database that lives and dies with the process (tests). Default: ${'data/chat.sqlite'}. */
|
|
42
|
+
readonly database?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The app: `instance` first (where it lives, chosen at run time), then the manifest's rows in order. The `db` row opens the database and applies
|
|
47
|
+
* `migrations/`; the one place that names the SQLite provider is this file (`acrylblends check-deps`), so a different provider is a change here.
|
|
48
|
+
*/
|
|
49
|
+
export async function createApp(chosen: AppInstance, options: AppOptions = {}): Promise<App> {
|
|
50
|
+
const manifest = readManifest(options.values)
|
|
51
|
+
const file = options.database ?? 'data/chat.sqlite'
|
|
52
|
+
const root = new Context()
|
|
53
|
+
const first = root.plugin(instance, chosen)
|
|
54
|
+
await first.await()
|
|
55
|
+
const fibers: Fiber[] = [
|
|
56
|
+
first,
|
|
57
|
+
...(await compose(root, manifest, registry, {
|
|
58
|
+
db: {
|
|
59
|
+
path: file === ':memory:' || isAbsolute(file) ? file : resolve(projectRoot, file),
|
|
60
|
+
migrations: join(projectRoot, 'migrations'),
|
|
61
|
+
},
|
|
62
|
+
server: { port: chosen.port.start, scan: chosen.port.scan ? 20 : 0 },
|
|
63
|
+
})),
|
|
64
|
+
]
|
|
65
|
+
return {
|
|
66
|
+
root,
|
|
67
|
+
url: root.nodeServer.url,
|
|
68
|
+
async stop() {
|
|
69
|
+
for (const fiber of [...fibers].reverse()) await fiber.dispose()
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import type {} from '../plugins/acryl-db/src/cordis.ts'
|
|
3
|
+
import { createApp } from './chat-app.ts'
|
|
4
|
+
import { openAiCompatible } from './llm.ts'
|
|
5
|
+
|
|
6
|
+
/** The app's own plugin: it builds the app on the `db` port and mounts it on the router. The mount is removed when the plugin is disposed. */
|
|
7
|
+
export const routes = {
|
|
8
|
+
name: 'routes',
|
|
9
|
+
inject: ['router', 'db'],
|
|
10
|
+
apply(ctx: Context) {
|
|
11
|
+
ctx.router.route('/', createApp(ctx.db, openAiCompatible))
|
|
12
|
+
},
|
|
13
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { isAbsolute, join, resolve } from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { Context, type Fiber } from '@deepseek-ai/cordis'
|
|
4
|
+
import * as db from '../plugins/acryl-db-sqlite/src/index.ts'
|
|
5
|
+
import * as door from '../plugins/acryl-door/src/index.ts'
|
|
6
|
+
import * as nodeServer from '../plugins/acryl-node-server/src/index.ts'
|
|
7
|
+
import * as router from '../plugins/acryl-router/src/index.ts'
|
|
8
|
+
import { extensions } from '../plugins/extensions/index.ts'
|
|
9
|
+
import { type AppInstance, instance } from '../plugins/instance/index.ts'
|
|
10
|
+
import {
|
|
11
|
+
blueprintsIn,
|
|
12
|
+
compose,
|
|
13
|
+
loadManifest,
|
|
14
|
+
type ParameterValue,
|
|
15
|
+
} from '../plugins/manifest/index.ts'
|
|
16
|
+
import { doorEnabled } from './door.ts'
|
|
17
|
+
import { routes } from './routes.ts'
|
|
18
|
+
|
|
19
|
+
/** The plugins this app's code provides. `blend.yaml` chooses, orders and configures them. */
|
|
20
|
+
export const registry = {
|
|
21
|
+
'acryl-router': router,
|
|
22
|
+
'acryl-db-sqlite': db,
|
|
23
|
+
'acryl-node-server': nodeServer,
|
|
24
|
+
'acryl-door': door,
|
|
25
|
+
extensions,
|
|
26
|
+
routes,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const projectRoot = fileURLToPath(new URL('..', import.meta.url))
|
|
30
|
+
|
|
31
|
+
/** The manifest next to this app's source. */
|
|
32
|
+
export const MANIFEST = join(projectRoot, 'blend.yaml')
|
|
33
|
+
|
|
34
|
+
/** The manifest, resolved. `createApp` and the tests read it the same way. */
|
|
35
|
+
export function readManifest(values?: Readonly<Record<string, ParameterValue>>) {
|
|
36
|
+
return loadManifest(MANIFEST, {
|
|
37
|
+
known: Object.keys(registry),
|
|
38
|
+
getBlueprint: blueprintsIn(join(projectRoot, 'blueprints')),
|
|
39
|
+
...(values ? { values } : {}),
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The manifest without some rows: without its server (and so its door) the app is reached in process, through `root.router.request`; the door is also left out in production. */
|
|
44
|
+
function without<M extends { plugins: readonly { use: string }[] }>(
|
|
45
|
+
manifest: M,
|
|
46
|
+
...uses: string[]
|
|
47
|
+
): M {
|
|
48
|
+
return {
|
|
49
|
+
...manifest,
|
|
50
|
+
plugins: manifest.plugins.map((row) =>
|
|
51
|
+
uses.includes(row.use) ? { ...row, disabled: true } : row,
|
|
52
|
+
),
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface App {
|
|
57
|
+
readonly root: Context
|
|
58
|
+
/** The address the server listens on; empty when the app was made with `listen: false`. */
|
|
59
|
+
readonly url: string
|
|
60
|
+
stop(): Promise<void>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface AppOptions {
|
|
64
|
+
readonly values?: Readonly<Record<string, ParameterValue>>
|
|
65
|
+
/** The SQLite file, relative to the app folder; an absolute path is used as it is (tests give a file in a throwaway home). Default: `data/chat.sqlite`. */
|
|
66
|
+
readonly database?: string
|
|
67
|
+
/** Start the HTTP server (default). Tests pass `false` and call `app.root.router.request(path)`: it needs no port, so it also runs where a sandbox forbids listening. */
|
|
68
|
+
readonly listen?: boolean
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The app: `instance` first (where it lives, chosen at run time), then the manifest's rows in order. The `db` row opens the database and applies
|
|
73
|
+
* `migrations/`; the one place that names the SQLite provider is this file (`acrylblends check-deps`), so a different provider is a change here.
|
|
74
|
+
*/
|
|
75
|
+
export async function createApp(chosen: AppInstance, options: AppOptions = {}): Promise<App> {
|
|
76
|
+
const read = readManifest(options.values)
|
|
77
|
+
const listen = options.listen
|
|
78
|
+
// Rows left out: the server (and the door, which needs it) when the app is reached in process; the door when it is switched off; live plugin installs in production.
|
|
79
|
+
const leaveOut = new Set<string>()
|
|
80
|
+
if (listen === false) leaveOut.add('acryl-node-server').add('acryl-door')
|
|
81
|
+
if (!doorEnabled()) leaveOut.add('acryl-door')
|
|
82
|
+
if (process.env.NODE_ENV === 'production') leaveOut.add('extensions')
|
|
83
|
+
const manifest = without(read, ...leaveOut)
|
|
84
|
+
const file = options.database ?? 'data/chat.sqlite'
|
|
85
|
+
const root = new Context()
|
|
86
|
+
const first = root.plugin(instance, chosen)
|
|
87
|
+
await first.await()
|
|
88
|
+
const fibers: Fiber[] = [
|
|
89
|
+
first,
|
|
90
|
+
...(await compose(root, manifest, registry, {
|
|
91
|
+
db: {
|
|
92
|
+
path: isAbsolute(file) ? file : resolve(projectRoot, file),
|
|
93
|
+
migrations: join(projectRoot, 'migrations'),
|
|
94
|
+
},
|
|
95
|
+
// What is installed while the app runs lives in plugins/, next to the plugins copied from the library (plugins/library.json lists those, so they are not installed twice).
|
|
96
|
+
extensions: { sources: [{ dir: join(projectRoot, 'plugins'), trust: 'install' }] },
|
|
97
|
+
server: { port: chosen.port.start, scan: chosen.port.scan ? 20 : 0 },
|
|
98
|
+
})),
|
|
99
|
+
]
|
|
100
|
+
return {
|
|
101
|
+
root,
|
|
102
|
+
url: listen === false ? '' : root.nodeServer.url,
|
|
103
|
+
async stop() {
|
|
104
|
+
for (const fiber of [...fibers].reverse()) await fiber.dispose()
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import type {} from '../plugins/acryl-db/src/cordis.ts'
|
|
3
|
+
import { chatAccess } from './access.ts'
|
|
4
|
+
import { createApp } from './chat-app.ts'
|
|
5
|
+
import { openAiCompatible } from './llm.ts'
|
|
6
|
+
import { createClient } from './runtime.ts'
|
|
7
|
+
|
|
8
|
+
/** The app's own plugin: it builds the app on the `db` port and mounts it on the router. The mount is removed when the plugin is disposed. */
|
|
9
|
+
export const routes = {
|
|
10
|
+
name: 'routes',
|
|
11
|
+
inject: ['router', 'db'],
|
|
12
|
+
apply(ctx: Context) {
|
|
13
|
+
// In production the chat is closed unless it has a token or is made public on purpose: anyone who reaches it spends the model key.
|
|
14
|
+
const { access, notices } = chatAccess(process.env)
|
|
15
|
+
for (const line of notices) console.warn(line)
|
|
16
|
+
ctx.router.route('/', createApp(ctx.db, openAiCompatible, access))
|
|
17
|
+
ctx.router.route('/client', createClient())
|
|
18
|
+
},
|
|
19
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { isAbsolute, join, resolve } from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { Context, type Fiber } from '@deepseek-ai/cordis'
|
|
4
|
+
import * as db from '../plugins/acryl-db-sqlite/src/index.ts'
|
|
5
|
+
import * as door from '../plugins/acryl-door/src/index.ts'
|
|
6
|
+
import * as nodeServer from '../plugins/acryl-node-server/src/index.ts'
|
|
7
|
+
import * as router from '../plugins/acryl-router/src/index.ts'
|
|
8
|
+
import { extensions } from '../plugins/extensions/index.ts'
|
|
9
|
+
import { type AppInstance, instance } from '../plugins/instance/index.ts'
|
|
10
|
+
import {
|
|
11
|
+
blueprintsIn,
|
|
12
|
+
compose,
|
|
13
|
+
loadManifest,
|
|
14
|
+
type ParameterValue,
|
|
15
|
+
} from '../plugins/manifest/index.ts'
|
|
16
|
+
import { doorEnabled } from './door.ts'
|
|
17
|
+
import { routes } from './routes.ts'
|
|
18
|
+
|
|
19
|
+
/** The plugins this app's code provides. `blend.yaml` chooses, orders and configures them. */
|
|
20
|
+
export const registry = {
|
|
21
|
+
'acryl-router': router,
|
|
22
|
+
'acryl-db-sqlite': db,
|
|
23
|
+
'acryl-node-server': nodeServer,
|
|
24
|
+
'acryl-door': door,
|
|
25
|
+
extensions,
|
|
26
|
+
routes,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const projectRoot = fileURLToPath(new URL('..', import.meta.url))
|
|
30
|
+
|
|
31
|
+
/** The manifest next to this app's source. */
|
|
32
|
+
export const MANIFEST = join(projectRoot, 'blend.yaml')
|
|
33
|
+
|
|
34
|
+
/** The manifest, resolved. `createApp` and the tests read it the same way. */
|
|
35
|
+
export function readManifest(values?: Readonly<Record<string, ParameterValue>>) {
|
|
36
|
+
return loadManifest(MANIFEST, {
|
|
37
|
+
known: Object.keys(registry),
|
|
38
|
+
getBlueprint: blueprintsIn(join(projectRoot, 'blueprints')),
|
|
39
|
+
...(values ? { values } : {}),
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The manifest without some rows: without its server (and so its door) the app is reached in process, through `root.router.request`; the door is also left out in production. */
|
|
44
|
+
function without<M extends { plugins: readonly { use: string }[] }>(
|
|
45
|
+
manifest: M,
|
|
46
|
+
...uses: string[]
|
|
47
|
+
): M {
|
|
48
|
+
return {
|
|
49
|
+
...manifest,
|
|
50
|
+
plugins: manifest.plugins.map((row) =>
|
|
51
|
+
uses.includes(row.use) ? { ...row, disabled: true } : row,
|
|
52
|
+
),
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface App {
|
|
57
|
+
readonly root: Context
|
|
58
|
+
/** The address the server listens on; empty when the app was made with `listen: false`. */
|
|
59
|
+
readonly url: string
|
|
60
|
+
stop(): Promise<void>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface AppOptions {
|
|
64
|
+
readonly values?: Readonly<Record<string, ParameterValue>>
|
|
65
|
+
/** The SQLite file, relative to the app folder; an absolute path is used as it is (tests give a file in a throwaway home). Default: `data/chat.sqlite`. */
|
|
66
|
+
readonly database?: string
|
|
67
|
+
/** Start the HTTP server (default). Tests pass `false` and call `app.root.router.request(path)`: it needs no port, so it also runs where a sandbox forbids listening. */
|
|
68
|
+
readonly listen?: boolean
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The app: `instance` first (where it lives, chosen at run time), then the manifest's rows in order. The `db` row opens the database and applies
|
|
73
|
+
* `migrations/`; the one place that names the SQLite provider is this file (`acrylblends check-deps`), so a different provider is a change here.
|
|
74
|
+
*/
|
|
75
|
+
export async function createApp(chosen: AppInstance, options: AppOptions = {}): Promise<App> {
|
|
76
|
+
const read = readManifest(options.values)
|
|
77
|
+
const listen = options.listen
|
|
78
|
+
// Rows left out: the server (and the door, which needs it) when the app is reached in process; the door when it is switched off; live plugin installs in production.
|
|
79
|
+
const leaveOut = new Set<string>()
|
|
80
|
+
if (listen === false) leaveOut.add('acryl-node-server').add('acryl-door')
|
|
81
|
+
if (!doorEnabled()) leaveOut.add('acryl-door')
|
|
82
|
+
if (process.env.NODE_ENV === 'production') leaveOut.add('extensions')
|
|
83
|
+
const manifest = without(read, ...leaveOut)
|
|
84
|
+
const file = options.database ?? 'data/chat.sqlite'
|
|
85
|
+
const root = new Context()
|
|
86
|
+
const first = root.plugin(instance, chosen)
|
|
87
|
+
await first.await()
|
|
88
|
+
const fibers: Fiber[] = [
|
|
89
|
+
first,
|
|
90
|
+
...(await compose(root, manifest, registry, {
|
|
91
|
+
db: {
|
|
92
|
+
path: isAbsolute(file) ? file : resolve(projectRoot, file),
|
|
93
|
+
migrations: join(projectRoot, 'migrations'),
|
|
94
|
+
},
|
|
95
|
+
// What is installed while the app runs lives in plugins/, next to the plugins copied from the library (plugins/library.json lists those, so they are not installed twice).
|
|
96
|
+
extensions: { sources: [{ dir: join(projectRoot, 'plugins'), trust: 'install' }] },
|
|
97
|
+
server: { port: chosen.port.start, scan: chosen.port.scan ? 20 : 0 },
|
|
98
|
+
})),
|
|
99
|
+
]
|
|
100
|
+
return {
|
|
101
|
+
root,
|
|
102
|
+
url: listen === false ? '' : root.nodeServer.url,
|
|
103
|
+
async stop() {
|
|
104
|
+
for (const fiber of [...fibers].reverse()) await fiber.dispose()
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import type {} from '../plugins/acryl-db/src/cordis.ts'
|
|
3
|
+
import { createApp } from './chat-app.ts'
|
|
4
|
+
import { openAiCompatible } from './llm.ts'
|
|
5
|
+
import { createClient } from './runtime.ts'
|
|
6
|
+
|
|
7
|
+
/** The app's own plugin: it builds the app on the `db` port and mounts it on the router. The mount is removed when the plugin is disposed. */
|
|
8
|
+
export const routes = {
|
|
9
|
+
name: 'routes',
|
|
10
|
+
inject: ['router', 'db'],
|
|
11
|
+
apply(ctx: Context) {
|
|
12
|
+
ctx.router.route('/', createApp(ctx.db, openAiCompatible))
|
|
13
|
+
ctx.router.route('/client', createClient())
|
|
14
|
+
},
|
|
15
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { isAbsolute, join, resolve } from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { Context, type Fiber } from '@deepseek-ai/cordis'
|
|
4
|
+
import * as db from '../plugins/acryl-db-sqlite/src/index.ts'
|
|
5
|
+
import * as door from '../plugins/acryl-door/src/index.ts'
|
|
6
|
+
import * as nodeServer from '../plugins/acryl-node-server/src/index.ts'
|
|
7
|
+
import * as router from '../plugins/acryl-router/src/index.ts'
|
|
8
|
+
import { type AppInstance, instance } from '../plugins/instance/index.ts'
|
|
9
|
+
import {
|
|
10
|
+
blueprintsIn,
|
|
11
|
+
compose,
|
|
12
|
+
loadManifest,
|
|
13
|
+
type ParameterValue,
|
|
14
|
+
} from '../plugins/manifest/index.ts'
|
|
15
|
+
import { doorEnabled } from './door.ts'
|
|
16
|
+
import { routes } from './routes.ts'
|
|
17
|
+
|
|
18
|
+
/** The plugins this app's code provides. `blend.yaml` chooses, orders and configures them. */
|
|
19
|
+
export const registry = {
|
|
20
|
+
'acryl-router': router,
|
|
21
|
+
'acryl-db-sqlite': db,
|
|
22
|
+
'acryl-node-server': nodeServer,
|
|
23
|
+
'acryl-door': door,
|
|
24
|
+
routes,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const projectRoot = fileURLToPath(new URL('..', import.meta.url))
|
|
28
|
+
|
|
29
|
+
/** The manifest next to this app's source. */
|
|
30
|
+
export const MANIFEST = join(projectRoot, 'blend.yaml')
|
|
31
|
+
|
|
32
|
+
/** The manifest, resolved. `createApp` and the tests read it the same way. */
|
|
33
|
+
export function readManifest(values?: Readonly<Record<string, ParameterValue>>) {
|
|
34
|
+
return loadManifest(MANIFEST, {
|
|
35
|
+
known: Object.keys(registry),
|
|
36
|
+
getBlueprint: blueprintsIn(join(projectRoot, 'blueprints')),
|
|
37
|
+
...(values ? { values } : {}),
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The manifest without some rows: without its server (and so its door) the app is reached in process, through `root.router.request`; the door is also left out in production. */
|
|
42
|
+
function without<M extends { plugins: readonly { use: string }[] }>(
|
|
43
|
+
manifest: M,
|
|
44
|
+
...uses: string[]
|
|
45
|
+
): M {
|
|
46
|
+
return {
|
|
47
|
+
...manifest,
|
|
48
|
+
plugins: manifest.plugins.map((row) =>
|
|
49
|
+
uses.includes(row.use) ? { ...row, disabled: true } : row,
|
|
50
|
+
),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface App {
|
|
55
|
+
readonly root: Context
|
|
56
|
+
/** The address the server listens on; empty when the app was made with `listen: false`. */
|
|
57
|
+
readonly url: string
|
|
58
|
+
stop(): Promise<void>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface AppOptions {
|
|
62
|
+
readonly values?: Readonly<Record<string, ParameterValue>>
|
|
63
|
+
/** The SQLite file, relative to the app folder; an absolute path is used as it is (tests give a file in a throwaway home). Default: `data/chat.sqlite`. */
|
|
64
|
+
readonly database?: string
|
|
65
|
+
/** Start the HTTP server (default). Tests pass `false` and call `app.root.router.request(path)`: it needs no port, so it also runs where a sandbox forbids listening. */
|
|
66
|
+
readonly listen?: boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The app: `instance` first (where it lives, chosen at run time), then the manifest's rows in order. The `db` row opens the database and applies
|
|
71
|
+
* `migrations/`; the one place that names the SQLite provider is this file (`acrylblends check-deps`), so a different provider is a change here.
|
|
72
|
+
*/
|
|
73
|
+
export async function createApp(chosen: AppInstance, options: AppOptions = {}): Promise<App> {
|
|
74
|
+
const manifest = readManifest(options.values)
|
|
75
|
+
const listen = options.listen
|
|
76
|
+
const file = options.database ?? 'data/chat.sqlite'
|
|
77
|
+
const root = new Context()
|
|
78
|
+
const first = root.plugin(instance, chosen)
|
|
79
|
+
await first.await()
|
|
80
|
+
const fibers: Fiber[] = [
|
|
81
|
+
first,
|
|
82
|
+
...(await compose(
|
|
83
|
+
root,
|
|
84
|
+
listen === false
|
|
85
|
+
? without(manifest, 'acryl-node-server', 'acryl-door')
|
|
86
|
+
: doorEnabled()
|
|
87
|
+
? manifest
|
|
88
|
+
: without(manifest, 'acryl-door'),
|
|
89
|
+
registry,
|
|
90
|
+
{
|
|
91
|
+
db: {
|
|
92
|
+
path: isAbsolute(file) ? file : resolve(projectRoot, file),
|
|
93
|
+
migrations: join(projectRoot, 'migrations'),
|
|
94
|
+
},
|
|
95
|
+
server: { port: chosen.port.start, scan: chosen.port.scan ? 20 : 0 },
|
|
96
|
+
},
|
|
97
|
+
)),
|
|
98
|
+
]
|
|
99
|
+
return {
|
|
100
|
+
root,
|
|
101
|
+
url: listen === false ? '' : root.nodeServer.url,
|
|
102
|
+
async stop() {
|
|
103
|
+
for (const fiber of [...fibers].reverse()) await fiber.dispose()
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
2
|
+
import type {} from '../plugins/acryl-db/src/cordis.ts'
|
|
3
|
+
import { createApp } from './chat-app.ts'
|
|
4
|
+
import { openAiCompatible } from './llm.ts'
|
|
5
|
+
import { createClient } from './runtime.ts'
|
|
6
|
+
|
|
7
|
+
/** The app's own plugin: it builds the app on the `db` port and mounts it on the router. The mount is removed when the plugin is disposed. */
|
|
8
|
+
export const routes = {
|
|
9
|
+
name: 'routes',
|
|
10
|
+
inject: ['router', 'db'],
|
|
11
|
+
apply(ctx: Context) {
|
|
12
|
+
ctx.router.route('/', createApp(ctx.db, openAiCompatible))
|
|
13
|
+
ctx.router.route('/client', createClient())
|
|
14
|
+
},
|
|
15
|
+
}
|