@vizualmodel/vmblu-cli 0.3.5 → 0.4.1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vizualmodel/vmblu-cli",
3
- "version": "0.3.5",
4
- "schemaVersion": "0.8.4",
3
+ "version": "0.4.1",
4
+ "schemaVersion": "0.9.1",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "vmblu": "bin/vmblu.js"
@@ -37,7 +37,8 @@
37
37
  },
38
38
  "scripts": {
39
39
  "vmblu": "vmblu",
40
- "local": "node ./bin/vmblu.js profile",
40
+ "local-profile": "node ./bin/vmblu.js profile",
41
+ "local-init": "node ./bin/vmblu.js init",
41
42
  "build": "rollup -c ./commands/profile/rollup.config.js"
42
43
  }
43
44
  }
@@ -0,0 +1,268 @@
1
+ # Annex A: Semantic Clarifications for blueprint.schema.json
2
+
3
+ This annex captures subtle semantic points that are not fully enforceable in the JSON Schema but are critical for correct usage of the vmblu format by both humans and LLMs.
4
+
5
+ ## A.1 Nodes
6
+
7
+ A source node represents an indivisible implementation unit, whereas a group node is a purely architectural composition of nodes.
8
+ A source node can represent a UI element, the access to a database, a login procedure, a 3D scene list etc.
9
+ The name of a node should be meaningful and unique inside a group node.
10
+ The prompt for a node should be a clear, concise and up-to-date description of its function.
11
+
12
+ ## A.2 Interface names and Pin names
13
+
14
+ - Pins are grouped in **interfaces**
15
+ - There can only be one anonymous interface (name is ""), this is acceptable for nodes that only have a few pins.
16
+ - Group pins together into meaningful interfaces
17
+ - Use the following convention when giving a name to a pin: if the pin belongs to an interface start the name with the name of that interface followed by a period. If the rest of the name is more than one word, separate the words by a hyphen. Examples: _file.save_, _file.save-as_, _file.convert-to-uppercase_, where the interface name is _file_.
18
+
19
+ ## A.3 Pin contracts and types
20
+
21
+ ### (1) Types and `vmbluType`
22
+
23
+ * `vmbluType` always refers to either:
24
+
25
+ * a primitive (`string`, `number`, `boolean`, `any`, …), or
26
+ * a named type in the model’s `types` section.
27
+ * Structural payload details belong in the `types` section, not on pins.
28
+
29
+ ### (2) Pin contract roles
30
+
31
+ * Every pin may declare a `contract` with a `role`:
32
+
33
+ * `role: "owner"` means: **this pin defines the payload type** and is authoritative.
34
+ * `role: "follower"` means: **this pin adapts** to the connected owner’s contract.
35
+ * If `role` is `"owner"`, the pin contract must include the payload type
36
+ * If `role` is `"follower"`, the pin must *not* include the payload type
37
+
38
+ A follower pin has no payload type by itself; it becomes meaningful only when connected. When a follower pin is disconnected, it stays a follower and has no stored payload type.
39
+
40
+ For input/output pins, the payload type is a single type that refers to a type in the `types`section. For request/reply pins the payload type consists of two types: the request payload and the reply payload. Both types also refer to a type in the `types` section.
41
+
42
+ A contract can thus have three different forms:
43
+
44
+ * for pins that follow it is simply
45
+ ```json
46
+ "contract" : {
47
+ "role": "follower"
48
+ }
49
+ ```
50
+ * for input or output pins that own the contract it is
51
+ ```json
52
+ "contract" : {
53
+ "role": "owner",
54
+ "payload" : "vmbluType"
55
+ }
56
+ ```
57
+ * for request or reply pins that own the contract it is
58
+ ```json
59
+ "contract" : {
60
+ "role": "owner",
61
+ "payload" : {
62
+ "request" : "vmbluType",
63
+ "reply": "vmbluType"
64
+ }
65
+ }
66
+ ```
67
+
68
+ Keep the blueprint lean and deterministic. Avoid duplicating payload shapes in multiple places; define once in `types` and reference in the contract payload.
69
+
70
+ ## A.4 Connections
71
+
72
+ Connections are always made between pins. Interfaces are organizational only and are never connection endpoints.
73
+ Connections between pins are specified in the `connections` array. Message flow is from `src` to `dst`. `src` and `dst` are specified by the name of the pin and the name of the node. If the name of the node is omitted it is supposed to be a pin of the group node that contains the connection. This is the only way to receive messages from or send messages to the outside of the group node.
74
+
75
+ This means that the following connections are valid
76
+
77
+ * src: output pin @ node, dst : input pin @ node
78
+ simple connection
79
+ * src: request pin @ node, dst reply pin @ node
80
+ request/reply
81
+ * src: request pin @ node, dst: input pin @ node
82
+ input pin can listen to requests, but not reply
83
+ * src: output pin @ node, dst: output pin
84
+ An output pin of a node is connected to an output pin of the containing group node
85
+ * src: input pin, dst: input pin @ node
86
+ An input pin of the containing group node is connected to an input of a node inside the group node.
87
+
88
+ For a connection between two pins also the contracts of the pins have to be checked.
89
+
90
+ * **owner ↔ follower**: valid. The follower must conform to the owner’s `payload`.
91
+ * **owner ↔ owner**: valid only if both owners’ `payload` values match. If they do not match, the connection is invalid unless one side is changed to `follower`.
92
+ * **follower ↔ follower**: invalid. To make it valid, upgrade one side to `owner` and define its `vmbluType`.
93
+
94
+ Implementation obligations:
95
+
96
+ * When connecting a follower to an owner, adapt the follower implementation (or add an adapter node) so its payload handling conforms to the owner’s `vmbluType`.
97
+ * Changing an owner’s `vmbluType` is a breaking change for all connected followers; update them accordingly.
98
+ * Changing a pin’s role (`owner` ↔ `follower`) is an architectural decision. Do it deliberately and update connections/implementations accordingly.
99
+ * If a connection is invalid under the rules above, either:
100
+
101
+ * adjust roles, or
102
+ * introduce an explicit adapter node, or
103
+ * change payload types (as a conscious breaking change).
104
+
105
+ ## A.5 Pin Message Handlers
106
+
107
+ Every **input** or **reply** pin corresponds to a message handler in the node implementation.
108
+
109
+ The naming convention for a handler is as follows:
110
+
111
+ - Handler name is `on<PinNameInCamelCase>`.
112
+ - Example:
113
+ - Pin: `"name": "saveMessage", "kind": "input"`
114
+ - Handler: `onSaveMessage(payload)`
115
+
116
+ This uniform convention ensures LLMs and the editor can always map pins to their corresponding handler.
117
+
118
+ Do not return a value from a handler, it is ignored.
119
+
120
+ ## A.6 Pin prompts
121
+
122
+ A **pin prompt** is a short, natural-language description written primarily for an LLM during the **design phase** of a system.
123
+
124
+ It serves as:
125
+
126
+ * semantic guidance for generating handlers and emitters,
127
+ * a reminder of *why* a pin exists,
128
+ * a clarification of *when* a pin is used.
129
+
130
+ It is **not** a specification and **not authoritative**. Do not duplicate in a prompt what is in the pin contract.
131
+
132
+ ## What a Pin Prompt Should Answer
133
+
134
+ Depending on pin kind, the prompt should answer **one core question**.
135
+
136
+ * Output pins: *When do I emit, and why?*
137
+ * Input pins: *What do I do when a message arrives?*
138
+ * Request pins: *What is being requested, and for what purpose?*
139
+ * Reply pins: *What does this reply confirm or return?*
140
+
141
+ A pin prompt must **not**:
142
+
143
+ * Describe payload fields or structure
144
+ (that belongs in `types`)
145
+ * Repeat `vmbluType`
146
+ * Define validation rules
147
+ * Impose constraints not expressible elsewhere
148
+ * Explain internal implementation details
149
+
150
+ Avoid phrases like:
151
+
152
+ * “This pin sends an object containing…”
153
+ * “The payload has fields…”
154
+ * “Must always / must never…”
155
+
156
+ ## Length and Form
157
+
158
+ * **1–2 sentences**
159
+ * **Present tense**
160
+ * **Plain language**
161
+ * No lists, no markdown, no long explanations
162
+
163
+ Think: *intent note*, not documentation.
164
+
165
+ ## Examples
166
+
167
+ * output: *“Emits updated orbital parameters whenever the simulation time advances. Trigger: after physics integration.”*
168
+ * input: *“Applies incoming orbit updates to update the rendered trajectory.”*
169
+ * request: *“Requests the active camera data.”*
170
+ * reply: *“Returns the spec of the active camera to the requestor.”*
171
+
172
+ ## A.7 Request / Reply Semantics
173
+
174
+ A Request/Reply connection allows to group a message and the response to that message in one exchange.
175
+ Because the Request/Reply connection consists of two data exchanges, the request and the reply, the contract for the pin in the owner-role contains two vmbluTypes: one for the request and one for the reply.
176
+
177
+ - A **request pin** is an **output pin**.
178
+ - It is used by the requester node to initiate a request with `tx.request('pinName', payload)`.
179
+ - This function returns a **Promise** which resolves when the callee replies. The **Promise** is generated and managed by the runtime.
180
+
181
+ - A **reply pin** is an **input pin** on the callee.
182
+ - It receives the request payload and has a handler like any input pin.
183
+ - Inside this handler, the callee must call `tx.reply(payload)` to respond to the requester.
184
+ - The runtime delivers this reply on the backchannel and resolves the requester’s Promise.
185
+ - Note that the handler return value is ignored, optional async only to await internal work.
186
+ - If tx.reply is not called, the request promise will simply time out.
187
+
188
+ - **Connections**:
189
+ - Normally, `request` pins connect to `reply` pins.
190
+ - It is also valid to connect a `request` pin to an `input` pin (e.g. for logging or monitoring), but in that case no reply is sent.
191
+
192
+ Typical use of request/reply
193
+
194
+ The requesting node issues a request and then waits for the reply
195
+
196
+ ```js
197
+ tx.request('pinName', requestPayload).then ( replyPayload => {
198
+
199
+ // handle the reply from the other node
200
+ ...
201
+ })
202
+ ```
203
+
204
+ The receiving node has a handler for the request
205
+
206
+ ```js
207
+ onPinName(requestPayload) {
208
+ // does some processing
209
+ ...
210
+ // and replies to the requesting node
211
+ tx.reply(replyPayload)
212
+ }
213
+ ```
214
+
215
+ ## A.8 Factory Function Signature
216
+
217
+ A source node references its implementation via a **factory** object (`path` + `function`).
218
+ The factory function is called by the runtime to create the node instance.
219
+
220
+ The runtime will detect if the factory function is a generator function or a class name and will call the factory function in that case as follows: `new factoryFunction(...)`
221
+
222
+ In order to let documentation tools find the handlers of a node, add a _node_ JSdoc tag in the file where the handlers are defined.
223
+ The tage remains valid until the end of the file or until a new node tag is defined.
224
+
225
+ - Signature:
226
+
227
+ ```js
228
+ /**
229
+ * @node node name
230
+ */
231
+ export function createMyNode( tx, sx ) { ... }
232
+ ```
233
+
234
+ - tx: object exposing runtime message functions (send, request, reply).
235
+ - sx: arbitrary initialization data supplied by the model. sx can be null.
236
+ - rx: not passed to the node. Runtime-only directives; used by the runtime to decide how/where to host the node (e.g. worker thread, debug flags).
237
+
238
+ ## A.9 Dock Nodes and Drift
239
+
240
+ - A dock node references another node defined in a different file via a link.
241
+ - Pins and connections of the dock node are kept in the importing file.
242
+ - If the external node definition changes, the editor highlights differences (“drift”) between the dock node and its linked definition.
243
+
244
+ ## A.10 MCP Support
245
+
246
+ Vmblu supports the Model Context Protocol (MCP). An LLM can directly communicate with nodes of a vmblu application by sending messages to nodes. The node has to indicate which inputs are available as an MCP tool simply by adding the tag 'mcp' before the handler's header:
247
+
248
+ ```js
249
+ /**
250
+ * @mcp
251
+ * @param ...
252
+ */
253
+ onMessage() { // handler for the message
254
+ ...
255
+ }
256
+ ```
257
+
258
+ The messages marked will be assembled by the vmblu editor into a tool file that can be used by an LLM interacting with the application.
259
+
260
+ ## A.11 AI Generation Guidelines
261
+
262
+ For LLMs working with vmblu files:
263
+
264
+ - Respect node and pin names — do not rename unless explicitly asked.
265
+ - When generating source code for the nodes in the vmblu file, only generate code for the nodes, the _main_ function and node setup is generated directly from the vmblu file itself.
266
+ - When changing code for an application only change the code for the nodes, not the application file that was generated. That file will be re-generated by the editor.
267
+
268
+
@@ -0,0 +1,372 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "@vizual_model/cli/templates/0.9.1/blueprint.schema.json",
4
+ "title": "vmblu Blueprint Model (v0.9.1)",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["header", "root"],
8
+ "properties": {
9
+ "header": { "$ref": "#/$defs/Header" },
10
+
11
+ "imports": {
12
+ "description": "List of vmblu blueprint files that are referenced in the model. Allows for faster loading.",
13
+ "type": "array",
14
+ "items": { "type": "string", "minLength": 1 },
15
+ "uniqueItems": true
16
+ },
17
+
18
+ "factories": {
19
+ "description": "List of files where the source code of the source nodes can be found. Used by the docgen tool.",
20
+ "type": "array",
21
+ "items": { "type": "string", "minLength": 1 },
22
+ "uniqueItems": true
23
+ },
24
+
25
+ "libraries": {
26
+ "description": "List of vmblu blueprint files from which the editor allows the user to select nodes in an easy way.",
27
+ "type": "array",
28
+ "items": { "type": "string", "minLength": 1 },
29
+ "uniqueItems": true
30
+ },
31
+
32
+ "types": {
33
+ "description": "Repository of named data types used in pin contracts. Each vmbluType is either a primitive (string/number/boolean/any/...) or a key in this 'types' object.",
34
+ "$ref": "#/$defs/Types"
35
+ },
36
+
37
+ "root": {
38
+ "description": "The starting point of the vmblu blueprint model.",
39
+ "$ref": "#/$defs/Node"
40
+ }
41
+ },
42
+
43
+ "$defs": {
44
+ "Header": {
45
+ "description": "Contains meta information about the model. Used by tools and editors.",
46
+ "type": "object",
47
+ "properties": {
48
+ "version": { "type": "string" },
49
+ "created": { "type": "string", "format": "date-time" },
50
+ "saved": { "type": "string", "format": "date-time" },
51
+ "utc": { "type": "string", "format": "date-time" },
52
+ "style": { "type": "string" },
53
+ "runtime": { "type": "string" }
54
+ },
55
+ "additionalProperties": false,
56
+ "required": ["version", "created", "saved", "utc"]
57
+ },
58
+
59
+ "Types": {
60
+ "description": "Repository of named architectural data types.",
61
+ "type": "object",
62
+ "additionalProperties": { "$ref": "#/$defs/Type" }
63
+ },
64
+
65
+ "Type": {
66
+ "description": "Definition of a single named type used in contracts.",
67
+ "type": "object",
68
+ "properties": {
69
+ "kind": {
70
+ "description": "Basic category of the type.",
71
+ "type": "string",
72
+ "enum": ["object", "array", "external", "primitive"],
73
+ "default": "object"
74
+ },
75
+ "summary": {
76
+ "description": "Short human-readable description of the type.",
77
+ "type": "string"
78
+ },
79
+
80
+ "fields": {
81
+ "description": "For object types: map of field name to field descriptor.",
82
+ "type": "object",
83
+ "additionalProperties": {
84
+ "type": "object",
85
+ "properties": {
86
+ "vmbluType": {
87
+ "description": "A vmblu type: either a primitive (string/number/boolean/any/...) or a named type key in the 'types' section.",
88
+ "type": "string",
89
+ "minLength": 1
90
+ },
91
+ "summary": {
92
+ "description": "Short description of the field.",
93
+ "type": "string"
94
+ }
95
+ },
96
+ "required": ["vmbluType"],
97
+ "additionalProperties": false
98
+ }
99
+ },
100
+
101
+ "required": {
102
+ "description": "For object types: list of required field names.",
103
+ "type": "array",
104
+ "items": { "type": "string", "minLength": 1 },
105
+ "uniqueItems": true
106
+ },
107
+
108
+ "items": {
109
+ "description": "For array types: element type descriptor.",
110
+ "type": "object",
111
+ "properties": {
112
+ "vmbluType": {
113
+ "description": "A vmblu type: either a primitive (string/number/boolean/any/...) or a named type key in the 'types' section.",
114
+ "type": "string",
115
+ "minLength": 1
116
+ },
117
+ "summary": {
118
+ "description": "Short description of the element.",
119
+ "type": "string"
120
+ }
121
+ },
122
+ "required": ["vmbluType"],
123
+ "additionalProperties": false
124
+ },
125
+
126
+ "external": {
127
+ "description": "For external/opaque types: identifies the external symbol (e.g. THREE.Vector3).",
128
+ "type": "object",
129
+ "properties": {
130
+ "library": {
131
+ "description": "Library or package providing this type (e.g. 'three').",
132
+ "type": "string",
133
+ "minLength": 1
134
+ },
135
+ "symbol": {
136
+ "description": "Symbol name of the type in the library (e.g. 'Vector3').",
137
+ "type": "string",
138
+ "minLength": 1
139
+ }
140
+ },
141
+ "required": ["symbol"],
142
+ "additionalProperties": false
143
+ }
144
+ },
145
+ "allOf": [
146
+ {
147
+ "oneOf": [
148
+ {
149
+ "description": "Object type must define fields.",
150
+ "properties": { "kind": { "const": "object" } },
151
+ "required": ["fields"]
152
+ },
153
+ {
154
+ "description": "Array type must define items.",
155
+ "properties": { "kind": { "const": "array" } },
156
+ "required": ["items"]
157
+ },
158
+ {
159
+ "description": "External type must define external symbol info.",
160
+ "properties": { "kind": { "const": "external" } },
161
+ "required": ["external"]
162
+ },
163
+ {
164
+ "description": "Primitive type is opaque here (typically used for documentation or aliasing).",
165
+ "properties": { "kind": { "const": "primitive" } }
166
+ }
167
+ ]
168
+ }
169
+ ],
170
+ "additionalProperties": false
171
+ },
172
+
173
+ "Node": {
174
+ "description": "A node describes a component of a software system. Nodes communicate via messages. There are three types of nodes.",
175
+ "oneOf": [
176
+ {
177
+ "allOf": [
178
+ { "$ref": "#/$defs/NodeBase" },
179
+ { "$ref": "#/$defs/SourceNodeSpec" }
180
+ ],
181
+ "unevaluatedProperties": false
182
+ },
183
+ {
184
+ "allOf": [
185
+ { "$ref": "#/$defs/NodeBase" },
186
+ { "$ref": "#/$defs/GroupNodeSpec" }
187
+ ],
188
+ "unevaluatedProperties": false
189
+ },
190
+ {
191
+ "allOf": [
192
+ { "$ref": "#/$defs/NodeBase" },
193
+ { "$ref": "#/$defs/DockNodeSpec" }
194
+ ],
195
+ "unevaluatedProperties": false
196
+ }
197
+ ]
198
+ },
199
+
200
+ "NodeBase": {
201
+ "type": "object",
202
+ "properties": {
203
+ "kind": { "enum": ["source", "group", "dock"] },
204
+ "name": { "type": "string", "minLength": 1, "pattern": "^[^@]+$" },
205
+ "label": { "type": "string" },
206
+
207
+ "interfaces": {
208
+ "type": "array",
209
+ "items": { "$ref": "#/$defs/Interface" }
210
+ },
211
+
212
+ "prompt": { "type": "string" },
213
+
214
+ "sx": { "$ref": "#/$defs/NodeInitialization" },
215
+ "rx": { "$ref": "#/$defs/RuntimeDirectives" }
216
+ },
217
+ "required": ["kind", "name"]
218
+ },
219
+
220
+ "SourceNodeSpec": {
221
+ "description": "A source node has an implementation in source code.",
222
+ "type": "object",
223
+ "properties": {
224
+ "kind": { "const": "source" },
225
+ "factory": {
226
+ "description": "Describes where the node's implementation can be found.",
227
+ "type": "object",
228
+ "properties": {
229
+ "path": { "type": "string", "minLength": 1 },
230
+ "function": { "type": "string", "minLength": 1 }
231
+ },
232
+ "required": ["path", "function"],
233
+ "additionalProperties": false
234
+ }
235
+ },
236
+ "required": ["factory"]
237
+ },
238
+
239
+ "GroupNodeSpec": {
240
+ "description": "A group node consists of other connected nodes. It allows to build modular models.",
241
+ "type": "object",
242
+ "properties": {
243
+ "kind": { "const": "group" },
244
+
245
+ "nodes": {
246
+ "type": "array",
247
+ "items": { "$ref": "#/$defs/Node" }
248
+ },
249
+
250
+ "connections": {
251
+ "type": "array",
252
+ "items": { "$ref": "#/$defs/Connection" }
253
+ }
254
+ }
255
+ },
256
+
257
+ "DockNodeSpec": {
258
+ "description": "A dock node is a placeholder for a node that is defined in another file, specified in the link field.",
259
+ "type": "object",
260
+ "properties": {
261
+ "kind": { "const": "dock" },
262
+ "link": {
263
+ "type": "object",
264
+ "properties": {
265
+ "path": { "type": "string", "minLength": 1 },
266
+ "node": { "type": "string", "minLength": 1, "pattern": "^[^@]+$" }
267
+ },
268
+ "required": ["node"],
269
+ "additionalProperties": false
270
+ }
271
+ },
272
+ "required": ["link"]
273
+ },
274
+
275
+ "Interface": {
276
+ "description": "An interface groups a number of pins of a node.",
277
+ "type": "object",
278
+ "required": ["name"],
279
+ "properties": {
280
+ "name": { "type": "string", "pattern": "^(|[^@]+)$" },
281
+ "pins": {
282
+ "type": "array",
283
+ "items": { "$ref": "#/$defs/Pin" }
284
+ }
285
+ },
286
+ "additionalProperties": false
287
+ },
288
+
289
+ "Pin": {
290
+ "description": "A node can receive or send a message via a pin.",
291
+ "type": "object",
292
+ "required": ["name", "kind"],
293
+ "properties": {
294
+ "name": { "type": "string", "minLength": 1, "pattern": "^[^@]+$" },
295
+ "kind": { "enum": ["input", "output", "request", "reply"] },
296
+ "contract": {
297
+ "type": "object",
298
+ "oneOf": [
299
+ {
300
+ "type": "object",
301
+ "properties": {
302
+ "role": { "const": "follower" }
303
+ },
304
+ "required": ["role"],
305
+ "additionalProperties": false
306
+ },
307
+ {
308
+ "type": "object",
309
+ "properties": {
310
+ "role": { "const": "owner" },
311
+ "payload": { "type": "string", "minLength": 1 }
312
+ },
313
+ "required": ["role", "payload"],
314
+ "additionalProperties": false
315
+ },
316
+ {
317
+ "type": "object",
318
+ "properties": {
319
+ "role": { "const": "owner" },
320
+ "payload": {
321
+ "type": "object",
322
+ "properties": {
323
+ "request": { "type": "string", "minLength": 1 },
324
+ "reply": { "type": "string", "minLength": 1 }
325
+ },
326
+ "required": ["request", "reply"],
327
+ "additionalProperties": false
328
+ }
329
+ },
330
+ "required": ["role", "payload"],
331
+ "additionalProperties": false
332
+ }
333
+ ]
334
+ },
335
+ "prompt": { "type": "string" }
336
+ },
337
+ "additionalProperties": false
338
+ },
339
+
340
+ "Connection": {
341
+ "description": "A connection can be made between two pins. The message flow is from 'src' to 'dst'.",
342
+ "type": "object",
343
+ "additionalProperties": false,
344
+ "required": ["src", "dst"],
345
+ "properties": {
346
+ "src": { "$ref": "#/$defs/Address" },
347
+ "dst": { "$ref": "#/$defs/Address" }
348
+ }
349
+ },
350
+
351
+ "Address": {
352
+ "description": "The address of a pin. If node is omitted the node is the containing group node.",
353
+ "type": "object",
354
+ "additionalProperties": false,
355
+ "properties": {
356
+ "node": { "type": "string", "minLength": 1, "pattern": "^[^@]+$" },
357
+ "pin": { "type": "string", "minLength": 1, "pattern": "^[^@]+$" }
358
+ },
359
+ "required": ["pin"]
360
+ },
361
+
362
+ "NodeInitialization": {
363
+ "description": "Node-defined initialization data passed to the node at creation time. Shape is entirely up to the node designer.",
364
+ "type": "object"
365
+ },
366
+
367
+ "RuntimeDirectives": {
368
+ "description": "Runtime-defined creation/hosting directives. Shape is determined by the runtime (e.g., host=worker, debug=true).",
369
+ "type": "object"
370
+ }
371
+ }
372
+ }