omp packages

@restranger/repi-lib0.0.1-ca9b82c

Contracts, service seam registry, TUI display primitives, and session utilities for repi plugins

restranger Published 8 hours ago MIT ~796 downloads/mo

Untested on omp

This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.

Did it work for you?

Result

One report per person. A new report replaces your old one.

Reports 0

No reports yet.

Readme

@restranger/repi-lib

Общая библиотека для независимых repi-плагинов. Это не плагин и не ядро: lib не загружает расширения, не знает про permissions, modes или ui и не имеет зависимостей от других @restranger/repi-* пакетов.

Зачем

Pi загружает каждое расширение отдельно и даёт ему ExtensionAPI. Плагинам нужен только один общий слой, чтобы находить друг друга, когда они установлены вместе, и не падать, когда соседа нет.

lib даёт четыре вещи:

  • contracts — id контракта с мажорной версией (re:permissions@1, re:modes@1, re:ui@1), формы сервисов и ReadyEvent;
  • seam — process-global реестр сервисов с ключом contract + sessionId;
  • session — единый способ получить sessionId из контекста хоста;
  • shared types — ViewNode, Tone, Disposable и структурные типы Pi, чтобы плагинам не приходилось импортировать друг друга или тянуть тяжёлый host-пакет в тесты.

Провайдер

import { announceReady, getSessionId, publishService } from "@restranger/repi-lib";

export default function permissionsPlugin(pi) {
  const registrations = new Map();

  pi.on("session_start", (_event, ctx) => {
    const sessionId = getSessionId(ctx);
    registrations.get(sessionId)?.dispose();
    const registration = publishService({
      contract: "re:permissions@1",
      sessionId,
      owner: "repi:permissions",
      impl: permissionsService,
    });
    registrations.set(sessionId, registration);

    announceReady(pi.events, {
      contract: "re:permissions@1",
      version: 1,
      sessionId,
      owner: "repi:permissions",
      capabilities: ["policy-layer", "decision-events"],
    });
  });

  pi.on("session_shutdown", (_event, ctx) => {
    const sessionId = getSessionId(ctx);
    registrations.get(sessionId)?.dispose();
    registrations.delete(sessionId);
  });
}

Потребитель

import { getService, onService } from "@restranger/repi-lib";

const current = getService<PermissionsService>("re:permissions@1", sessionId);
if (current) {
  attach(current);
} else {
  // Standalone path: permissions not installed, keep working without it.
}

const registration = onService<PermissionsService>(
  pi.events,
  "re:permissions@1",
  sessionId,
  (permissions) => attach(permissions),
);
pi.on("session_shutdown", () => registration.dispose());

onService сначала подписывается на repi:ready:v1, затем делает pull из реестра: публикация между этими шагами не теряется. Повторный announce того же сервиса не вызывает обработчик дважды.

Правила

  • Плагин зависит от @restranger/repi-lib, но никогда от другого плагина.
  • В рантайме — только getService / onService; никаких статических импортов чужого плагина.
  • import type из чужого плагина тоже не нужен: общие типы живут в lib.
  • sessionId — часть ключа. Один процесс может содержать несколько сессий; сервисы не должны протекать между ними.
  • publishService одного владельца заменяет свой же сервис при reload; другой владелец — ошибка.
  • dispose идемпотентен и удаляет только свою запись.
  • globalThis + Symbol.for используются потому, что jiti изолирует модули между расширениями. Обычный module-level singleton не будет общим.