Collapsed the Even plugin architecture into a single unified plugin by merging board card routes and replacing the transport stub with a real webhook implementation, removing the separate cards plugin from the workspace. The new unified plugin (`fusion-plugin-even-realities-glasses`) now exports con Fusion-Task-Id: FN-3970
161 lines
5.3 KiB
TypeScript
161 lines
5.3 KiB
TypeScript
import { definePlugin } from "@fusion/plugin-sdk";
|
|
import type { FusionPlugin, PluginContext, PluginRouteDefinition, PluginRouteResponse } from "@fusion/plugin-sdk";
|
|
import { createNotifier } from "./notifier.js";
|
|
import { requestReview, startWork } from "./agent-actions.js";
|
|
import { runQuickCapture } from "./quick-capture.js";
|
|
import { quickCaptureRoutes } from "./routes/quick-capture-routes.js";
|
|
import { createNotificationRoutes } from "./routes/notification-routes.js";
|
|
import { agentActionRoutes } from "./routes/agent-action-routes.js";
|
|
import { boardRoutes } from "./routes/board-routes.js";
|
|
import {
|
|
getCompanionWebhookUrl,
|
|
getFusionToken,
|
|
getNotifyColumns,
|
|
getQuickCaptureColumn,
|
|
settingsSchema,
|
|
} from "./settings.js";
|
|
import { WebhookGlassesTransport } from "./transport.js";
|
|
import { createTransportRoutes } from "./routes/transport-routes.js";
|
|
|
|
export type PluginDb = {
|
|
exec(sql: string): void;
|
|
prepare(sql: string): {
|
|
get(...args: unknown[]): unknown;
|
|
all(...args: unknown[]): unknown;
|
|
run(...args: unknown[]): unknown;
|
|
};
|
|
};
|
|
|
|
type PluginInstance = {
|
|
transport: WebhookGlassesTransport;
|
|
notifier: ReturnType<typeof createNotifier>;
|
|
};const instances = new Map<string, PluginInstance>();
|
|
|
|
function getDbFromTaskStore(ctx: PluginContext): PluginDb {
|
|
const pluginStore = ctx.taskStore.getPluginStore();
|
|
const db = (pluginStore as unknown as { db?: PluginDb }).db;
|
|
if (!db) throw new Error("Plugin database unavailable");
|
|
return db;
|
|
}
|
|
|
|
function getInstanceOrResponse(ctx: PluginContext): { instance?: PluginInstance; error?: PluginRouteResponse } {
|
|
const instance = instances.get(ctx.pluginId);
|
|
if (!instance) return { error: { status: 503, body: { error: "Plugin instance not initialized" } } };
|
|
return { instance };
|
|
}
|
|
|
|
const coreRoutes: PluginRouteDefinition[] = [
|
|
{
|
|
method: "GET",
|
|
path: "/status",
|
|
handler: async (_req, ctx) => {
|
|
const { instance, error } = getInstanceOrResponse(ctx);
|
|
if (!instance) return error as PluginRouteResponse;
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
connected: instance.transport.connected,
|
|
transport: instance.transport.status,
|
|
lastPollTime: instance.notifier.lastPolledAt() ?? null,
|
|
notifyOnColumns: getNotifyColumns(ctx.settings),
|
|
},
|
|
};
|
|
},
|
|
},
|
|
{
|
|
method: "POST",
|
|
path: "/reconnect",
|
|
handler: async (_req, ctx) => {
|
|
const { instance, error } = getInstanceOrResponse(ctx);
|
|
if (!instance) return error as PluginRouteResponse;
|
|
await instance.transport.disconnect();
|
|
await instance.transport.connect();
|
|
return { status: 200, body: { ok: true } };
|
|
},
|
|
},
|
|
];
|
|
|
|
const notificationRoutes = createNotificationRoutes((ctx) => instances.get(ctx.pluginId)?.notifier);
|
|
const transportRoutes = createTransportRoutes((ctx) => instances.get(ctx.pluginId)?.transport);
|
|
|
|
const plugin: FusionPlugin = definePlugin({
|
|
manifest: {
|
|
id: "fusion-plugin-even-realities-glasses",
|
|
name: "Even Realities Glasses",
|
|
version: "0.1.0",
|
|
description: "Task-focused card bridge between Fusion and Even Realities glasses.",
|
|
author: "Fusion Team",
|
|
fusionVersion: ">=0.1.0",
|
|
settingsSchema,
|
|
},
|
|
state: "installed",
|
|
routes: [...coreRoutes, ...boardRoutes, ...quickCaptureRoutes, ...agentActionRoutes, ...notificationRoutes, ...transportRoutes],
|
|
hooks: {
|
|
onSchemaInit: (db) => {
|
|
(db as PluginDb).exec(`
|
|
CREATE TABLE IF NOT EXISTS even_realities_seen_tasks (
|
|
taskId TEXT PRIMARY KEY,
|
|
lastColumn TEXT NOT NULL,
|
|
updatedAt TEXT NOT NULL
|
|
)
|
|
`);
|
|
},
|
|
onLoad: async (ctx) => {
|
|
const token = getFusionToken(ctx.settings);
|
|
if (!token) {
|
|
ctx.logger.warn("fusionApiToken is missing; even-realities plugin not initialized");
|
|
return;
|
|
}
|
|
const db = getDbFromTaskStore(ctx);
|
|
const transport = new WebhookGlassesTransport({
|
|
companionWebhookUrl: getCompanionWebhookUrl(ctx.settings),
|
|
});
|
|
await transport.connect();
|
|
transport.onAction(async (action) => {
|
|
if (action.type === "quick-capture") {
|
|
await runQuickCapture(
|
|
{ text: action.text, column: undefined },
|
|
{
|
|
taskStore: ctx.taskStore,
|
|
pluginId: ctx.pluginId,
|
|
defaultColumn: getQuickCaptureColumn(ctx.settings),
|
|
},
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (!action.taskId) return;
|
|
|
|
if (action.type === "start-work") {
|
|
await startWork({ taskId: action.taskId }, { taskStore: ctx.taskStore, pluginId: ctx.pluginId });
|
|
return;
|
|
}
|
|
|
|
if (action.type === "request-review") {
|
|
await requestReview({ taskId: action.taskId }, { taskStore: ctx.taskStore, pluginId: ctx.pluginId });
|
|
}
|
|
});
|
|
|
|
const notifier = createNotifier({
|
|
taskStore: ctx.taskStore,
|
|
db,
|
|
transport,
|
|
settings: ctx.settings,
|
|
logger: ctx.logger,
|
|
pluginId: ctx.pluginId,
|
|
});
|
|
notifier.start();
|
|
instances.set(ctx.pluginId, { transport, notifier });
|
|
},
|
|
onUnload: async () => {
|
|
for (const [pluginId, instance] of instances.entries()) {
|
|
await instance.notifier.stop();
|
|
await instance.transport.disconnect();
|
|
instances.delete(pluginId);
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
export default plugin;
|