@abraca/plugin 2.26.0 → 2.28.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 CHANGED
@@ -116,11 +116,17 @@ interface AbraPlugin {
116
116
  */
117
117
  serverCacheRenderer?(docId: string, ydoc: AbraYDoc): unknown;
118
118
  /**
119
- * Server-side background runners (Nitro context, service-role identity).
119
+ * Host-side background runners. Historically a Nitro, service-role context;
120
+ * now host-agnostic and capability-gated — a runner declares what it
121
+ * `requires` (e.g. `process`) and any host that can satisfy it runs it
122
+ * (Nitro, Electron-main `utilityProcess`, a headless Node daemon). Runners
123
+ * with no `requires` run everywhere, exactly as before. Identity is the
124
+ * host's to decide: a doc-transport runner can use the logged-in user's
125
+ * identity rather than a global service role.
120
126
  *
121
- * Typed as `unknown[]` in the shared contract because each host wraps
122
- * the runner ctx with its own `AbracadabraClient` / `AbracadabraProvider`
123
- * / `DocCacheAPI` / etc. — a structural override here would force every
127
+ * Typed as `unknown[]` in the shared contract because each host wraps the
128
+ * runner ctx with its own `AbracadabraClient` / `AbracadabraProvider` /
129
+ * `DocCacheAPI` / etc. — a structural override here would force every
124
130
  * consumer to either match the shared base structure exactly or fight
125
131
  * input-contravariance. Consumers narrow this to their own
126
132
  * `ServerRunnerDefinition[]` in their `CouPlugin` / `AbracadabraPlugin`
@@ -253,6 +259,72 @@ interface CollaborationUser {
253
259
  publicKey?: string;
254
260
  docId?: string;
255
261
  }
262
+ /**
263
+ * A capability a runner can require from its host environment. A host starts a
264
+ * runner only if it can satisfy every requirement, and injects the matching
265
+ * handle into the runner context's `host` bag. Hosts that can't satisfy a
266
+ * requirement skip the runner. This is what lets ONE runner definition target
267
+ * Electron-main, a headless Node daemon, or a future Nitro-with-PTY host
268
+ * interchangeably.
269
+ *
270
+ * Currently the only token is `process` (the host can execute OS processes /
271
+ * spawn a pseudo-terminal). String union so new capabilities stay additive.
272
+ */
273
+ type HostCapability = "process";
274
+ /** Options for spawning a pseudo-terminal via the `process` host capability. */
275
+ interface PtySpawnOptions {
276
+ /** Working directory. Defaults to the host runner's cwd. */
277
+ cwd?: string;
278
+ /** Extra environment variables, merged over the host's. */
279
+ env?: Record<string, string>;
280
+ /** Initial column count. Host default (e.g. 80) when omitted. */
281
+ cols?: number;
282
+ /** Initial row count. Host default (e.g. 24) when omitted. */
283
+ rows?: number;
284
+ /** `TERM` value. Defaults to `xterm-color`. */
285
+ term?: string;
286
+ }
287
+ /**
288
+ * A live pseudo-terminal handle returned by `ProcessHostCapability.spawn`. The
289
+ * host owns the real PTY (node-pty, a helper binary, …); the runner drives it
290
+ * through this structural interface so the plugin bundle never imports a native
291
+ * module.
292
+ */
293
+ interface PtyHandle {
294
+ /** Write bytes to the PTY's stdin. */
295
+ write(data: string | Uint8Array): void;
296
+ /** Resize the PTY. */
297
+ resize(cols: number, rows: number): void;
298
+ /** Subscribe to PTY output. Returns an unsubscribe function. */
299
+ onData(listener: (chunk: Uint8Array) => void): () => void;
300
+ /** Subscribe to PTY exit. Returns an unsubscribe function. */
301
+ onExit(listener: (info: {
302
+ exitCode: number;
303
+ signal?: number;
304
+ }) => void): () => void;
305
+ /** Terminate the PTY (host-defined default signal, e.g. SIGTERM). */
306
+ kill(signal?: string): void;
307
+ /** OS process id, when the host exposes one. */
308
+ readonly pid?: number;
309
+ }
310
+ /**
311
+ * The `process` capability handle: the host can spawn OS processes / PTYs.
312
+ * Injected into a runner's context (`ctx.host.process`) only by hosts that can
313
+ * satisfy `requires: ['process']`.
314
+ */
315
+ interface ProcessHostCapability {
316
+ /** Spawn a pseudo-terminal running `file` with `args`. */
317
+ spawn(file: string, args?: readonly string[], options?: PtySpawnOptions): PtyHandle;
318
+ }
319
+ /**
320
+ * Bag of host-capability handles injected into a runner's context. The host
321
+ * populates only what it can provide; a runner that declared a matching
322
+ * `requires` entry is guaranteed the corresponding handle is present (the host
323
+ * would not have started it otherwise).
324
+ */
325
+ interface HostCapabilities {
326
+ process?: ProcessHostCapability;
327
+ }
256
328
  /**
257
329
  * Cleanup function returned by a runner's `start()`. Called on graceful
258
330
  * server shutdown.
@@ -268,6 +340,14 @@ type RunnerCleanup = (() => void | Promise<void>) | undefined | void;
268
340
  */
269
341
  interface ServerRunnerDefinition<TCtx = ServerRunnerContextBase> {
270
342
  name: string;
343
+ /**
344
+ * Host capabilities this runner needs. A host starts the runner only if it
345
+ * can satisfy every entry (and injects the matching handles into the
346
+ * context's `host` bag). Hosts that can't satisfy a requirement skip the
347
+ * runner. Omit / leave empty to run in any host — the historical behaviour
348
+ * of pure Y.Doc-observer runners, unchanged.
349
+ */
350
+ requires?: readonly HostCapability[];
271
351
  /** Method shorthand — bivariant, so consumer types can narrow `ctx`. */
272
352
  start(ctx: TCtx): Promise<RunnerCleanup> | RunnerCleanup;
273
353
  }
@@ -280,6 +360,12 @@ interface ServerRunnerContextBase {
280
360
  rootDoc: AbraYDoc;
281
361
  /** Space ID if this runner is scoped to a space; `null` for hub-scoped runners. */
282
362
  spaceId?: string | null;
363
+ /**
364
+ * Host-capability handles satisfying this runner's `requires`. Populated by
365
+ * the host with whatever it can provide; a runner that declared
366
+ * `requires: ['process']` can rely on `host.process` being present.
367
+ */
368
+ host?: HostCapabilities;
283
369
  }
284
370
  //#endregion
285
371
  //#region packages/plugin/src/registry.d.ts
@@ -386,7 +472,7 @@ declare class PluginRegistry<P extends AbraPlugin = AbraPlugin> {
386
472
  * Wasm / Web Worker / iframe sandboxing so the contract is enforced at the
387
473
  * runtime boundary, not just the host-API boundary.
388
474
  */
389
- type PluginCapability = /** Outbound HTTP — `network` (any host) or `network:<host>` (exact match, wildcards allowed). */`network${"" | `:${string}`}` /** Filesystem reads — only meaningful in Tauri / native hosts. */ | "fs:read" /** Filesystem writes — Tauri / native only. */ | "fs:write" /** Read the clipboard. */ | "clipboard:read" /** Write the clipboard. */ | "clipboard:write" /** Read any Y.Doc the host has loaded. */ | "doc-read" /** Mutate Y.Docs (transact_mut against the host's provider). */ | "doc-write" /** Broadcast presence / awareness state. */ | "awareness" /** Register an RPC v1 handler on the service runner. */ | "server-runner" /** Contribute items to the global command palette. */ | "command-palette" /** Contribute editor toolbar items. */ | "toolbar" /** Contribute bubble-menu items (text selection). */ | "bubble-menu" /** Contribute slash-command suggestion items. */ | "slash-command" /** Contribute drag-handle items. */ | "drag-handle" /** Register a page-type renderer with the given slug. */ | `page-type:${string}` /** Send messages on the in-doc chat channel. */ | "chat:send" /** Read in-doc chat history. */ | "chat:read" /** Show user-visible notifications. */ | "notifications:show";
475
+ type PluginCapability = /** Outbound HTTP — `network` (any host) or `network:<host>` (exact match, wildcards allowed). */`network${"" | `:${string}`}` /** Filesystem reads — only meaningful in Tauri / native hosts. */ | "fs:read" /** Filesystem writes — Tauri / native only. */ | "fs:write" /** Read the clipboard. */ | "clipboard:read" /** Write the clipboard. */ | "clipboard:write" /** Read any Y.Doc the host has loaded. */ | "doc-read" /** Mutate Y.Docs (transact_mut against the host's provider). */ | "doc-write" /** Broadcast presence / awareness state. */ | "awareness" /** Register an RPC v1 handler on the service runner. */ | "server-runner" /** Execute OS processes / spawn a pseudo-terminal — only honoured by process-capable hosts (Electron main, headless Node). */ | "process:exec" /** Contribute items to the global command palette. */ | "command-palette" /** Contribute editor toolbar items. */ | "toolbar" /** Contribute bubble-menu items (text selection). */ | "bubble-menu" /** Contribute slash-command suggestion items. */ | "slash-command" /** Contribute drag-handle items. */ | "drag-handle" /** Register a page-type renderer with the given slug. */ | `page-type:${string}` /** Send messages on the in-doc chat channel. */ | "chat:send" /** Read in-doc chat history. */ | "chat:read" /** Show user-visible notifications. */ | "notifications:show";
390
476
  interface PluginManifestAuthor {
391
477
  /** GitHub login. Identifies the author in the registry. */
392
478
  github?: string;
@@ -673,4 +759,4 @@ interface SandboxOptions extends PluginHostOptions {
673
759
  */
674
760
  declare function loadSandboxedPlugin(manifest: Pick<PluginManifest, "id" | "capabilities">, artifactUrl: string, options?: SandboxOptions): Promise<SandboxedPlugin>;
675
761
  //#endregion
676
- export { type AbraAwarenessContribution, type AbraCommandItem, type AbraKeyboardShortcut, type AbraMentionItem, type AbraMentionProvider, type AbraNodePanelSlot, type AbraPlugin, type AbraSettingsPanel, type AbraTiptapExtension, type AbraYDoc, CapabilityDenied, type ClientPluginCtx, type CollaborationUser, type CommandPaletteCtx, type DragHandlePluginCtx, type EditorPluginCtx, type EditorRefLike, type ExternalPluginEntry, type GuardedClipboard, type GuardedFetch, type GuardedNotifications, type MentionPluginCtx, type NodePanelCtx, type PluginCapability, type PluginHost, type PluginHostOptions, type PluginManifest, type PluginManifestAuthor, type PluginManifestContributes, type PluginOrigin, type PluginPricing, PluginRegistry, type PluginRegistryEntry, type PluginVersionStatus, type RefLike, type RunnerCleanup, type SandboxOptions, type SandboxedPlugin, type ServerRunnerContextBase, type ServerRunnerDefinition, createPluginHost, isSafePluginUrl, loadSandboxedPlugin, matchesNetworkCapability, normalizePluginUrl };
762
+ export { type AbraAwarenessContribution, type AbraCommandItem, type AbraKeyboardShortcut, type AbraMentionItem, type AbraMentionProvider, type AbraNodePanelSlot, type AbraPlugin, type AbraSettingsPanel, type AbraTiptapExtension, type AbraYDoc, CapabilityDenied, type ClientPluginCtx, type CollaborationUser, type CommandPaletteCtx, type DragHandlePluginCtx, type EditorPluginCtx, type EditorRefLike, type ExternalPluginEntry, type GuardedClipboard, type GuardedFetch, type GuardedNotifications, type HostCapabilities, type HostCapability, type MentionPluginCtx, type NodePanelCtx, type PluginCapability, type PluginHost, type PluginHostOptions, type PluginManifest, type PluginManifestAuthor, type PluginManifestContributes, type PluginOrigin, type PluginPricing, PluginRegistry, type PluginRegistryEntry, type PluginVersionStatus, type ProcessHostCapability, type PtyHandle, type PtySpawnOptions, type RefLike, type RunnerCleanup, type SandboxOptions, type SandboxedPlugin, type ServerRunnerContextBase, type ServerRunnerDefinition, createPluginHost, isSafePluginUrl, loadSandboxedPlugin, matchesNetworkCapability, normalizePluginUrl };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abraca/plugin",
3
- "version": "2.26.0",
3
+ "version": "2.28.0",
4
4
  "description": "Shared plugin contract + registry for @abraca/nuxt and cou-shell.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -26,6 +26,11 @@ export {
26
26
  type AbraSettingsPanel,
27
27
  type AbraKeyboardShortcut,
28
28
  type CollaborationUser,
29
+ type HostCapability,
30
+ type PtySpawnOptions,
31
+ type PtyHandle,
32
+ type ProcessHostCapability,
33
+ type HostCapabilities,
29
34
  type RunnerCleanup,
30
35
  type ServerRunnerDefinition,
31
36
  type ServerRunnerContextBase,
package/src/manifest.ts CHANGED
@@ -35,6 +35,8 @@ export type PluginCapability =
35
35
  | "awareness"
36
36
  /** Register an RPC v1 handler on the service runner. */
37
37
  | "server-runner"
38
+ /** Execute OS processes / spawn a pseudo-terminal — only honoured by process-capable hosts (Electron main, headless Node). */
39
+ | "process:exec"
38
40
  /** Contribute items to the global command palette. */
39
41
  | "command-palette"
40
42
  /** Contribute editor toolbar items. */
package/src/types.ts CHANGED
@@ -151,11 +151,17 @@ export interface AbraPlugin {
151
151
  serverCacheRenderer?(docId: string, ydoc: AbraYDoc): unknown;
152
152
 
153
153
  /**
154
- * Server-side background runners (Nitro context, service-role identity).
154
+ * Host-side background runners. Historically a Nitro, service-role context;
155
+ * now host-agnostic and capability-gated — a runner declares what it
156
+ * `requires` (e.g. `process`) and any host that can satisfy it runs it
157
+ * (Nitro, Electron-main `utilityProcess`, a headless Node daemon). Runners
158
+ * with no `requires` run everywhere, exactly as before. Identity is the
159
+ * host's to decide: a doc-transport runner can use the logged-in user's
160
+ * identity rather than a global service role.
155
161
  *
156
- * Typed as `unknown[]` in the shared contract because each host wraps
157
- * the runner ctx with its own `AbracadabraClient` / `AbracadabraProvider`
158
- * / `DocCacheAPI` / etc. — a structural override here would force every
162
+ * Typed as `unknown[]` in the shared contract because each host wraps the
163
+ * runner ctx with its own `AbracadabraClient` / `AbracadabraProvider` /
164
+ * `DocCacheAPI` / etc. — a structural override here would force every
159
165
  * consumer to either match the shared base structure exactly or fight
160
166
  * input-contravariance. Consumers narrow this to their own
161
167
  * `ServerRunnerDefinition[]` in their `CouPlugin` / `AbracadabraPlugin`
@@ -316,6 +322,82 @@ export interface CollaborationUser {
316
322
  docId?: string;
317
323
  }
318
324
 
325
+ // ── Host capabilities (capability-gated runners) ───────────────────────────────
326
+
327
+ /**
328
+ * A capability a runner can require from its host environment. A host starts a
329
+ * runner only if it can satisfy every requirement, and injects the matching
330
+ * handle into the runner context's `host` bag. Hosts that can't satisfy a
331
+ * requirement skip the runner. This is what lets ONE runner definition target
332
+ * Electron-main, a headless Node daemon, or a future Nitro-with-PTY host
333
+ * interchangeably.
334
+ *
335
+ * Currently the only token is `process` (the host can execute OS processes /
336
+ * spawn a pseudo-terminal). String union so new capabilities stay additive.
337
+ */
338
+ export type HostCapability = "process";
339
+
340
+ /** Options for spawning a pseudo-terminal via the `process` host capability. */
341
+ export interface PtySpawnOptions {
342
+ /** Working directory. Defaults to the host runner's cwd. */
343
+ cwd?: string;
344
+ /** Extra environment variables, merged over the host's. */
345
+ env?: Record<string, string>;
346
+ /** Initial column count. Host default (e.g. 80) when omitted. */
347
+ cols?: number;
348
+ /** Initial row count. Host default (e.g. 24) when omitted. */
349
+ rows?: number;
350
+ /** `TERM` value. Defaults to `xterm-color`. */
351
+ term?: string;
352
+ }
353
+
354
+ /**
355
+ * A live pseudo-terminal handle returned by `ProcessHostCapability.spawn`. The
356
+ * host owns the real PTY (node-pty, a helper binary, …); the runner drives it
357
+ * through this structural interface so the plugin bundle never imports a native
358
+ * module.
359
+ */
360
+ export interface PtyHandle {
361
+ /** Write bytes to the PTY's stdin. */
362
+ write(data: string | Uint8Array): void;
363
+ /** Resize the PTY. */
364
+ resize(cols: number, rows: number): void;
365
+ /** Subscribe to PTY output. Returns an unsubscribe function. */
366
+ onData(listener: (chunk: Uint8Array) => void): () => void;
367
+ /** Subscribe to PTY exit. Returns an unsubscribe function. */
368
+ onExit(
369
+ listener: (info: { exitCode: number; signal?: number }) => void,
370
+ ): () => void;
371
+ /** Terminate the PTY (host-defined default signal, e.g. SIGTERM). */
372
+ kill(signal?: string): void;
373
+ /** OS process id, when the host exposes one. */
374
+ readonly pid?: number;
375
+ }
376
+
377
+ /**
378
+ * The `process` capability handle: the host can spawn OS processes / PTYs.
379
+ * Injected into a runner's context (`ctx.host.process`) only by hosts that can
380
+ * satisfy `requires: ['process']`.
381
+ */
382
+ export interface ProcessHostCapability {
383
+ /** Spawn a pseudo-terminal running `file` with `args`. */
384
+ spawn(
385
+ file: string,
386
+ args?: readonly string[],
387
+ options?: PtySpawnOptions,
388
+ ): PtyHandle;
389
+ }
390
+
391
+ /**
392
+ * Bag of host-capability handles injected into a runner's context. The host
393
+ * populates only what it can provide; a runner that declared a matching
394
+ * `requires` entry is guaranteed the corresponding handle is present (the host
395
+ * would not have started it otherwise).
396
+ */
397
+ export interface HostCapabilities {
398
+ process?: ProcessHostCapability;
399
+ }
400
+
319
401
  // ── Server runners ────────────────────────────────────────────────────────────
320
402
 
321
403
  /**
@@ -337,6 +419,14 @@ export type RunnerCleanup =
337
419
  */
338
420
  export interface ServerRunnerDefinition<TCtx = ServerRunnerContextBase> {
339
421
  name: string;
422
+ /**
423
+ * Host capabilities this runner needs. A host starts the runner only if it
424
+ * can satisfy every entry (and injects the matching handles into the
425
+ * context's `host` bag). Hosts that can't satisfy a requirement skip the
426
+ * runner. Omit / leave empty to run in any host — the historical behaviour
427
+ * of pure Y.Doc-observer runners, unchanged.
428
+ */
429
+ requires?: readonly HostCapability[];
340
430
  /** Method shorthand — bivariant, so consumer types can narrow `ctx`. */
341
431
  start(ctx: TCtx): Promise<RunnerCleanup> | RunnerCleanup;
342
432
  }
@@ -350,4 +440,10 @@ export interface ServerRunnerContextBase {
350
440
  rootDoc: AbraYDoc;
351
441
  /** Space ID if this runner is scoped to a space; `null` for hub-scoped runners. */
352
442
  spaceId?: string | null;
443
+ /**
444
+ * Host-capability handles satisfying this runner's `requires`. Populated by
445
+ * the host with whatever it can provide; a runner that declared
446
+ * `requires: ['process']` can rely on `host.process` being present.
447
+ */
448
+ host?: HostCapabilities;
353
449
  }