New:Socket for Asana Is Now Available.Learn more
Get Started

@opencode-ai/plugin

Package Overview
Dependencies
Maintainers
2
Versions
11563
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@opencode-ai/plugin - npm Package Compare versions

Comparing version
0.0.0-dev-18706
to
0.0.0-dev-18708
+19
dist/effect/rpc.d.ts
import type { RpcApi } from "@opencode-ai/client/effect/api";
export type { RpcClient } from "@opencode-ai/client/effect/api";
import type { Rpc } from "@opencode-ai/schema/rpc";
import type { Effect, Scope } from "effect";
import type { Registration } from "./registration.js";
export interface RpcCallContext<M extends Rpc.Method> {
readonly error: Rpc.ErrorFactory<M>;
}
export type RpcHandlers<D extends Rpc.Definition> = {
readonly [Name in keyof D["methods"]]: (input: Rpc.Output<D["methods"][Name]["input"]>, context: RpcCallContext<D["methods"][Name]>) => Effect.Effect<Rpc.HandlerOutput<D["methods"][Name]["output"]>, Rpc.HandlerError<D["methods"][Name]>>;
};
export interface RpcRegistration<D extends Rpc.Definition> extends Registration {
readonly events: {
readonly emit: (...args: Rpc.EventInput<D>) => Effect.Effect<void, unknown>;
};
}
export interface RpcDomain extends RpcApi<Rpc.SystemError, never, unknown> {
readonly register: <const D extends Rpc.Definition>(definition: D, handlers: RpcHandlers<NoInfer<D>>) => Effect.Effect<RpcRegistration<D>, unknown, Scope.Scope>;
}
import type { RpcApi, RpcCallOptions } from "@opencode-ai/client/promise/api";
import type { Rpc } from "@opencode-ai/schema/rpc";
import type { Registration } from "./registration.js";
export type { RpcEventPayload } from "@opencode-ai/client/promise/api";
export interface RpcCallContext<M extends Rpc.Method> {
readonly signal: AbortSignal;
readonly error: Rpc.ErrorFactory<M>;
}
export type RpcHandlers<D extends Rpc.PortableDefinition> = {
readonly [Name in keyof D["methods"]]: (input: Rpc.Output<D["methods"][Name]["input"]>, context: RpcCallContext<D["methods"][Name]>) => Promise<Rpc.HandlerOutput<D["methods"][Name]["output"]> | Rpc.HandlerError<D["methods"][Name]>>;
};
export interface RpcRegistration<D extends Rpc.PortableDefinition> extends Registration {
readonly events: {
readonly emit: (...args: Rpc.EventInput<D>) => Promise<void>;
};
}
export interface RpcDomain extends RpcApi<Pick<RpcCallOptions, "signal"> & {
readonly location?: never;
readonly headers?: never;
}> {
readonly register: <const D extends Rpc.PortableDefinition>(definition: D, handlers: RpcHandlers<NoInfer<D>>) => Promise<RpcRegistration<D>>;
}
export { Rpc } from "@opencode-ai/schema/rpc";
export { Rpc } from "@opencode-ai/schema/rpc";
+1
-0

@@ -14,4 +14,5 @@ export * as Plugin from "./plugin.js";

export { Reference } from "@opencode-ai/schema/reference";
export { Rpc } from "@opencode-ai/schema/rpc";
export { Skill } from "@opencode-ai/schema/skill";
export { Vcs } from "@opencode-ai/schema/vcs";
export { WebSearch } from "@opencode-ai/schema/websearch";

@@ -13,4 +13,5 @@ export * as Plugin from "./plugin.js";

export { Reference } from "@opencode-ai/schema/reference";
export { Rpc } from "@opencode-ai/schema/rpc";
export { Skill } from "@opencode-ai/schema/skill";
export { Vcs } from "@opencode-ai/schema/vcs";
export { WebSearch } from "@opencode-ai/schema/websearch";
+2
-1

@@ -16,2 +16,3 @@ import type { ExperimentalApi, GenerateApi, PluginApi } from "@opencode-ai/client/effect/api";

import type { ReferenceDomain } from "./reference.js";
import type { RpcDomain } from "./rpc.js";
import type { SessionDomain } from "./session.js";

@@ -42,2 +43,3 @@ import type { ShellDomain } from "./shell.js";

readonly reference: ReferenceDomain;
readonly rpc: RpcDomain;
readonly session: SessionDomain;

@@ -53,3 +55,2 @@ readonly shell: ShellDomain;

readonly id: string;
readonly tui?: boolean;
readonly vcs?: VcsDiscovery;

@@ -56,0 +57,0 @@ readonly effect: (context: Context) => Effect.Effect<void, never, R>;

@@ -6,2 +6,101 @@ import { Tool } from "@opencode-ai/schema/tool";

const compiledEndpoints = new WeakMap();
class ReturnedRpcError extends Error {
type;
data;
constructor(type, message, data) {
super(message);
this.type = type;
this.data = data;
}
}
const makeStreams = Effect.fn("Plugin.Event.makeStreams")(function* () {
const context = yield* Effect.context();
const subscriptions = new Set();
// Async iterators own separate scopes, so close them when the plugin unloads.
yield* Effect.addFinalizer(() => Effect.promise(() => Promise.all(Array.from(subscriptions, (close) => close()))));
return ((stream, options) => ({
[Symbol.asyncIterator]() {
const iterator = Stream.toAsyncIterableWith(stream, context)[Symbol.asyncIterator]();
const close = () => {
subscriptions.delete(close);
options?.signal?.removeEventListener("abort", abort);
return iterator.return?.() ?? Promise.resolve({ done: true, value: undefined });
};
const abort = () => {
void close();
};
subscriptions.add(close);
options?.signal?.addEventListener("abort", abort, { once: true });
if (options?.signal?.aborted)
abort();
return {
next: () => iterator.next().then((result) => (result.done ? close().then(() => result) : result), (error) => close().then(() => Promise.reject(error))),
return: close,
};
},
}));
});
const rpcFromEffect = Effect.fn("Plugin.Rpc.fromEffect")(function* (host, streams) {
const context = yield* Effect.context();
const run = Effect.runPromiseWith(context);
const client = (definition) => {
const local = host(definition);
const subscribe = (name, options) => streams(local.events.subscribe(name), options);
return Object.assign(Object.fromEntries(Object.keys(definition.methods).map((name) => [
name,
(input, options) => {
// SAFETY: The local client was built from this definition, so every declared key is an Effect method.
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion
const method = local[name];
return run(method(input), { signal: options?.signal });
},
])), {
events: {
subscribe,
on: (name, handler, options) => {
const controller = new AbortController();
const signal = options?.signal ? AbortSignal.any([controller.signal, options.signal]) : controller.signal;
void (async () => {
for await (const event of subscribe(name, { signal }))
await handler(event);
})().catch((error) => run(Effect.logError(error)));
return () => controller.abort();
},
},
});
};
const register = (definition, handlers) => run(host.register(definition,
// SAFETY: Each entry preserves its definition key; Core restores that method's erased schema and error types.
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion
Object.fromEntries(Object.entries(handlers).map(([name, handler]) => [
name,
(input, context) => Effect.tryPromise({
try: (signal) => {
// SAFETY: Promise RPC handlers return Promise values before this adapter erases their concrete types.
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion
return Reflect.apply(handler, undefined, [
input,
{
signal,
error: (type, message, data) => new ReturnedRpcError(type, message, data),
},
]);
},
catch: (error) => hostRpcError(context, error),
}).pipe(Effect.flatMap((result) => result instanceof ReturnedRpcError
? Effect.fail(hostRpcError(context, result))
: Effect.succeed(result))),
])))).then((registration) => ({
dispose: () => run(registration.dispose),
events: { emit: (...args) => run(registration.events.emit(...args)) },
}));
// SAFETY: Client and register implement RpcDomain from the same portable definitions and schema adapters.
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion
return Object.assign(client, { register });
});
function hostRpcError(context, error) {
if (!(error instanceof ReturnedRpcError))
return error;
return context.error(error.type, error.message, error.data);
}
function compileEndpoint(endpoint) {

@@ -56,3 +155,2 @@ const cached = compiledEndpoints.get(endpoint);

id: plugin.id,
tui: plugin.tui,
vcs: plugin.vcs,

@@ -77,2 +175,3 @@ effect: (host) => Effect.gen(function* () {

const context = yield* Effect.context();
const streams = yield* makeStreams();
// Run a hook registration on the plugin scope and resolve once it is registered.

@@ -136,3 +235,3 @@ const register = (effect) => Effect.runPromiseWith(context)(effect).then((registration) => ({

event: {
subscribe: () => Stream.toAsyncIterable(host.event.subscribe().pipe(Stream.mapEffect((event) => Schema.encodeUnknownEffect(OpenCodeEvent)(event)), Stream.map((event) => event))),
subscribe: (options) => streams(host.event.subscribe().pipe(Stream.mapEffect((event) => Schema.encodeUnknownEffect(OpenCodeEvent)(event)), Stream.map((event) => event)), options),
},

@@ -219,2 +318,3 @@ experimental: {

},
rpc: yield* rpcFromEffect(host.rpc, streams),
skill: {

@@ -221,0 +321,0 @@ list: adaptApiMethod(SkillEndpoints["skill.list"], host.skill.list),

@@ -15,4 +15,5 @@ export type { PluginOptions } from "../options.js";

export { Reference } from "@opencode-ai/schema/reference";
export { Rpc } from "@opencode-ai/schema/rpc";
export { Skill } from "@opencode-ai/schema/skill";
export { Vcs } from "@opencode-ai/schema/vcs";
export { WebSearch } from "@opencode-ai/schema/websearch";

@@ -13,4 +13,5 @@ export * as Plugin from "./plugin.js";

export { Reference } from "@opencode-ai/schema/reference";
export { Rpc } from "@opencode-ai/schema/rpc";
export { Skill } from "@opencode-ai/schema/skill";
export { Vcs } from "@opencode-ai/schema/vcs";
export { WebSearch } from "@opencode-ai/schema/websearch";

@@ -16,2 +16,3 @@ import type { OpenCodeClient } from "@opencode-ai/client";

import type { ReferenceDomain } from "./reference.js";
import type { RpcDomain } from "./rpc.js";
import type { SessionDomain } from "./session.js";

@@ -42,2 +43,3 @@ import type { ShellDomain } from "./shell.js";

readonly reference: ReferenceDomain;
readonly rpc: RpcDomain;
readonly session: SessionDomain;

@@ -54,3 +56,2 @@ readonly shell: ShellDomain;

readonly id: string;
readonly tui?: boolean;
readonly vcs?: VcsDiscovery;

@@ -57,0 +58,0 @@ readonly setup: (context: Context) => Promise<Cleanup | void> | Cleanup | void;

@@ -28,6 +28,9 @@ import type { AgentInfo, CommandInfo, FormCancelInput, FormInfo, FormReplyInput, IntegrationInfo, LocationRef, McpResource, McpServer, ModelInfo, OpenCodeClient, OpenCodeEvent, PermissionSavedInfo, PermissionRequest, Project, ProviderInfo, ReferenceInfo, SessionInfo, SessionMessageInfo, SessionInboxInfo, ShellInfo, SkillInfo, VcsInfo } from "@opencode-ai/client";

}
type OpenCodeEventMap = {
[Type in OpenCodeEvent["type"]]: Extract<OpenCodeEvent, {
type: Type;
}>;
};
export interface Data {
readonly on: <Type extends OpenCodeEvent["type"]>(type: Type, handler: (event: Extract<OpenCodeEvent, {
type: Type;
}>) => void) => () => void;
readonly on: <Type extends OpenCodeEvent["type"]>(type: Type, handler: (event: OpenCodeEventMap[Type]) => void) => () => void;
readonly listen: (handler: (event: {

@@ -34,0 +37,0 @@ details: OpenCodeEvent;

{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
"version": "0.0.0-dev-18706",
"version": "0.0.0-dev-18708",
"type": "module",

@@ -9,3 +9,3 @@ "license": "MIT",

"test": "bun test --timeout 5000",
"typecheck": "tsgo --noEmit",
"typecheck": "tsgo --noEmit -p tsconfig.tests.json",
"build": "tsc -p tsconfig.build.json"

@@ -36,6 +36,6 @@ },

"@ai-sdk/provider": "3.0.8",
"@opencode-ai/ai": "0.0.0-dev-18706",
"@opencode-ai/client": "0.0.0-dev-18706",
"@opencode-ai/protocol": "0.0.0-dev-18706",
"@opencode-ai/schema": "0.0.0-dev-18706",
"@opencode-ai/ai": "0.0.0-dev-18708",
"@opencode-ai/client": "0.0.0-dev-18708",
"@opencode-ai/protocol": "0.0.0-dev-18708",
"@opencode-ai/schema": "0.0.0-dev-18708",
"@standard-schema/spec": "1.1.0",

@@ -46,3 +46,3 @@ "effect": "4.0.0-rc.112",

"peerDependencies": {
"@opencode-ai/theme": "0.0.0-dev-18706",
"@opencode-ai/theme": "0.0.0-dev-18708",
"@opentui/core": ">=0.5.9",

@@ -67,3 +67,3 @@ "@opentui/solid": ">=0.5.9",

"devDependencies": {
"@opencode-ai/theme": "0.0.0-dev-18706",
"@opencode-ai/theme": "0.0.0-dev-18708",
"@opentui/core": "0.5.9",

@@ -70,0 +70,0 @@ "@opentui/solid": "0.5.9",