@deepblend/dsh-blender-provider-local 0.2.6 → 0.2.8

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/lib/index.js CHANGED
@@ -167,6 +167,20 @@ export const ProviderConfig = z.object({
167
167
  sessionActions: z.array(z.string()).default([]),
168
168
  /** How long a kept session may sit idle before it is closed. */
169
169
  sessionIdleMs: z.number().default(60_000),
170
+ /**
171
+ * The socket a Blender bridge is listening on, when the operator wants the session actions served by
172
+ * THE USER'S OWN BLENDER rather than one this provider spawns (SPEC §20 M6 "Live Bridge").
173
+ *
174
+ * Empty means "spawn one", which is what every deployment did before this key existed. Setting it is
175
+ * how a user says *use the Blender I already have open* — and the difference is not speed, it is that
176
+ * they can watch the operations happen in the window they are looking at.
177
+ *
178
+ * A CONFIGURED SOCKET THAT DOES NOT ANSWER IS AN ERROR, not a fallback to spawning. Silently doing
179
+ * something else than what was asked is the failure mode this repository keeps paying for: the user
180
+ * would see operations succeed and never learn that their Blender was not the one doing them. The
181
+ * refusal names the add-on, because that is what has to be enabled.
182
+ */
183
+ sessionSocket: z.string().default(''),
170
184
  })
171
185
 
172
186
  /**
@@ -211,6 +225,28 @@ async function connect(socketPath, timeoutMs) {
211
225
  }
212
226
  }
213
227
 
228
+ /**
229
+ * The actions this provider can serve from a kept session, and the ONLY ones it can.
230
+ *
231
+ * DERIVED, NOT CHOSEN: these are exactly the actions whose calls go through `runBootstrap`, which is
232
+ * the one place the session path is decided. Everything else spawns its own process on purpose —
233
+ * `render_frames` most of all, because cancelling a render has to kill exactly that render, and a
234
+ * request inside a kept session cannot be killed without ending the session (and, attached, the
235
+ * user's own Blender).
236
+ *
237
+ * WHY THE LIST IS CHECKED RATHER THAN TRUSTED. MEASURED: naming `render_frames` in `sessionActions`
238
+ * used to do NOTHING AT ALL — the configuration was inert, the render ran in its own process as
239
+ * before, and nothing anywhere said so. A key that silently does nothing is worse than a key that
240
+ * fails: the operator believes they changed something. `contract/session-routing.test.mjs` derives
241
+ * this set from the source, so adding a `runBootstrap` call site without adding it here goes red.
242
+ */
243
+ export const SESSION_ROUTABLE_ACTIONS = Object.freeze([
244
+ 'get_capabilities',
245
+ 'compile_scene',
246
+ 'render_preview',
247
+ 'render_views',
248
+ ])
249
+
214
250
  /**
215
251
  * A live Blender, presented as one shape whatever carries the bytes.
216
252
  *
@@ -560,6 +596,20 @@ export default class LocalBlenderRuntime extends Service {
560
596
  // See the host's constructor: a key this row does not read is a startup error, because the schema
561
597
  // accepts it silently (SPEC §17's nested groups versus this package's flat keys).
562
598
  assertKnownConfigKeys(ProviderConfig, config, 'deepblend-blender-runtime')
599
+ // A CONFIGURED ACTION THAT CANNOT BE ROUTED IS REFUSED AT STARTUP, not ignored. Naming a render
600
+ // here would leave the operator believing their renders now run in their own Blender, while the
601
+ // render quietly kept its own process — the same shape as a key this row does not read, which the
602
+ // line above already refuses.
603
+ const unroutable = (config.sessionActions ?? []).filter(action => !SESSION_ROUTABLE_ACTIONS.includes(action))
604
+ if (unroutable.length > 0) {
605
+ throw new Error(
606
+ `sessionActions names ${unroutable.map(action => `"${action}"`).join(', ')}, and this provider can only ` +
607
+ `serve ${SESSION_ROUTABLE_ACTIONS.join(', ')} from a kept session. The renders that must be cancellable ` +
608
+ '(`render_frames`) spawn their own process on purpose: cancelling one kills exactly that render, and a ' +
609
+ 'request inside a kept session cannot be killed without ending the session — which, attached, is the ' +
610
+ 'user\'s own Blender.',
611
+ )
612
+ }
563
613
  this.config = config
564
614
  this.bootstrapPath = this._resolveBootstrapPath(config.bootstrapPath)
565
615
  /** Resolved once: the host must be configured with the same value. */
@@ -948,7 +998,12 @@ export default class LocalBlenderRuntime extends Service {
948
998
  */
949
999
  async _keptSession() {
950
1000
  if (this._session !== null && !this._session.closed) return this._session
951
- this._session = await this.openSession()
1001
+ const socket = this.config.sessionSocket
1002
+ // THE USER'S BLENDER, when they have named one. `attachSession` owns the refusal and its message;
1003
+ // this only decides which of the two transports the configured actions travel over.
1004
+ this._session = typeof socket === 'string' && socket.length > 0
1005
+ ? await this.attachSession(socket)
1006
+ : await this.openSession()
952
1007
  return this._session
953
1008
  }
954
1009
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepblend/dsh-blender-provider-local",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "type": "module",
5
5
  "description": "Local Blender Provider: the BlenderRuntime Service over ctx.subprocess (batch execution).",
6
6
  "main": "lib/index.js",
@@ -11,7 +11,7 @@
11
11
  "./package.json": "./package.json"
12
12
  },
13
13
  "dependencies": {
14
- "@deepblend/dsh-blender-contracts": "0.2.6"
14
+ "@deepblend/dsh-blender-contracts": "0.2.8"
15
15
  },
16
16
  "license": "MIT",
17
17
  "repository": {