@abraca/orchestrator 2.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 +237 -0
- package/dist/abracadabra-orchestrator.cjs +2709 -0
- package/dist/abracadabra-orchestrator.cjs.map +1 -0
- package/dist/abracadabra-orchestrator.esm.js +2665 -0
- package/dist/abracadabra-orchestrator.esm.js.map +1 -0
- package/dist/index.d.ts +261 -0
- package/package.json +39 -0
- package/src/actions/awareness.ts +101 -0
- package/src/actions/chat.ts +30 -0
- package/src/actions/connect.ts +16 -0
- package/src/actions/content.ts +46 -0
- package/src/actions/cursor.ts +30 -0
- package/src/actions/document.ts +120 -0
- package/src/actions/flow.ts +46 -0
- package/src/actions/index.ts +197 -0
- package/src/actions/navigate.ts +15 -0
- package/src/actions/type.ts +113 -0
- package/src/actor-connection.ts +246 -0
- package/src/converters/markdownToYjs.ts +932 -0
- package/src/converters/types.ts +179 -0
- package/src/crypto.ts +71 -0
- package/src/define.ts +147 -0
- package/src/easing.ts +15 -0
- package/src/index.ts +105 -0
- package/src/orchestrator.ts +153 -0
- package/src/timeline-runner.ts +84 -0
- package/src/types.ts +245 -0
- package/src/utils.ts +50 -0
- package/src/yjs-utils.ts +238 -0
package/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# @abraca/orchestrator
|
|
2
|
+
|
|
3
|
+
Orchestrate simulated actors on an Abracadabra CRDT server for screen recording. Define a cast of actors, script their actions on a timeline, and run the scene — the orchestrator connects to the server and performs everything in real time while you record the dashboard.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @abraca/orchestrator
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `@abraca/dabra`, `yjs`, `y-protocols`
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { defineScene, actor, actions } from '@abraca/orchestrator'
|
|
17
|
+
|
|
18
|
+
export default defineScene({
|
|
19
|
+
server: { url: 'ws://localhost:8080' },
|
|
20
|
+
actors: [
|
|
21
|
+
actor('Alice', { color: '#F97066' }),
|
|
22
|
+
actor('Bob', { color: '#4A90D9' }),
|
|
23
|
+
],
|
|
24
|
+
timeline: [
|
|
25
|
+
{ at: 0, actor: 'Alice', action: actions.connect() },
|
|
26
|
+
{ at: 0, actor: 'Bob', action: actions.connect() },
|
|
27
|
+
{ at: 1000, actor: 'Alice', action: actions.navigate('my-doc') },
|
|
28
|
+
{ at: 2000, actor: 'Alice', action: actions.type('my-doc', 'Hello from Alice!') },
|
|
29
|
+
{ at: 3000, actor: 'Bob', action: actions.navigate('my-doc') },
|
|
30
|
+
{ at: 4000, actor: 'Bob', action: actions.type('my-doc', '\nHello from Bob!') },
|
|
31
|
+
{ at: 8000, actor: 'Alice', action: actions.disconnect() },
|
|
32
|
+
{ at: 8000, actor: 'Bob', action: actions.disconnect() },
|
|
33
|
+
],
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## CLI Usage
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Build packages first
|
|
41
|
+
pnpm build:packages
|
|
42
|
+
|
|
43
|
+
# Run a scene script (from repo root)
|
|
44
|
+
node --experimental-transform-types \
|
|
45
|
+
packages/orchestrator/dist/abracadabra-orchestrator.esm.js ./my-scene.ts
|
|
46
|
+
|
|
47
|
+
# Validate without connecting
|
|
48
|
+
node --experimental-transform-types \
|
|
49
|
+
packages/orchestrator/dist/abracadabra-orchestrator.esm.js --dry-run ./my-scene.ts
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Scene scripts are TypeScript files loaded via `--experimental-transform-types`. The `--dry-run` flag validates the scene structure (actor names, action types, required fields) and prints the timeline without connecting to the server.
|
|
53
|
+
|
|
54
|
+
> **Note:** Do not use `--conditions=source` — Node's type stripping does not support files under `node_modules`, which would break the `@abraca/dabra` peer dependency resolution.
|
|
55
|
+
|
|
56
|
+
## Scene Definition
|
|
57
|
+
|
|
58
|
+
A scene is defined with `defineScene()` and must export as the default export.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
defineScene({
|
|
62
|
+
server: { url: 'ws://localhost:8080', inviteCode: 'optional-invite' },
|
|
63
|
+
actors: [ ... ],
|
|
64
|
+
timeline: [ ... ],
|
|
65
|
+
vars: { hubDocId: 'abc-123' }, // pre-defined variables
|
|
66
|
+
duration: 30000, // auto-stop after 30s
|
|
67
|
+
onStart: async () => { ... }, // runs before timeline
|
|
68
|
+
onEnd: async () => { ... }, // runs after timeline
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Actors
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
actor('Name', {
|
|
76
|
+
color: '#hex', // cursor / avatar color
|
|
77
|
+
keyFile: './keys/name', // optional Ed25519 key path (auto-generated if missing)
|
|
78
|
+
avatar: 'https://...', // optional avatar URL
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Actors authenticate via Ed25519 challenge-response. If no `keyFile` is provided, a deterministic keypair is derived from the actor name.
|
|
83
|
+
|
|
84
|
+
## Actions Reference
|
|
85
|
+
|
|
86
|
+
All actions are created via the `actions` factory object.
|
|
87
|
+
|
|
88
|
+
### Connection
|
|
89
|
+
|
|
90
|
+
| Action | Factory | Description |
|
|
91
|
+
|--------|---------|-------------|
|
|
92
|
+
| `connect` | `actions.connect()` | Connect actor to server, authenticate, sync root doc |
|
|
93
|
+
| `disconnect` | `actions.disconnect()` | Clear awareness and disconnect gracefully |
|
|
94
|
+
|
|
95
|
+
### Navigation
|
|
96
|
+
|
|
97
|
+
| Action | Factory | Description |
|
|
98
|
+
|--------|---------|-------------|
|
|
99
|
+
| `navigate` | `actions.navigate(docId)` | Set actor's active document (awareness `docId` field) |
|
|
100
|
+
|
|
101
|
+
### Text Editing
|
|
102
|
+
|
|
103
|
+
| Action | Factory | Description |
|
|
104
|
+
|--------|---------|-------------|
|
|
105
|
+
| `type` | `actions.type(docId, text, opts?)` | Type text character-by-character with realistic timing |
|
|
106
|
+
| `typeDelete` | `actions.typeDelete(docId, count, opts?)` | Delete characters one at a time (backspace) |
|
|
107
|
+
| `select` | `actions.select(docId, anchor, head)` | Set a text selection range |
|
|
108
|
+
| `moveCursor` | `actions.moveCursor(docId, from, to, duration, easing?)` | Animate cursor movement with easing |
|
|
109
|
+
|
|
110
|
+
Options for `type`: `{ speed?: number, variance?: number, position?: number }`
|
|
111
|
+
Options for `typeDelete`: `{ speed?: number, variance?: number, position?: number }`
|
|
112
|
+
Easing: `'linear'` | `'easeIn'` | `'easeOut'` | `'easeInOut'`
|
|
113
|
+
|
|
114
|
+
### Document Operations
|
|
115
|
+
|
|
116
|
+
| Action | Factory | Description |
|
|
117
|
+
|--------|---------|-------------|
|
|
118
|
+
| `createDocument` | `actions.createDocument(parentId, label, opts?)` | Create a new doc in the tree |
|
|
119
|
+
| `moveDocument` | `actions.moveDocument(docId, newParentId, order?)` | Move a doc to a new parent |
|
|
120
|
+
| `renameDocument` | `actions.renameDocument(docId, label)` | Rename a document |
|
|
121
|
+
| `writeContent` | `actions.writeContent(docId, markdown)` | Set doc content from markdown (bulk) |
|
|
122
|
+
| `deleteContent` | `actions.deleteContent(docId, from, length)` | Delete a range of content elements |
|
|
123
|
+
| `setMeta` | `actions.setMeta(docId, meta)` | Merge metadata fields (icon, color, etc.) |
|
|
124
|
+
|
|
125
|
+
Options for `createDocument`: `{ docType?: string, meta?: Record<string, unknown>, assignId?: string }`
|
|
126
|
+
|
|
127
|
+
The `assignId` option stores the newly created document's ID in the scene `vars` map, so later actions can reference it with `${varName}`.
|
|
128
|
+
|
|
129
|
+
### Awareness & UI
|
|
130
|
+
|
|
131
|
+
| Action | Factory | Description |
|
|
132
|
+
|--------|---------|-------------|
|
|
133
|
+
| `setStatus` | `actions.setStatus(status)` | Set actor's status text (or `null` to clear) |
|
|
134
|
+
| `setAwareness` | `actions.setAwareness(fields, docId?)` | Set arbitrary awareness fields |
|
|
135
|
+
| `clearAwareness` | `actions.clearAwareness(fields, docId?)` | Remove awareness fields |
|
|
136
|
+
| `pointerMove` | `actions.pointerMove(docId, from, to, duration, easing?)` | Animate pointer movement |
|
|
137
|
+
| `scrollTo` | `actions.scrollTo(docId, position)` | Set scroll position (0–1) |
|
|
138
|
+
|
|
139
|
+
### Kanban
|
|
140
|
+
|
|
141
|
+
| Action | Factory | Description |
|
|
142
|
+
|--------|---------|-------------|
|
|
143
|
+
| `kanbanHover` | `actions.kanbanHover(docId, cardId)` | Hover a kanban card (or `null` to clear) |
|
|
144
|
+
| `kanbanDrag` | `actions.kanbanDrag(docId, cardId, toColumnId, duration)` | Animate dragging a card to a column |
|
|
145
|
+
|
|
146
|
+
### Chat
|
|
147
|
+
|
|
148
|
+
| Action | Factory | Description |
|
|
149
|
+
|--------|---------|-------------|
|
|
150
|
+
| `sendChat` | `actions.sendChat(channel, message)` | Send a chat message via stateless protocol |
|
|
151
|
+
|
|
152
|
+
### Flow Control
|
|
153
|
+
|
|
154
|
+
| Action | Factory | Description |
|
|
155
|
+
|--------|---------|-------------|
|
|
156
|
+
| `wait` | `actions.wait(duration)` | Pause for N milliseconds |
|
|
157
|
+
| `parallel` | `actions.parallel(entries)` | Run timeline entries concurrently |
|
|
158
|
+
| `sequence` | `actions.sequence(entries)` | Run timeline entries one after another |
|
|
159
|
+
| `repeat` | `actions.repeat(times, entries)` | Loop entries N times |
|
|
160
|
+
|
|
161
|
+
## Timeline
|
|
162
|
+
|
|
163
|
+
Timeline entries are scheduled by their `at` field (milliseconds from scene start). Entries with the same `at` value run in parallel. Within `parallel` and `sequence` blocks, `at` is relative to the block's start.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
timeline: [
|
|
167
|
+
// These run at the same time (both at 0ms)
|
|
168
|
+
{ at: 0, actor: 'Alice', action: actions.connect() },
|
|
169
|
+
{ at: 0, actor: 'Bob', action: actions.connect() },
|
|
170
|
+
|
|
171
|
+
// Nested parallel block with relative offsets
|
|
172
|
+
{
|
|
173
|
+
at: 2000,
|
|
174
|
+
action: actions.parallel([
|
|
175
|
+
{ actor: 'Alice', action: actions.type('doc', 'Hello') },
|
|
176
|
+
{ at: 500, actor: 'Bob', action: actions.type('doc', 'World') },
|
|
177
|
+
]),
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
// Sequence: each runs after the previous completes
|
|
181
|
+
{
|
|
182
|
+
at: 5000,
|
|
183
|
+
action: actions.sequence([
|
|
184
|
+
{ actor: 'Alice', action: actions.type('doc', 'Line 1\n') },
|
|
185
|
+
{ actor: 'Alice', action: actions.type('doc', 'Line 2\n') },
|
|
186
|
+
]),
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
// Repeat: loop 3 times
|
|
190
|
+
{
|
|
191
|
+
at: 10000,
|
|
192
|
+
action: actions.repeat(3, [
|
|
193
|
+
{ actor: 'Bob', action: actions.type('doc', '.') },
|
|
194
|
+
{ action: actions.wait(1000) },
|
|
195
|
+
]),
|
|
196
|
+
},
|
|
197
|
+
]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Variables
|
|
201
|
+
|
|
202
|
+
Define variables in `vars` and reference them with `${name}` in string fields (docId, parentId, text, label, markdown, etc.).
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
defineScene({
|
|
206
|
+
vars: { hubDoc: 'abc-123' },
|
|
207
|
+
// ...
|
|
208
|
+
timeline: [
|
|
209
|
+
// Create a doc and store its ID as "newDoc"
|
|
210
|
+
{
|
|
211
|
+
at: 1000,
|
|
212
|
+
actor: 'Alice',
|
|
213
|
+
action: actions.createDocument('${hubDoc}', 'My Page', { assignId: 'newDoc' }),
|
|
214
|
+
},
|
|
215
|
+
// Reference the created doc later
|
|
216
|
+
{
|
|
217
|
+
at: 3000,
|
|
218
|
+
actor: 'Alice',
|
|
219
|
+
action: actions.type('${newDoc}', 'Content goes here'),
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
})
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Programmatic API
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
import { Orchestrator } from '@abraca/orchestrator'
|
|
229
|
+
|
|
230
|
+
const orchestrator = new Orchestrator()
|
|
231
|
+
|
|
232
|
+
await orchestrator.load('./my-scene.ts')
|
|
233
|
+
orchestrator.prepare()
|
|
234
|
+
orchestrator.dryRun() // optional: validate + print timeline
|
|
235
|
+
await orchestrator.run()
|
|
236
|
+
await orchestrator.cleanup()
|
|
237
|
+
```
|