import http from 'http'; import { MajkInterface } from './majk-interface-types'; /** * Core types that plugins need * We define minimal versions here to avoid coupling */ export interface PluginLogger { debug(message: string, ...args: any[]): void; info(message: string, ...args: any[]): void; warn(message: string, ...args: any[]): void; error(message: string, ...args: any[]): void; } export interface PluginStorage { get(key: string): Promise; set(key: string, value: T): Promise; delete(key: string): Promise; clear(): Promise; keys(): Promise; } export interface ScopedTimers { setTimeout(callback: () => void, ms: number): NodeJS.Timeout; setInterval(callback: () => void, ms: number): NodeJS.Timeout; clearTimeout(timer: NodeJS.Timeout): void; clearInterval(timer: NodeJS.Timeout): void; } export interface ScopedIpcRegistry { handle(channel: string, handler: (...args: any[]) => Promise | any): void; removeHandler(channel: string): void; send(channel: string, ...args: any[]): void; } export interface CapabilityMetadata { pluginId: string; type: string; serviceName: string; metadata: Record; registeredAt: Date; } /** * Handle representing a callback registered for RPC invocation */ export interface CallbackHandle { /** Marker to identify this as a callback handle */ __isCallback: true; /** Unique identifier for this callback */ callbackId: string; /** RPC service name where callback is registered */ serviceName: string; } /** * Options for creating explicit callbacks with cleanup strategies */ export interface CreateCallbackOptions { /** Auto-cleanup after N invocations (callback self-destructs) */ maxCalls?: number; /** Auto-cleanup after N milliseconds */ timeout?: number; } /** * Statistics about callback usage */ export interface CallbackStats { /** Total number of active callbacks */ activeCallbacks: number; /** Number of inline (auto-wrapped) callbacks */ inlineCallbacks: number; /** Number of explicit callbacks */ explicitCallbacks: number; /** Total callbacks created since bridge creation */ totalCreated: number; /** Total callbacks cleaned up */ totalCleaned: number; } export interface PluginContext { pluginId: string; pluginRoot: string; dataDir: string; app: { version: string; name: string; appDataDir: string; }; http: { port?: number; secret?: string; baseUrl: string; }; resources?: ResourceProvider; majk: MajkInterface; ipc: ScopedIpcRegistry; timers: ScopedTimers; storage: PluginStorage; logger: PluginLogger; rpc: { registerService(serviceName: string, implementation: T): Promise; unregisterService(serviceName: string): Promise; createProxy any>>(serviceName: string, options?: { timeout?: number; }): Promise; /** * Create an explicit callback with cleanup strategies * @param callback - The callback function to register * @param options - Cleanup options (maxCalls, timeout) * @returns A handle that can be passed through RPC calls */ createCallback?(callback: (...args: TArgs) => TReturn | Promise, options?: CreateCallbackOptions): Promise; /** * Manually cleanup a callback registered via createCallback() * @param handle - The callback handle to cleanup */ cleanupCallback?(handle: CallbackHandle): void; /** * Get statistics about callback usage * @returns Current callback statistics */ getCallbackStats?(): CallbackStats; }; capabilities: { getProviders(type: string): Promise; }; } export interface PluginCapabilities { apiVersion: string; capabilities: PluginCapability[]; } export interface PluginCapability { type: string; [key: string]: any; } export interface ToolImplementation { handler: (input: any, context: any) => Promise; inputSchema?: any; } export interface InProcessPlugin { readonly id: string; readonly name: string; readonly version: string; getCapabilities?(): PluginCapabilities | Promise; createTool?(toolName: string, toolConfig: any): ToolImplementation | Promise; onLoad?(context: PluginContext): Promise; onUnload?(): Promise; onAppReady?(): Promise; isHealthy?(): Promise<{ healthy: boolean; errors?: string[]; warnings?: string[]; }> | { healthy: boolean; errors?: string[]; warnings?: string[]; }; } export interface PluginHealthStatus { healthy: boolean; errors?: string[]; warnings?: string[]; lastChecked?: Date; } export type ToolHandler = (input: TInput, ctx: { majk: any; storage: any; logger: any; }) => Promise | TOutput; export type ApiMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; export type Scope = 'global' | 'conversation' | 'teammate' | 'project'; export interface ToolSpec { name: string; description: string; inputSchema: Record; metadata?: Record; } export interface RequestLike { method: ApiMethod; url: string; pathname: string; query: URLSearchParams; params: Record; body: any; headers: http.IncomingHttpHeaders; } export interface ResponseLike { status(code: number): ResponseLike; json(data: any): void; send(data: string | Buffer): void; setHeader(key: string, value: string): void; } export type RouteHandler = (req: RequestLike, res: ResponseLike, ctx: { majk: any; storage: any; logger: any; http: any; }) => any | Promise; export interface JsonSchema { type?: 'object' | 'array' | 'string' | 'number' | 'boolean' | 'null'; properties?: Record; items?: JsonSchema; required?: string[]; additionalProperties?: boolean | JsonSchema; enum?: any[]; minimum?: number; maximum?: number; minLength?: number; maxLength?: number; pattern?: string; description?: string; default?: any; examples?: any[]; [key: string]: any; } export interface ApiRouteDef { method: ApiMethod; path: `/api/${string}`; name: string; description: string; handler: RouteHandler; requestSchema: { query?: JsonSchema; params?: JsonSchema; body?: JsonSchema; }; responseSchema?: JsonSchema; tags?: string[]; deprecated?: boolean; } export type FunctionHandler = (input: TInput, context: PluginContext) => Promise | TOutput; export type SubscriptionHandler = (input: TInput, context: PluginContext) => AsyncGenerator; export interface FunctionDefinition { name: string; description: string; input: JsonSchema; output: JsonSchema; handler: FunctionHandler; tags?: string[]; deprecated?: boolean; } export interface SubscriptionDefinition { name: string; description: string; input: JsonSchema; output: JsonSchema; handler: SubscriptionHandler; } export interface Transport { name: string; initialize(registry: FunctionRegistry, context: PluginContext): Promise; start(): Promise; stop(): Promise; getMetadata(): TransportMetadata; } export interface TransportMetadata { type: string; endpoint?: string; discovery?: string; [key: string]: any; } export interface ClientGenerationConfig { output: string; languages: ('typescript' | 'javascript')[]; includeReactHooks?: boolean; includeValidation?: boolean; } export interface FunctionRegistry { functions: Map; subscriptions: Map; plugin: { id: string; name: string; version: string; }; getFunctionNames(): string[]; getSubscriptionNames(): string[]; toOpenAPISpec(): any; } export interface ClientGenerationConfig { output: string; languages: ('typescript' | 'javascript')[]; includeReactHooks?: boolean; includeValidation?: boolean; } export type HistoryMode = 'browser' | 'hash'; export interface UiConfig { appDir?: string; base?: `/${string}`; history?: HistoryMode; } export interface ScreenBase { id: string; name: string; description: string; route: `/plugin-screens/${Id}/${string}`; pluginPath?: `/${string}`; pluginPathHash?: `#/${string}`; } export interface ReactScreen extends ScreenBase { /** * @deprecated Use pluginPath instead. Will be removed in v3.0.0 */ reactPath?: `/${string}`; /** * @deprecated Use pluginPathHash instead. Will be removed in v3.0.0 */ hash?: `#/${string}`; } export type HtmlScreen = ScreenBase & ({ html: string; } | { htmlFile: `${string}.html`; }); export interface NavPaneActionConfig { /** Unique identifier for this action */ id: string; /** Display label for the button */ label: string; /** Optional icon/emoji to display */ icon?: string; /** Event configuration - dispatched when button is clicked */ event?: { /** Event name (e.g., 'my-plugin:quick-action') */ name: string; /** Static payload to merge with runtime context */ payload?: Record; }; /** Route to navigate to after event is dispatched */ route?: string; /** Context types to collect and pass to event handler */ context?: Array<'conversation' | 'project' | 'teammate' | 'filter' | 'search'>; /** Display order (lower = higher priority, appears first) */ order?: number; /** HeroUI button color */ color?: 'primary' | 'success' | 'warning' | 'danger' | 'default'; /** HeroUI button variant */ variant?: 'flat' | 'solid' | 'bordered' | 'light'; /** Tooltip text shown on hover */ tooltip?: string; } export interface ConfigWizardDef { schema?: JsonSchema; storageKey?: string; /** * @deprecated Use pluginPath instead. Will be removed in v3.0.0 */ path?: `/${string}`; /** * @deprecated Use pluginPathHash instead. Will be removed in v3.0.0 */ hash?: `#/${string}`; pluginPath?: `/${string}`; pluginPathHash?: `#/${string}`; title: string; width?: number; height?: number; shouldShow?: (ctx: any) => boolean | Promise; description?: string; settings?: { /** * @deprecated Use pluginPath instead. Will be removed in v3.0.0 */ path?: `/${string}`; /** * @deprecated Use pluginPathHash instead. Will be removed in v3.0.0 */ hash?: `#/${string}`; pluginPath?: `/${string}`; pluginPathHash?: `#/${string}`; title: string; description?: string; }; } export interface SettingsDef { /** * @deprecated Use pluginPath instead. Will be removed in v3.0.0 */ path?: `/${string}`; /** * @deprecated Use pluginPathHash instead. Will be removed in v3.0.0 */ hash?: `#/${string}`; pluginPath?: `/${string}`; pluginPathHash?: `#/${string}`; title: string; description?: string; } export type EntityType = 'mcpServer' | 'agent' | 'teamMember' | 'tool' | 'project' | 'execution'; export interface MCPServerEntity { id: string; name: string; description?: string; type: 'local' | 'remote' | 'docker' | 'custom' | 'stdio'; connectionConfig: { url?: string; command?: string; args?: string[]; env?: Record; workingDirectory?: string; authType?: 'none' | 'apiKey' | 'oauth' | 'basic' | 'custom'; authConfig?: Record; }; } /** * Represents a service function binding for a teammate * Maps a teammate to specific plugin functions */ export interface ServiceFunctionBinding { pluginId: string; serviceName: string; functionName: string; } export interface TeamMemberEntity { id: string; name: string; systemPrompt: string; avatar?: string; title?: string; expertise: string[]; personality: { workStyle: 'methodical' | 'creative' | 'analytical' | 'pragmatic'; codeStyle: 'verbose' | 'concise' | 'documented' | 'minimalist'; reviewStyle: 'thorough' | 'pragmatic' | 'security-focused' | 'performance-focused'; communicationStyle: 'detailed' | 'concise' | 'technical' | 'explanatory'; }; skills: { primary: string[]; secondary: string[]; languages: string[]; frameworks: string[]; tools: string[]; }; metadata: { totalTasks: number; successfulTasks: number; projectHistory: any[]; codeStats: { linesWritten: number; filesModified: number; testsWritten: number; reviewsPassed: number; commitsCreated: number; }; }; mcpServerIds: string[]; /** * Functions available to this teammate * Simple mode: ["getCourses", "getCourseDetails"] - auto-populates pluginId and serviceName * Verbose mode: [{ serviceName: "...", functionName: "..." }] - pluginId auto-populated if omitted * Mixed mode: Can combine strings and objects in same array */ functions?: (string | ServiceFunctionBinding)[]; } export interface IdentityProviderInfo { name: string; website: string; documentationUrl?: string; } export interface IdentitySetupInstructions { title: string; steps: string[]; estimatedTime?: string; } export interface IdentityTestConfig { endpoint: string; method: string; headers?: Record; body?: any; successCriteria?: { statusCode?: number; bodyContains?: string[]; bodyMatch?: Record; }; } export interface IdentityDefinitionCapability extends PluginCapability { type: 'identity-definition'; identityType: string; version?: string; category: string; displayName: string; description: string; provider?: IdentityProviderInfo; tags?: string[]; configurationSchema: JsonSchema; usageSchema?: JsonSchema; setupInstructions?: IdentitySetupInstructions; testable?: boolean; testConfig?: IdentityTestConfig; } export interface IdentityRequirement { id: string; identityType: string; parameters?: Record; required: boolean; purpose?: string; description?: string; } export interface ServiceParameterMetadata { name: string; type: string; description: string; required: boolean; defaultValue?: any; } export interface ServiceReturnMetadata { type: string; description: string; } export interface ServiceFunctionMetadata { name: string; description: string; parameters: ServiceParameterMetadata[]; returns: ServiceReturnMetadata; examples?: string[]; tags?: string[]; deprecated?: boolean; identityRequirements?: IdentityRequirement[]; } export interface ServiceMetadata { name: string; description: string; version: string; category?: string; tags?: string[]; implements?: string; } export interface ServiceCapability extends PluginCapability { type: 'service'; serviceName: string; serviceType: string; discoverable?: boolean; metadata: ServiceMetadata; identityRequirements?: IdentityRequirement[]; functions: ServiceFunctionMetadata[]; implements?: string; } export interface FunctionEnrichment { examples?: string[]; tags?: string[]; deprecated?: boolean; identities?: IdentityRequirement[]; } export interface SecretScope { type: 'global' | 'project' | 'integration' | 'conversation' | 'user'; id?: string; } export interface SecretResolutionContext { conversationId?: string; projectId?: string; teamMemberId?: string; integrationId?: string; userId?: string; } export interface SecretResolutionResult { found: boolean; value?: string; scope?: string; source: string; metadata?: Record; } export interface SecretInfo { key: string; scope: SecretScope; description?: string; createdAt?: Date; updatedAt?: Date; tags?: string[]; } export interface SecretProviderInstance { resolve(key: string, scope?: SecretScope, context?: SecretResolutionContext): Promise; list?(scope?: SecretScope): Promise; has?(key: string, scope?: SecretScope): Promise; getInfo?(): Promise>; } export type SecretProviderFactory = (ctx: PluginContext) => SecretProviderInstance | Promise; export interface SecretProviderDef { name: string; priority?: number; scopes?: string[]; description?: string; icon?: string; tags?: string[]; factory: SecretProviderFactory; } export type CleanupFn = () => void; export type HealthCheckFn = (ctx: any) => Promise<{ healthy: boolean; details?: any; }> | { healthy: boolean; details?: any; }; /** * Context passed to skill setup/teardown functions */ export interface SkillContext { /** ID of the teammate enabling the skill */ teammateId: string; /** ID of the current conversation */ conversationId: string; /** Root directory of the project */ projectRootDir: string; /** Workspace directory for the project */ projectWorkspaceDir: string; } /** * Setup function signature - called when skill is enabled * Returns additional context/instructions to prepend to skill instructions */ export type SkillSetupFn = (context: SkillContext) => Promise; /** * Teardown function signature - called when skill is disabled */ export type SkillTeardownFn = (context: SkillContext) => Promise; /** * Reference to an RPC service function. * Used by plugins to declare which service function to call for setup/teardown/instructions. */ export interface SkillServiceFunctionRef { /** Name of the RPC service (as registered via context.rpc.registerService) */ serviceName: string; /** Name of the function to call on that service */ functionName: string; } /** * Skill capability - allows plugins to contribute skills via RPC service proxies. * * This enables plugins to provide skills without bundling code into the main process. * Setup/teardown functions are invoked via RPC, and instructions can be static or dynamic. * * Memory-only registration (not persisted to database). * * @example * ```typescript * // In plugin's getCapabilities() or via .skill() builder method * { * type: 'skill', * id: 'code-review-mode', * name: 'Code Review Mode', * description: 'Enables thorough code review', * instructions: 'You are now in Code Review Mode...', * setupRef: { serviceName: 'CodeReviewService', functionName: 'prepare' }, * tags: ['review', 'code-quality'] * } * ``` */ export interface SkillCapability extends PluginCapability { /** Must be 'skill' */ type: 'skill'; /** Unique identifier for the skill */ id: string; /** Display name for the skill */ name: string; /** One-sentence description of what the skill does */ description: string; /** * Static instructions string. * If instructionsRef is also provided, instructionsRef takes precedence. */ instructions?: string; /** * Reference to an RPC function that returns instructions dynamically. * Function signature: () => Promise * Takes precedence over static instructions if both provided. */ instructionsRef?: SkillServiceFunctionRef; /** * Reference to an RPC function to call when skill is enabled. * Function signature: (ctx: SkillContext) => Promise * Returns additional instructions to prepend, or void. */ setupRef?: SkillServiceFunctionRef; /** * Reference to an RPC function to call when skill is disabled. * Function signature: (ctx: SkillContext) => Promise */ teardownRef?: SkillServiceFunctionRef; /** Optional tags for categorization and search */ tags?: string[]; /** Optional list of tool names this skill uses */ tools?: string[]; /** Initial enabled state (default: true) */ enabled?: boolean; /** Optional metadata for discovery */ metadata?: Record; } export type MenuItemType = 'action' | 'divider' | 'header' | 'custom'; export interface MenuBadge { label: string; variant?: 'info' | 'success' | 'warning' | 'error'; } export interface MenuRightElement { type: 'chip' | 'indicator' | 'stat'; label?: string; value?: string; variant?: 'info' | 'success' | 'warning' | 'error' | 'primary'; count?: number; status?: 'success' | 'warning' | 'error' | 'info'; } export interface MenuItemStyle { color?: 'primary' | 'success' | 'warning' | 'error' | 'info'; weight?: 'normal' | 'bold'; size?: 'sm' | 'md' | 'lg'; } export interface MenuItemMetadata { sortPriority?: number; group?: string; tags?: string[]; [key: string]: any; } export interface MenuItemDeclaration { path: string; type?: MenuItemType; label: string; icon?: string; route?: string; description?: string; badge?: MenuBadge; rightElement?: MenuRightElement; shortcut?: string; disabled?: boolean; style?: MenuItemStyle; component?: string; metadata?: MenuItemMetadata; } /** * Function information for listing */ export interface FunctionInfo { name: string; description: string; tags?: string[]; deprecated?: boolean; } /** * Function provider interface - all methods async * Decouples MAJK from plugin internals */ export interface FunctionProvider { /** * List all available functions */ listFunctions(): Promise; /** * Check if function exists */ hasFunction(name: string): Promise; /** * Invoke a function with input * @throws Error if function doesn't exist */ invokeFunction(name: string, input: unknown): Promise; /** * Get JSON schema for a function */ getFunctionSchema(name: string): Promise; /** * Get OpenAPI spec for all functions */ getOpenAPISpec(): Promise; } /** * Plugin health status */ export interface AsyncPluginHealthStatus { healthy: boolean; error?: string; checks?: { [key: string]: { status: 'healthy' | 'unhealthy' | 'degraded'; message?: string; }; }; } /** * Main plugin interface - all async (replaces InProcessPlugin for new architecture) */ export interface Plugin { readonly id: string; readonly name: string; readonly version: string; /** * Get function provider (lazy initialization) * Returns null if plugin has no functions */ getFunctions(): Promise; /** * Health check */ isHealthy(): Promise; } /** * Resource provider - abstract plugin asset access * Replaces direct filesystem access (pluginRoot, pluginCodeDir) */ export interface ResourceProvider { /** * Read asset as binary buffer */ readAsset(relativePath: string): Promise; /** * Read asset as text */ readAssetAsText(relativePath: string): Promise; /** * Check if asset exists */ assetExists(relativePath: string): Promise; /** * List assets matching a glob pattern */ listAssets(pattern: string): Promise; /** * Get absolute path for an asset (for backward compatibility) * Returns null if asset doesn't exist */ getAssetPath(relativePath: string): Promise; } /** * Parsed protocol request from a majk:// URL. * Passed to handler functions. */ export interface ProtocolRequest { /** Original raw URL string */ rawUrl: string; /** Namespace extracted from URL (e.g., "github" from majk://github/...) */ namespace: string; /** Path after namespace (e.g., "/repos/open" from majk://github/repos/open) */ path: string; /** Query parameters */ query: Record; /** Fragment (hash) if present */ fragment?: string; } /** * Response from a protocol handler */ export interface ProtocolResponse { /** Whether the request was handled successfully */ success: boolean; /** Optional result data */ data?: unknown; /** Error message if not successful */ error?: string; /** Optional action to take (e.g., navigate, show notification) */ action?: ProtocolAction; } /** * Action to perform after handling a protocol URL */ export type ProtocolAction = { type: 'navigate'; route: string; } | { type: 'notification'; title: string; body: string; level?: 'info' | 'warning' | 'error'; } | { type: 'none'; }; /** * Pattern handler definition within a protocol capability. * * @example * { pattern: '/auth/callback', rpcMethod: 'handleOAuthCallback' } * { pattern: '/repo/:owner/:repo', rpcMethod: 'openRepository', description: 'Opens a repository' } */ export interface ProtocolHandlerDef { /** URL pattern to match (e.g., "/repos/:owner/:repo", "/callback/*") */ pattern: string; /** Function name on the plugin's RPC service to call */ rpcMethod: string; /** Optional description for documentation */ description?: string; } /** * Protocol capability declared by a plugin. * Enables handling majk:// URLs for deep linking. * * @example * ```typescript * definePlugin('github-plugin', 'GitHub Plugin', '1.0.0') * .protocol({ * namespace: 'github', * handlers: [ * { pattern: '/auth/callback', rpcMethod: 'handleOAuthCallback' }, * { pattern: '/repo/:owner/:repo', rpcMethod: 'openRepository' } * ] * }) * .function('handleOAuthCallback', { * description: 'Handle OAuth callback', * input: { type: 'object', properties: { request: {}, pathParams: {} } }, * output: { type: 'object' }, * handler: async ({ request, pathParams }, ctx) => { * // Handle the protocol URL * return { success: true }; * } * }) * .build() * ``` */ export interface ProtocolCapability extends PluginCapability { type: 'protocol'; /** Namespace for URL routing (e.g., "github" matches majk://github/...) */ namespace: string; /** * RPC service name (plugin's package name). * Auto-populated from plugin's packageName if not specified. * Example: "@majkapp/github-plugin" */ rpcService: string; /** Handler definitions for different URL patterns */ handlers: ProtocolHandlerDef[]; } /** * Base filters shared by all interceptor types */ export interface BaseInterceptorFilters { /** Limit to specific conversation IDs */ conversationIds?: string[]; /** Limit to specific agent IDs */ agentIds?: string[]; /** Limit to specific team member IDs */ teamMemberIds?: string[]; /** Limit to specific project IDs */ projectIds?: string[]; } /** * Filters specific to message interceptors */ export interface MessageInterceptorFilters extends BaseInterceptorFilters { /** Filter by message types */ messageTypes?: Array<'user' | 'assistant' | 'tool_result' | 'tool_use'>; } /** * Filters specific to tool interceptors */ export interface ToolInterceptorFilters extends BaseInterceptorFilters { /** Filter by tool names (supports wildcards like "read_*") */ toolNames?: string[]; /** Filter by regex pattern (as string, converted to RegExp by handler) */ toolPattern?: string; } /** * Configuration for a message interceptor */ export interface MessageInterceptorConfig { /** Unique identifier within plugin */ id: string; /** When to intercept: pre-send (before user message), post-receive (after tool result), post-assistant (after assistant response) */ phase: 'pre-send' | 'post-receive' | 'post-assistant'; /** Function name in plugin's global service to handle interception */ handler: string; /** Priority (higher = earlier, default: 50) */ priority?: number; /** Timeout in milliseconds (default: 5000) */ timeout?: number; /** Filters to limit when interceptor runs */ filters?: MessageInterceptorFilters; /** Optional metadata */ metadata?: { description?: string; tags?: string[]; enabled?: boolean; }; } /** * Configuration for a tool interceptor */ export interface ToolInterceptorConfig { /** Unique identifier within plugin */ id: string; /** When to intercept: pre-call (before tool execution), post-result (after tool execution) */ phase: 'pre-call' | 'post-result'; /** Function name in plugin's global service to handle interception */ handler: string; /** Priority (higher = earlier, default: 50) */ priority?: number; /** Timeout in milliseconds (default: 5000) */ timeout?: number; /** Filters to limit when interceptor runs */ filters?: ToolInterceptorFilters; /** Optional metadata */ metadata?: { description?: string; tags?: string[]; enabled?: boolean; }; } /** * Interceptor declaration (internal representation) */ export interface InterceptorDeclaration { id: string; interceptorType: 'message' | 'tool'; phase: string; handlerFunction: string; priority?: number; timeout?: number; filters?: BaseInterceptorFilters & { messageTypes?: Array<'user' | 'assistant' | 'tool_result' | 'tool_use'>; toolNames?: string[]; toolPattern?: string; }; metadata?: { description?: string; tags?: string[]; enabled?: boolean; }; } /** * Interceptor capability - allows plugins to intercept and modify messages or tool calls */ export interface InterceptorCapability extends PluginCapability { type: 'interceptor'; discoverable?: boolean; metadata: { name: string; description: string; version: string; }; interceptors: InterceptorDeclaration[]; } //# sourceMappingURL=types.d.ts.map