acrylblends 1.2.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/dist/index.d.ts +2 -1
- package/dist/index.js +39 -7
- package/dist/index.js.map +1 -1
- package/docs/framework/guides/create-acrylblends.md +4 -2
- package/docs/framework/workshop/README.md +2 -2
- package/docs/framework/workshop/facilitator.md +1 -1
- package/lib/cordis/door.d.ts +45 -2
- package/lib/cordis/door.js +120 -0
- package/package.json +1 -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/door.ts +75 -1
- package/plugins/acryl-door/src/host/operator-routes.ts +21 -1
- package/plugins/acryl-door/src/host/verbs.ts +11 -1
- package/plugins/acryl-door/src/index.ts +2 -1
- package/plugins/builder/index.ts +13 -35
- package/plugins/builder/package.ts +186 -0
- package/plugins/builder/plugin.json +3 -1
- package/plugins/extensions/index.ts +21 -17
- package/plugins/extensions/plugin-store.ts +27 -0
- package/plugins/extensions/plugin.json +1 -1
- package/plugins/extensions/stage.ts +15 -5
- package/plugins/manifest/plugin.json +1 -1
- 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/templates/agent/.gitattributes +2 -0
- package/templates/agent/blend.yaml +2 -0
- package/templates/agent/src/app.ts +4 -0
- package/templates/agent/template.json +1 -0
- package/templates/blank/.gitattributes +2 -0
- package/templates/chat/.gitattributes +2 -0
- package/templates/nodejs/.gitattributes +2 -0
- package/templates/notes/.gitattributes +2 -0
- package/templates/todo/.gitattributes +2 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an app remembers about the plugins it pulled: their source and digests, in the `plugin-share` row's config of `blend.yaml`. A row's config is
|
|
3
|
+
* resolved into `blend.lock.json` too, so the lock records them with no new format, and a copy of the app can fetch exactly the same bytes. Pure text
|
|
4
|
+
* to text; comments and the person's formatting stay.
|
|
5
|
+
*/
|
|
6
|
+
import { isMap, isSeq, parseDocument } from 'yaml'
|
|
7
|
+
|
|
8
|
+
export const ROW = 'plugin-share'
|
|
9
|
+
|
|
10
|
+
export interface PulledRecord {
|
|
11
|
+
/** `github:<owner>/<repo>@<version>` or an https address: something another machine can fetch. */
|
|
12
|
+
readonly source: string
|
|
13
|
+
/** The sha512 of the release file. */
|
|
14
|
+
readonly integrity: string
|
|
15
|
+
/** The sha256 of the plugin's content. */
|
|
16
|
+
readonly digest: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const isRecord = (value: unknown): value is PulledRecord => {
|
|
20
|
+
const record = value as Partial<Record<keyof PulledRecord, unknown>> | null
|
|
21
|
+
return (
|
|
22
|
+
typeof record?.source === 'string' &&
|
|
23
|
+
typeof record.integrity === 'string' &&
|
|
24
|
+
typeof record.digest === 'string'
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The plugins this definition remembers pulling. */
|
|
29
|
+
export function pulledRecords(text: string): Readonly<Record<string, PulledRecord>> {
|
|
30
|
+
const rows = (parseDocument(text).toJS() as { spec?: { rows?: unknown } } | null)?.spec?.rows
|
|
31
|
+
if (!Array.isArray(rows)) return {}
|
|
32
|
+
const pulled = (
|
|
33
|
+
rows.find((row: { name?: unknown }) => row?.name === ROW) as
|
|
34
|
+
| { config?: { pulled?: unknown } }
|
|
35
|
+
| undefined
|
|
36
|
+
)?.config?.pulled
|
|
37
|
+
if (typeof pulled !== 'object' || pulled === null) return {}
|
|
38
|
+
return Object.fromEntries(
|
|
39
|
+
Object.entries(pulled).filter(([, record]) => isRecord(record)),
|
|
40
|
+
) as Record<string, PulledRecord>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The definition with one plugin's record written, or taken out when `record` is undefined. Unchanged when it has no `plugin-share` row: an app
|
|
45
|
+
* composed without the row has nowhere to remember.
|
|
46
|
+
*/
|
|
47
|
+
export function withPulledRecord(
|
|
48
|
+
text: string,
|
|
49
|
+
name: string,
|
|
50
|
+
record: PulledRecord | undefined,
|
|
51
|
+
): string {
|
|
52
|
+
const document = parseDocument(text)
|
|
53
|
+
const rows = document.getIn(['spec', 'rows'])
|
|
54
|
+
if (!isSeq(rows)) return text
|
|
55
|
+
const row = rows.items.find((item) => isMap(item) && item.get('name') === ROW)
|
|
56
|
+
if (!isMap(row)) return text
|
|
57
|
+
if (record === undefined) {
|
|
58
|
+
if (!row.hasIn(['config', 'pulled', name])) return text
|
|
59
|
+
row.deleteIn(['config', 'pulled', name])
|
|
60
|
+
const left = row.getIn(['config', 'pulled'])
|
|
61
|
+
if (isMap(left) && left.items.length === 0) row.deleteIn(['config', 'pulled'])
|
|
62
|
+
const config = row.get('config')
|
|
63
|
+
if (isMap(config) && config.items.length === 0) row.delete('config')
|
|
64
|
+
} else {
|
|
65
|
+
row.setIn(['config'], row.get('config') ?? document.createNode({}))
|
|
66
|
+
if (!row.hasIn(['config', 'pulled'])) row.setIn(['config', 'pulled'], document.createNode({}))
|
|
67
|
+
row.setIn(['config', 'pulled', name], document.createNode(record))
|
|
68
|
+
}
|
|
69
|
+
return String(document)
|
|
70
|
+
}
|
|
@@ -49,6 +49,8 @@ spec:
|
|
|
49
49
|
approval: "{{parameters.approval}}"
|
|
50
50
|
- id: builder
|
|
51
51
|
name: builder
|
|
52
|
+
- id: plugin-share
|
|
53
|
+
name: plugin-share
|
|
52
54
|
- id: save
|
|
53
55
|
name: save
|
|
54
56
|
# The panel: a chat button on every page in development (the app's own agent, or Codex, Claude or pi started in this folder). It starts nothing in production.
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type ParameterValue,
|
|
21
21
|
} from '../plugins/manifest/index.ts'
|
|
22
22
|
import { ASSISTANT_PROMPT, type OpenAIConfig, openai } from '../plugins/openai/index.ts'
|
|
23
|
+
import { pluginShare } from '../plugins/plugin-share/index.ts'
|
|
23
24
|
import { sandboxPlugin } from '../plugins/sandbox/index.ts'
|
|
24
25
|
import { save } from '../plugins/save/index.ts'
|
|
25
26
|
import { tasks } from '../plugins/tasks/index.ts'
|
|
@@ -36,6 +37,7 @@ export const registry = {
|
|
|
36
37
|
'acryl-state': state,
|
|
37
38
|
extensions,
|
|
38
39
|
builder,
|
|
40
|
+
'plugin-share': pluginShare,
|
|
39
41
|
save,
|
|
40
42
|
sandbox: sandboxPlugin,
|
|
41
43
|
routes,
|
|
@@ -77,6 +79,7 @@ export interface App {
|
|
|
77
79
|
const DEVELOPMENT_ROWS: Record<string, string> = {
|
|
78
80
|
agent: 'POST /ask runs the agent, which spends the model key and calls its tools',
|
|
79
81
|
builder: 'the agent can write plugin source into plugins/',
|
|
82
|
+
'plugin-share': 'a plugin pulled from a release runs in this process',
|
|
80
83
|
extensions:
|
|
81
84
|
'a plugin installed live runs in this process, and POST /extensions/approve decides installs',
|
|
82
85
|
}
|
|
@@ -215,6 +218,7 @@ export async function composeInto(
|
|
|
215
218
|
rows: ownRows,
|
|
216
219
|
},
|
|
217
220
|
builder: { dir: extensionsDir },
|
|
221
|
+
'plugin-share': { dir: extensionsDir },
|
|
218
222
|
...(assistant
|
|
219
223
|
? {
|
|
220
224
|
// Every answer comes right after a look, so it says what the visitor's page shows now.
|