@stina/extension-api 0.18.0 → 0.19.1
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.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime.cjs +17 -10
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.js +17 -10
- package/dist/runtime.js.map +1 -1
- package/dist/{types-kRt2DzdL.d.cts → types-CvfONPis.d.cts} +2 -1
- package/dist/{types-kRt2DzdL.d.ts → types-CvfONPis.d.ts} +2 -1
- package/package.json +1 -1
- package/src/runtime.ts +23 -12
- package/src/types.ts +2 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/messages.ts"],"sourcesContent":["/**\n * @stina/extension-api\n *\n * Types and utilities for building Stina extensions.\n *\n * Extensions should import from this package for type definitions.\n * The runtime (worker-side code) should import from '@stina/extension-api/runtime'.\n */\n\n// Localization\nexport type { LocalizedString } from './types.js'\nexport { resolveLocalizedString } from './types.js'\n\n// Types\nexport type {\n // Manifest\n ExtensionManifest,\n Platform,\n ExtensionContributions,\n SettingDefinition,\n SettingOptionsMapping,\n SettingCreateMapping,\n ToolSettingsViewDefinition,\n ToolSettingsView,\n ToolSettingsListView,\n ToolSettingsListMapping,\n ToolSettingsComponentView,\n ToolSettingsActionDataSource,\n PanelDefinition,\n PanelView,\n PanelComponentView,\n PanelActionDataSource,\n PanelUnknownView,\n ProviderDefinition,\n PromptContribution,\n PromptSection,\n ToolDefinition,\n CommandDefinition,\n\n // Provider Configuration Schema\n ProviderConfigSchema,\n ProviderConfigProperty,\n ProviderConfigPropertyType,\n ProviderConfigSelectOption,\n ProviderConfigValidation,\n\n // Permissions\n Permission,\n NetworkPermission,\n StoragePermission,\n UserDataPermission,\n CapabilityPermission,\n SystemPermission,\n\n // Context\n ExtensionContext,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerSchedule,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n\n // AI Provider\n AIProvider,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolCall,\n\n // Tools\n Tool,\n ToolResult,\n\n // Actions\n Action,\n ActionResult,\n\n // Entry point\n ExtensionModule,\n} from './types.js'\n\n// Messages (for host implementation)\nexport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n ActivateMessage,\n DeactivateMessage,\n SettingsChangedMessage,\n ProviderChatRequestMessage,\n ProviderModelsRequestMessage,\n ToolExecuteRequestMessage,\n ToolExecuteResponseMessage,\n ActionExecuteRequestMessage,\n ActionExecuteResponseMessage,\n ResponseMessage,\n ReadyMessage,\n RequestMessage,\n RequestMethod,\n ProviderRegisteredMessage,\n ToolRegisteredMessage,\n ActionRegisteredMessage,\n StreamEventMessage,\n LogMessage,\n PendingRequest,\n} from './messages.js'\n\nexport { generateMessageId } from './messages.js'\n\n// Component types (for extension UI components)\nexport type {\n // Styling\n AllowedCSSProperty,\n ExtensionComponentStyle,\n // Base types\n ExtensionComponentData,\n // Iteration & Children\n ExtensionComponentIterator,\n ExtensionComponentChildren,\n // Actions\n ExtensionActionCall,\n ExtensionActionRef,\n // Data Sources & Panel Definition\n ExtensionDataSource,\n ExtensionPanelDefinition,\n // Component Props\n HeaderProps,\n LabelProps,\n ParagraphProps,\n ButtonProps,\n TextInputProps,\n DateTimeInputProps,\n SelectProps,\n VerticalStackProps,\n HorizontalStackProps,\n GridProps,\n DividerProps,\n IconProps,\n IconButtonType,\n IconButtonProps,\n PanelAction,\n PanelProps,\n ToggleProps,\n CollapsibleProps,\n PillVariant,\n PillProps,\n CheckboxProps,\n MarkdownProps,\n ModalProps,\n} from './types.components.js'\n","/**\n * A string that can be either a simple string or a map of language codes to localized strings.\n * When a simple string is provided, it's used as the default/fallback value.\n * When a map is provided, the appropriate language is selected at runtime.\n *\n * @example\n * // Simple string (backwards compatible)\n * name: \"Get Weather\"\n *\n * @example\n * // Localized strings\n * name: { en: \"Get Weather\", sv: \"Hämta väder\", de: \"Wetter abrufen\" }\n */\nexport type LocalizedString = string | Record<string, string>\n\n/**\n * Resolves a LocalizedString to an actual string value.\n * @param value The LocalizedString to resolve\n * @param lang The preferred language code (e.g., \"sv\", \"en\")\n * @param fallbackLang The fallback language code (defaults to \"en\")\n * @returns The resolved string value\n */\nexport function resolveLocalizedString(\n value: LocalizedString,\n lang: string,\n fallbackLang = 'en'\n): string {\n if (typeof value === 'string') {\n return value\n }\n // Try preferred language first, then fallback language, then first available, then empty string\n return value[lang] ?? value[fallbackLang] ?? Object.values(value)[0] ?? ''\n}\n\n/**\n * Extension manifest format (manifest.json)\n */\nexport interface ExtensionManifest {\n /** Unique identifier (e.g., \"ollama-provider\") */\n id: string\n /** Human-readable name */\n name: string\n /** Version string (semver) */\n version: string\n /** Short description */\n description: string\n /** Author information */\n author: {\n name: string\n url?: string\n }\n /** Repository URL */\n repository?: string\n /** License identifier */\n license?: string\n /** Minimum Stina version required */\n engines?: {\n stina: string\n }\n /** Supported platforms */\n platforms?: Platform[]\n /** Entry point file (relative to extension root) */\n main: string\n /** Required permissions */\n permissions: Permission[]\n /** What the extension contributes */\n contributes?: ExtensionContributions\n}\n\nexport type Platform = 'web' | 'electron' | 'tui'\n\n/**\n * What an extension can contribute to Stina\n */\nexport interface ExtensionContributions {\n /** User-configurable settings */\n settings?: SettingDefinition[]\n /** Tool settings views for UI */\n toolSettings?: ToolSettingsViewDefinition[]\n /** Right panel contributions */\n panels?: PanelDefinition[]\n /** AI providers */\n providers?: ProviderDefinition[]\n /** Tools for Stina to use */\n tools?: ToolDefinition[]\n /** Slash commands */\n commands?: CommandDefinition[]\n /** Prompt contributions for the system prompt */\n prompts?: PromptContribution[]\n}\n\n/**\n * Setting definition for the UI\n */\nexport interface SettingDefinition {\n /** Setting ID (namespaced automatically) */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** Setting type */\n type: 'string' | 'number' | 'boolean' | 'select'\n /** Default value */\n default?: unknown\n /** For select type: available options */\n options?: { value: string; label: string }[]\n /** For select type: load options from tool */\n optionsToolId?: string\n /** Params for options tool */\n optionsParams?: Record<string, unknown>\n /** Mapping for options tool response */\n optionsMapping?: SettingOptionsMapping\n /** Tool ID for creating a new option */\n createToolId?: string\n /** Label for create action */\n createLabel?: string\n /** Fields for create form */\n createFields?: SettingDefinition[]\n /** Static params always sent to create tool */\n createParams?: Record<string, unknown>\n /** Mapping for create tool response */\n createMapping?: SettingCreateMapping\n /** Validation rules */\n validation?: {\n required?: boolean\n min?: number\n max?: number\n pattern?: string\n }\n}\n\n/**\n * Mapping for select field options from tool response\n */\nexport interface SettingOptionsMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for option value */\n valueKey: string\n /** Key for option label */\n labelKey: string\n /** Optional key for description */\n descriptionKey?: string\n}\n\n/**\n * Mapping for create tool response\n */\nexport interface SettingCreateMapping {\n /** Key for result data object */\n resultKey?: string\n /** Key for option value (defaults to \"id\") */\n valueKey: string\n}\n\n/**\n * Tool settings view definition (UI schema)\n */\nexport interface ToolSettingsViewDefinition {\n /** Unique view ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** View configuration */\n view: ToolSettingsView\n /** Fields for create/edit forms (uses SettingDefinition) */\n fields?: SettingDefinition[]\n}\n\n/**\n * Tool settings view types\n */\nexport type ToolSettingsView = ToolSettingsListView | ToolSettingsComponentView\n\n/**\n * List view backed by tools\n */\nexport interface ToolSettingsListView {\n /** View kind */\n kind: 'list'\n /** Tool ID for listing items */\n listToolId: string\n /** Tool ID for fetching details (optional) */\n getToolId?: string\n /** Tool ID for creating/updating items (optional) */\n upsertToolId?: string\n /** Tool ID for deleting items (optional) */\n deleteToolId?: string\n /** Mapping from tool data to UI fields */\n mapping: ToolSettingsListMapping\n /** Param name for search query (default: \"query\") */\n searchParam?: string\n /** Param name for limit (default: \"limit\") */\n limitParam?: string\n /** Param name for get/delete ID (default: \"id\") */\n idParam?: string\n /** Static params always sent to list tool */\n listParams?: Record<string, unknown>\n}\n\n/**\n * Mapping from tool list data to UI fields\n */\nexport interface ToolSettingsListMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for total count in tool result data */\n countKey?: string\n /** Key for item ID */\n idKey: string\n /** Key for item label */\n labelKey: string\n /** Key for item description */\n descriptionKey?: string\n /** Key for secondary label */\n secondaryKey?: string\n}\n\n/**\n * Component-based tool settings view using the declarative DSL.\n * Reuses the same structure as PanelComponentView for consistency.\n */\nexport interface ToolSettingsComponentView {\n /** View kind */\n kind: 'component'\n /** Data sources. Keys become scope variables (e.g., \"$settings\"). */\n data?: Record<string, ToolSettingsActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Action-based data source for tool settings.\n */\nexport interface ToolSettingsActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that trigger refresh */\n refreshOn?: string[]\n}\n\n/**\n * Panel definition for right panel views\n */\nexport interface PanelDefinition {\n /** Unique panel ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Icon name (from huge-icons) */\n icon?: string\n /** Panel view schema */\n view: PanelView\n}\n\n/**\n * Panel view schema (declarative)\n */\nexport type PanelView = PanelComponentView | PanelUnknownView\n\nexport interface PanelUnknownView {\n /** View kind */\n kind: string\n /** Additional view configuration */\n [key: string]: unknown\n}\n\n/**\n * Action-based data source for declarative panels.\n * Uses actions (not tools) to fetch data.\n */\nexport interface PanelActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that should trigger a refresh of this data */\n refreshOn?: string[]\n}\n\n/**\n * Component-based panel view using the declarative DSL.\n * Data is fetched via actions, content is rendered via ExtensionComponent.\n */\nexport interface PanelComponentView {\n kind: 'component'\n /** Data sources available in the panel. Keys become variable names (e.g., \"$projects\"). */\n data?: Record<string, PanelActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Provider definition (metadata only, implementation in code)\n */\nexport interface ProviderDefinition {\n /** Provider ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Suggested default model when creating a new model configuration */\n suggestedDefaultModel?: string\n /** Default settings for this provider (e.g., { url: \"http://localhost:11434\" }) */\n defaultSettings?: Record<string, unknown>\n /** Schema for provider-specific configuration UI */\n configSchema?: ProviderConfigSchema\n}\n\n// ============================================================================\n// Prompt Contributions\n// ============================================================================\n\nexport type PromptSection = 'system' | 'behavior' | 'tools'\n\nexport interface PromptContribution {\n /** Unique ID within the extension */\n id: string\n /** Optional title for the prompt chunk */\n title?: string\n /** Prompt section placement */\n section?: PromptSection\n /** Plain text prompt content */\n text?: string\n /** Optional localized prompt content (keyed by locale, e.g. \"en\", \"sv\") */\n i18n?: Record<string, string>\n /** Optional ordering hint (lower comes first) */\n order?: number\n}\n\n// ============================================================================\n// Provider Configuration Schema\n// ============================================================================\n\n/**\n * Schema for provider-specific configuration.\n * Used to generate UI forms for configuring provider settings.\n */\nexport interface ProviderConfigSchema {\n /** Property definitions */\n properties: Record<string, ProviderConfigProperty>\n /** Display order of properties in UI (optional, defaults to object key order) */\n order?: string[]\n}\n\n/**\n * Property types for provider configuration\n */\nexport type ProviderConfigPropertyType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'password'\n | 'url'\n\n/**\n * Single property in a provider configuration schema.\n * Defines how a setting should be rendered and validated in the UI.\n */\nexport interface ProviderConfigProperty {\n /** Property type - determines UI control */\n type: ProviderConfigPropertyType\n /** Display label */\n title: string\n /** Help text shown below the input */\n description?: string\n /** Default value */\n default?: unknown\n /** Whether the field is required */\n required?: boolean\n /** Placeholder text for input fields */\n placeholder?: string\n /** For 'select' type: static options */\n options?: ProviderConfigSelectOption[]\n /** Validation rules */\n validation?: ProviderConfigValidation\n}\n\n/**\n * Option for select-type properties\n */\nexport interface ProviderConfigSelectOption {\n /** Value stored in settings */\n value: string\n /** Display label */\n label: string\n}\n\n/**\n * Validation rules for a property\n */\nexport interface ProviderConfigValidation {\n /** Regex pattern the value must match */\n pattern?: string\n /** Minimum string length */\n minLength?: number\n /** Maximum string length */\n maxLength?: number\n /** Minimum number value */\n min?: number\n /** Maximum number value */\n max?: number\n}\n\n/**\n * Tool definition (metadata only, implementation in code)\n */\nexport interface ToolDefinition {\n /** Tool ID */\n id: string\n /**\n * Display name - can be a simple string or localized strings.\n * @example \"Get Weather\"\n * @example { en: \"Get Weather\", sv: \"Hämta väder\" }\n */\n name: LocalizedString\n /**\n * Description for Stina - can be a simple string or localized strings.\n * Note: The AI always receives the English description (or fallback) for consistency.\n * Localized descriptions are used for UI display only.\n * @example \"Fetches current weather for a location\"\n * @example { en: \"Fetches current weather\", sv: \"Hämtar aktuellt väder\" }\n */\n description: LocalizedString\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Command definition\n */\nexport interface CommandDefinition {\n /** Command ID (e.g., \"weather\" for /weather) */\n id: string\n /** Display name */\n name: string\n /** Description */\n description: string\n}\n\n// ============================================================================\n// Permissions\n// ============================================================================\n\nexport type Permission =\n | NetworkPermission\n | StoragePermission\n | UserDataPermission\n | CapabilityPermission\n | SystemPermission\n\n/** Network access permissions */\nexport type NetworkPermission =\n | 'network:*'\n | `network:localhost`\n | `network:localhost:${number}`\n | `network:${string}`\n\n/** Storage permissions */\nexport type StoragePermission = 'database.own' | 'storage.local'\n\n/** User data permissions */\nexport type UserDataPermission =\n | 'user.profile.read'\n | 'user.location.read'\n | 'chat.history.read'\n | 'chat.current.read'\n\n/** Capability permissions */\nexport type CapabilityPermission =\n | 'provider.register'\n | 'tools.register'\n | 'actions.register'\n | 'settings.register'\n | 'commands.register'\n | 'panels.register'\n | 'events.emit'\n | 'scheduler.register'\n | 'chat.message.write'\n\n/** System permissions */\nexport type SystemPermission =\n | 'files.read'\n | 'files.write'\n | 'clipboard.read'\n | 'clipboard.write'\n\n// ============================================================================\n// Extension Context (API available to extensions)\n// ============================================================================\n\n/**\n * Disposable resource that can be cleaned up\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * Context provided to extension's activate function.\n *\n * ## User ID Context\n *\n * The `userId` field provides the current user context for extensions. It is set when:\n * - A tool is executed by a user\n * - An action is executed by a user\n * - A scheduled job fires (if the job was created with a userId)\n *\n * For extension activation, `userId` is undefined since activation happens at system level.\n * Extensions should check for `userId` in their tool/action handlers to access user-specific data.\n *\n * @example\n * ```typescript\n * // In a tool execute handler:\n * execute: async (params, context) => {\n * if (context.userId) {\n * // User-specific logic\n * const userData = await storage.getForUser(context.userId, 'preferences')\n * }\n * }\n * ```\n */\nexport interface ExtensionContext {\n /** Extension metadata */\n readonly extension: {\n readonly id: string\n readonly version: string\n readonly storagePath: string\n }\n\n /**\n * Current user ID if in a user context, undefined for global/system operations.\n * Set when tools or actions are executed by a user, or when a user-scoped job fires.\n */\n readonly userId?: string\n\n /** Network access (if permitted) */\n readonly network?: NetworkAPI\n\n /** Settings access (if permitted) */\n readonly settings?: SettingsAPI\n\n /** Provider registration (if permitted) */\n readonly providers?: ProvidersAPI\n\n /** Tool registration (if permitted) */\n readonly tools?: ToolsAPI\n\n /** Action registration (if permitted) */\n readonly actions?: ActionsAPI\n\n /** Event emission (if permitted) */\n readonly events?: EventsAPI\n\n /** Scheduler access (if permitted) */\n readonly scheduler?: SchedulerAPI\n\n /** User data access (if permitted) */\n readonly user?: UserAPI\n\n /** Chat access (if permitted) */\n readonly chat?: ChatAPI\n\n /** Database access (if permitted) */\n readonly database?: DatabaseAPI\n\n /** Local storage (if permitted) */\n readonly storage?: StorageAPI\n\n /** Logging (always available) */\n readonly log: LogAPI\n}\n\n/**\n * Network API for making HTTP requests\n */\nexport interface NetworkAPI {\n /**\n * Fetch a URL (permissions are enforced by host)\n */\n fetch(url: string, options?: RequestInit): Promise<Response>\n\n /**\n * Streaming fetch for responses like NDJSON or SSE.\n * Yields text chunks as they arrive from the server.\n *\n * @throws {Error} If the request fails or encounters a network error.\n * The error message will contain details about the failure.\n *\n * @example\n * ```typescript\n * try {\n * for await (const chunk of context.network.fetchStream(url, options)) {\n * // Process each chunk (may contain partial lines)\n * buffer += chunk\n * }\n * } catch (error) {\n * console.error('Streaming fetch failed:', error.message)\n * }\n * ```\n */\n fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown>\n}\n\n/**\n * Settings API for reading/writing extension settings\n */\nexport interface SettingsAPI {\n /**\n * Get all settings for this extension\n */\n getAll<T extends Record<string, unknown>>(): Promise<T>\n\n /**\n * Get a specific setting value\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a setting value\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Listen for setting changes\n */\n onChange(callback: (key: string, value: unknown) => void): Disposable\n}\n\n/**\n * Providers API for registering AI providers\n */\nexport interface ProvidersAPI {\n /**\n * Register an AI provider\n */\n register(provider: AIProvider): Disposable\n}\n\n/**\n * Tools API for registering tools\n */\nexport interface ToolsAPI {\n /**\n * Register a tool that Stina can use\n */\n register(tool: Tool): Disposable\n}\n\n/**\n * Actions API for registering UI actions\n */\nexport interface ActionsAPI {\n /**\n * Register an action that UI components can invoke\n */\n register(action: Action): Disposable\n}\n\n/**\n * Events API for notifying the host\n */\nexport interface EventsAPI {\n /**\n * Emit a named event with optional payload\n */\n emit(name: string, payload?: Record<string, unknown>): Promise<void>\n}\n\n/**\n * Scheduler schedule types\n */\nexport type SchedulerSchedule =\n | { type: 'at'; at: string }\n | { type: 'cron'; cron: string; timezone?: string }\n | { type: 'interval'; everyMs: number }\n\n/**\n * Scheduler job request\n */\nexport interface SchedulerJobRequest {\n id: string\n schedule: SchedulerSchedule\n payload?: Record<string, unknown>\n misfire?: 'run_once' | 'skip'\n /**\n * Optional user ID for user-scoped jobs.\n * If set, the job is associated with a specific user and the userId\n * will be passed to the extension when the job fires.\n */\n userId?: string\n}\n\n/**\n * Scheduler fire payload\n */\nexport interface SchedulerFirePayload {\n id: string\n payload?: Record<string, unknown>\n scheduledFor: string\n firedAt: string\n delayMs: number\n /** User ID if this is a user-scoped job, undefined if global */\n userId?: string\n}\n\n/**\n * Scheduler API for registering jobs\n */\nexport interface SchedulerAPI {\n schedule(job: SchedulerJobRequest): Promise<void>\n cancel(jobId: string): Promise<void>\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable\n}\n\n/**\n * User profile data\n */\nexport interface UserProfile {\n firstName?: string\n nickname?: string\n language?: string\n timezone?: string\n}\n\n/**\n * User API for profile access\n */\nexport interface UserAPI {\n getProfile(): Promise<UserProfile>\n}\n\n/**\n * Chat instruction message\n */\nexport interface ChatInstructionMessage {\n text: string\n conversationId?: string\n}\n\n/**\n * Chat API for appending instructions\n */\nexport interface ChatAPI {\n appendInstruction(message: ChatInstructionMessage): Promise<void>\n}\n\n/**\n * Database API for extension-specific tables\n */\nexport interface DatabaseAPI {\n /**\n * Execute a SQL query (only extension's prefixed tables allowed)\n */\n execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>\n}\n\n/**\n * Simple key-value storage API with support for user-scoped storage.\n *\n * ## Global vs User-Scoped Storage\n *\n * Extensions have access to two types of storage:\n * - **Global storage**: Shared across all users, accessed via `get()`, `set()`, etc.\n * - **User-scoped storage**: Isolated per user, accessed via `getForUser()`, `setForUser()`, etc.\n *\n * Use global storage for extension-wide settings and user-scoped storage for\n * user preferences, session data, or any data that should be private to a user.\n *\n * @example\n * ```typescript\n * // Global storage (extension-wide)\n * await storage.set('apiEndpoint', 'https://api.example.com')\n * const endpoint = await storage.get<string>('apiEndpoint')\n *\n * // User-scoped storage (per-user)\n * if (context.userId) {\n * await storage.setForUser(context.userId, 'preferences', { theme: 'dark' })\n * const prefs = await storage.getForUser<Preferences>(context.userId, 'preferences')\n * }\n * ```\n */\nexport interface StorageAPI {\n /**\n * Get a value by key (global/extension-scoped)\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a value (global/extension-scoped)\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key (global/extension-scoped)\n */\n delete(key: string): Promise<void>\n\n /**\n * Get all keys (global/extension-scoped)\n */\n keys(): Promise<string[]>\n\n /**\n * Get a value by key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n getForUser<T>(userId: string, key: string): Promise<T | undefined>\n\n /**\n * Set a value for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n * @param value The value to store\n */\n setForUser(userId: string, key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n deleteForUser(userId: string, key: string): Promise<void>\n\n /**\n * Get all keys for a specific user (user-scoped)\n * @param userId The user ID\n */\n keysForUser(userId: string): Promise<string[]>\n}\n\n/**\n * Logging API\n */\nexport interface LogAPI {\n debug(message: string, data?: Record<string, unknown>): void\n info(message: string, data?: Record<string, unknown>): void\n warn(message: string, data?: Record<string, unknown>): void\n error(message: string, data?: Record<string, unknown>): void\n}\n\n// ============================================================================\n// AI Provider Types\n// ============================================================================\n\n/**\n * AI provider implementation\n */\nexport interface AIProvider {\n /** Provider ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n\n /**\n * Get available models from this provider\n * @param options Optional settings for the provider (e.g., URL)\n */\n getModels(options?: GetModelsOptions): Promise<ModelInfo[]>\n\n /**\n * Chat completion with streaming\n */\n chat(\n messages: ChatMessage[],\n options: ChatOptions\n ): AsyncGenerator<StreamEvent, void, unknown>\n\n /**\n * Optional: Generate embeddings\n */\n embed?(texts: string[]): Promise<number[][]>\n}\n\n/**\n * Model information\n */\nexport interface ModelInfo {\n /** Model ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Context window size */\n contextLength?: number\n}\n\n/**\n * Chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n content: string\n /** For assistant messages: tool calls made by the model */\n tool_calls?: ToolCall[]\n /** For tool messages: the ID of the tool call this is a response to */\n tool_call_id?: string\n}\n\n/**\n * A tool call made by the model\n */\nexport interface ToolCall {\n /** Unique ID for this tool call */\n id: string\n /** Tool name/ID to invoke */\n name: string\n /** Arguments for the tool (as parsed object) */\n arguments: Record<string, unknown>\n}\n\n/**\n * Options for chat completion\n */\nexport interface ChatOptions {\n /** Model to use */\n model?: string\n /** Temperature (0-1) */\n temperature?: number\n /** Maximum tokens to generate */\n maxTokens?: number\n /** Abort signal for cancellation */\n signal?: AbortSignal\n /** Provider-specific settings from model configuration */\n settings?: Record<string, unknown>\n /** Available tools for this request */\n tools?: ToolDefinition[]\n}\n\n/**\n * Options for getModels\n */\nexport interface GetModelsOptions {\n /** Provider-specific settings (e.g., URL for Ollama) */\n settings?: Record<string, unknown>\n}\n\n/**\n * Streaming events from chat\n */\nexport type StreamEvent =\n | { type: 'content'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'tool_start'; name: string; input: unknown; toolCallId: string }\n | { type: 'tool_end'; name: string; output: unknown; toolCallId: string }\n | { type: 'done'; usage?: { inputTokens: number; outputTokens: number } }\n | { type: 'error'; message: string }\n\n// ============================================================================\n// Tool Types\n// ============================================================================\n\n/**\n * Tool implementation\n */\nexport interface Tool {\n /** Tool ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n /** Description for Stina */\n description: string\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n\n /**\n * Execute the tool\n */\n execute(params: Record<string, unknown>): Promise<ToolResult>\n}\n\n/**\n * Tool execution result\n */\nexport interface ToolResult {\n /** Whether the tool succeeded */\n success: boolean\n /** Result data (for Stina to use) */\n data?: unknown\n /** Human-readable message */\n message?: string\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Action Types (for UI interactions, separate from Tools)\n// ============================================================================\n\n/**\n * Action implementation for UI interactions.\n * Actions are invoked by UI components, not by Stina (AI).\n */\nexport interface Action {\n /** Action ID (unique within the extension) */\n id: string\n\n /**\n * Execute the action\n * @param params Parameters from the UI component (with $-values already resolved)\n */\n execute(params: Record<string, unknown>): Promise<ActionResult>\n}\n\n/**\n * Action execution result\n */\nexport interface ActionResult {\n /** Whether the action succeeded */\n success: boolean\n /** Result data (returned to UI) */\n data?: unknown\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Extension Entry Point\n// ============================================================================\n\n/**\n * Extension entry point interface\n */\nexport interface ExtensionModule {\n /**\n * Called when extension is activated\n */\n activate(context: ExtensionContext): void | Disposable | Promise<void | Disposable>\n\n /**\n * Called when extension is deactivated\n */\n deactivate?(): void | Promise<void>\n}\n","/**\n * Message protocol between Extension Host and Extension Workers\n */\n\nimport type {\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolResult,\n ActionResult,\n ModelInfo,\n SchedulerFirePayload,\n} from './types.js'\n\n// ============================================================================\n// Host → Worker Messages\n// ============================================================================\n\nexport type HostToWorkerMessage =\n | ActivateMessage\n | DeactivateMessage\n | SettingsChangedMessage\n | SchedulerFireMessage\n | ProviderChatRequestMessage\n | ProviderModelsRequestMessage\n | ToolExecuteRequestMessage\n | ActionExecuteRequestMessage\n | ResponseMessage\n | StreamingFetchChunkMessage\n\nexport interface ActivateMessage {\n type: 'activate'\n id: string\n payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n }\n}\n\nexport interface DeactivateMessage {\n type: 'deactivate'\n id: string\n}\n\nexport interface SettingsChangedMessage {\n type: 'settings-changed'\n id: string\n payload: {\n key: string\n value: unknown\n }\n}\n\nexport interface SchedulerFireMessage {\n type: 'scheduler-fire'\n id: string\n payload: SchedulerFirePayload\n}\n\nexport interface ProviderChatRequestMessage {\n type: 'provider-chat-request'\n id: string\n payload: {\n providerId: string\n messages: ChatMessage[]\n options: ChatOptions\n }\n}\n\nexport interface ProviderModelsRequestMessage {\n type: 'provider-models-request'\n id: string\n payload: {\n providerId: string\n options?: GetModelsOptions\n }\n}\n\nexport interface ToolExecuteRequestMessage {\n type: 'tool-execute-request'\n id: string\n payload: {\n toolId: string\n params: Record<string, unknown>\n /** User ID if the tool is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ActionExecuteRequestMessage {\n type: 'action-execute-request'\n id: string\n payload: {\n actionId: string\n params: Record<string, unknown>\n /** User ID if the action is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ResponseMessage {\n type: 'response'\n id: string\n payload: {\n requestId: string\n success: boolean\n data?: unknown\n error?: string\n }\n}\n\n/**\n * Message sent from host to worker with streaming fetch data chunks.\n * Used for streaming network responses (e.g., NDJSON streams from Ollama).\n */\nexport interface StreamingFetchChunkMessage {\n type: 'streaming-fetch-chunk'\n id: string\n payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n }\n}\n\n// ============================================================================\n// Worker → Host Messages\n// ============================================================================\n\nexport type WorkerToHostMessage =\n | ReadyMessage\n | RequestMessage\n | ProviderRegisteredMessage\n | ToolRegisteredMessage\n | ActionRegisteredMessage\n | StreamEventMessage\n | LogMessage\n | ProviderModelsResponseMessage\n | ToolExecuteResponseMessage\n | ActionExecuteResponseMessage\n | StreamingFetchAckMessage\n\nexport interface ReadyMessage {\n type: 'ready'\n}\n\n/**\n * Message sent from worker to host to acknowledge receipt of a streaming fetch chunk.\n * This enables backpressure control to prevent unbounded memory growth.\n */\nexport interface StreamingFetchAckMessage {\n type: 'streaming-fetch-ack'\n payload: {\n requestId: string\n }\n}\n\nexport interface RequestMessage {\n type: 'request'\n id: string\n method: RequestMethod\n payload: unknown\n}\n\nexport type RequestMethod =\n | 'network.fetch'\n | 'network.fetch-stream'\n | 'settings.getAll'\n | 'settings.get'\n | 'settings.set'\n | 'user.getProfile'\n | 'events.emit'\n | 'scheduler.schedule'\n | 'scheduler.cancel'\n | 'chat.appendInstruction'\n | 'database.execute'\n | 'storage.get'\n | 'storage.set'\n | 'storage.delete'\n | 'storage.keys'\n | 'storage.getForUser'\n | 'storage.setForUser'\n | 'storage.deleteForUser'\n | 'storage.keysForUser'\n\nexport interface ProviderRegisteredMessage {\n type: 'provider-registered'\n payload: {\n id: string\n name: string\n }\n}\n\nexport interface ToolRegisteredMessage {\n type: 'tool-registered'\n payload: {\n id: string\n name: string\n description: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface ActionRegisteredMessage {\n type: 'action-registered'\n payload: {\n id: string\n }\n}\n\nexport interface StreamEventMessage {\n type: 'stream-event'\n payload: {\n requestId: string\n event: StreamEvent\n }\n}\n\nexport interface ProviderModelsResponseMessage {\n type: 'provider-models-response'\n payload: {\n requestId: string\n models: ModelInfo[]\n error?: string\n }\n}\n\nexport interface ToolExecuteResponseMessage {\n type: 'tool-execute-response'\n payload: {\n requestId: string\n result: ToolResult\n error?: string\n }\n}\n\nexport interface ActionExecuteResponseMessage {\n type: 'action-execute-response'\n payload: {\n requestId: string\n result: ActionResult\n error?: string\n }\n}\n\nexport interface LogMessage {\n type: 'log'\n payload: {\n level: 'debug' | 'info' | 'warn' | 'error'\n message: string\n data?: Record<string, unknown>\n }\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\nexport interface PendingRequest<T = unknown> {\n resolve: (value: T) => void\n reject: (error: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,SAAS,uBACd,OACA,MACA,eAAe,MACP;AACR,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,IAAI,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,KAAK,EAAE,CAAC,KAAK;AAC1E;;;ACgPO,SAAS,oBAA4B;AAC1C,SAAO,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACjE;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/messages.ts"],"sourcesContent":["/**\n * @stina/extension-api\n *\n * Types and utilities for building Stina extensions.\n *\n * Extensions should import from this package for type definitions.\n * The runtime (worker-side code) should import from '@stina/extension-api/runtime'.\n */\n\n// Localization\nexport type { LocalizedString } from './types.js'\nexport { resolveLocalizedString } from './types.js'\n\n// Types\nexport type {\n // Manifest\n ExtensionManifest,\n Platform,\n ExtensionContributions,\n SettingDefinition,\n SettingOptionsMapping,\n SettingCreateMapping,\n ToolSettingsViewDefinition,\n ToolSettingsView,\n ToolSettingsListView,\n ToolSettingsListMapping,\n ToolSettingsComponentView,\n ToolSettingsActionDataSource,\n PanelDefinition,\n PanelView,\n PanelComponentView,\n PanelActionDataSource,\n PanelUnknownView,\n ProviderDefinition,\n PromptContribution,\n PromptSection,\n ToolDefinition,\n CommandDefinition,\n\n // Provider Configuration Schema\n ProviderConfigSchema,\n ProviderConfigProperty,\n ProviderConfigPropertyType,\n ProviderConfigSelectOption,\n ProviderConfigValidation,\n\n // Permissions\n Permission,\n NetworkPermission,\n StoragePermission,\n UserDataPermission,\n CapabilityPermission,\n SystemPermission,\n\n // Context\n ExtensionContext,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerSchedule,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n\n // AI Provider\n AIProvider,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolCall,\n\n // Tools\n Tool,\n ToolResult,\n\n // Actions\n Action,\n ActionResult,\n\n // Entry point\n ExtensionModule,\n} from './types.js'\n\n// Messages (for host implementation)\nexport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n ActivateMessage,\n DeactivateMessage,\n SettingsChangedMessage,\n ProviderChatRequestMessage,\n ProviderModelsRequestMessage,\n ToolExecuteRequestMessage,\n ToolExecuteResponseMessage,\n ActionExecuteRequestMessage,\n ActionExecuteResponseMessage,\n ResponseMessage,\n ReadyMessage,\n RequestMessage,\n RequestMethod,\n ProviderRegisteredMessage,\n ToolRegisteredMessage,\n ActionRegisteredMessage,\n StreamEventMessage,\n LogMessage,\n PendingRequest,\n} from './messages.js'\n\nexport { generateMessageId } from './messages.js'\n\n// Component types (for extension UI components)\nexport type {\n // Styling\n AllowedCSSProperty,\n ExtensionComponentStyle,\n // Base types\n ExtensionComponentData,\n // Iteration & Children\n ExtensionComponentIterator,\n ExtensionComponentChildren,\n // Actions\n ExtensionActionCall,\n ExtensionActionRef,\n // Data Sources & Panel Definition\n ExtensionDataSource,\n ExtensionPanelDefinition,\n // Component Props\n HeaderProps,\n LabelProps,\n ParagraphProps,\n ButtonProps,\n TextInputProps,\n DateTimeInputProps,\n SelectProps,\n VerticalStackProps,\n HorizontalStackProps,\n GridProps,\n DividerProps,\n IconProps,\n IconButtonType,\n IconButtonProps,\n PanelAction,\n PanelProps,\n ToggleProps,\n CollapsibleProps,\n PillVariant,\n PillProps,\n CheckboxProps,\n MarkdownProps,\n ModalProps,\n} from './types.components.js'\n","/**\n * A string that can be either a simple string or a map of language codes to localized strings.\n * When a simple string is provided, it's used as the default/fallback value.\n * When a map is provided, the appropriate language is selected at runtime.\n *\n * @example\n * // Simple string (backwards compatible)\n * name: \"Get Weather\"\n *\n * @example\n * // Localized strings\n * name: { en: \"Get Weather\", sv: \"Hämta väder\", de: \"Wetter abrufen\" }\n */\nexport type LocalizedString = string | Record<string, string>\n\n/**\n * Resolves a LocalizedString to an actual string value.\n * @param value The LocalizedString to resolve\n * @param lang The preferred language code (e.g., \"sv\", \"en\")\n * @param fallbackLang The fallback language code (defaults to \"en\")\n * @returns The resolved string value\n */\nexport function resolveLocalizedString(\n value: LocalizedString,\n lang: string,\n fallbackLang = 'en'\n): string {\n if (typeof value === 'string') {\n return value\n }\n // Try preferred language first, then fallback language, then first available, then empty string\n return value[lang] ?? value[fallbackLang] ?? Object.values(value)[0] ?? ''\n}\n\n/**\n * Extension manifest format (manifest.json)\n */\nexport interface ExtensionManifest {\n /** Unique identifier (e.g., \"ollama-provider\") */\n id: string\n /** Human-readable name */\n name: string\n /** Version string (semver) */\n version: string\n /** Short description */\n description: string\n /** Author information */\n author: {\n name: string\n url?: string\n }\n /** Repository URL */\n repository?: string\n /** License identifier */\n license?: string\n /** Minimum Stina version required */\n engines?: {\n stina: string\n }\n /** Supported platforms */\n platforms?: Platform[]\n /** Entry point file (relative to extension root) */\n main: string\n /** Required permissions */\n permissions: Permission[]\n /** What the extension contributes */\n contributes?: ExtensionContributions\n}\n\nexport type Platform = 'web' | 'electron' | 'tui'\n\n/**\n * What an extension can contribute to Stina\n */\nexport interface ExtensionContributions {\n /** User-configurable settings */\n settings?: SettingDefinition[]\n /** Tool settings views for UI */\n toolSettings?: ToolSettingsViewDefinition[]\n /** Right panel contributions */\n panels?: PanelDefinition[]\n /** AI providers */\n providers?: ProviderDefinition[]\n /** Tools for Stina to use */\n tools?: ToolDefinition[]\n /** Slash commands */\n commands?: CommandDefinition[]\n /** Prompt contributions for the system prompt */\n prompts?: PromptContribution[]\n}\n\n/**\n * Setting definition for the UI\n */\nexport interface SettingDefinition {\n /** Setting ID (namespaced automatically) */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** Setting type */\n type: 'string' | 'number' | 'boolean' | 'select'\n /** Default value */\n default?: unknown\n /** For select type: available options */\n options?: { value: string; label: string }[]\n /** For select type: load options from tool */\n optionsToolId?: string\n /** Params for options tool */\n optionsParams?: Record<string, unknown>\n /** Mapping for options tool response */\n optionsMapping?: SettingOptionsMapping\n /** Tool ID for creating a new option */\n createToolId?: string\n /** Label for create action */\n createLabel?: string\n /** Fields for create form */\n createFields?: SettingDefinition[]\n /** Static params always sent to create tool */\n createParams?: Record<string, unknown>\n /** Mapping for create tool response */\n createMapping?: SettingCreateMapping\n /** Validation rules */\n validation?: {\n required?: boolean\n min?: number\n max?: number\n pattern?: string\n }\n}\n\n/**\n * Mapping for select field options from tool response\n */\nexport interface SettingOptionsMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for option value */\n valueKey: string\n /** Key for option label */\n labelKey: string\n /** Optional key for description */\n descriptionKey?: string\n}\n\n/**\n * Mapping for create tool response\n */\nexport interface SettingCreateMapping {\n /** Key for result data object */\n resultKey?: string\n /** Key for option value (defaults to \"id\") */\n valueKey: string\n}\n\n/**\n * Tool settings view definition (UI schema)\n */\nexport interface ToolSettingsViewDefinition {\n /** Unique view ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** View configuration */\n view: ToolSettingsView\n /** Fields for create/edit forms (uses SettingDefinition) */\n fields?: SettingDefinition[]\n}\n\n/**\n * Tool settings view types\n */\nexport type ToolSettingsView = ToolSettingsListView | ToolSettingsComponentView\n\n/**\n * List view backed by tools\n */\nexport interface ToolSettingsListView {\n /** View kind */\n kind: 'list'\n /** Tool ID for listing items */\n listToolId: string\n /** Tool ID for fetching details (optional) */\n getToolId?: string\n /** Tool ID for creating/updating items (optional) */\n upsertToolId?: string\n /** Tool ID for deleting items (optional) */\n deleteToolId?: string\n /** Mapping from tool data to UI fields */\n mapping: ToolSettingsListMapping\n /** Param name for search query (default: \"query\") */\n searchParam?: string\n /** Param name for limit (default: \"limit\") */\n limitParam?: string\n /** Param name for get/delete ID (default: \"id\") */\n idParam?: string\n /** Static params always sent to list tool */\n listParams?: Record<string, unknown>\n}\n\n/**\n * Mapping from tool list data to UI fields\n */\nexport interface ToolSettingsListMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for total count in tool result data */\n countKey?: string\n /** Key for item ID */\n idKey: string\n /** Key for item label */\n labelKey: string\n /** Key for item description */\n descriptionKey?: string\n /** Key for secondary label */\n secondaryKey?: string\n}\n\n/**\n * Component-based tool settings view using the declarative DSL.\n * Reuses the same structure as PanelComponentView for consistency.\n */\nexport interface ToolSettingsComponentView {\n /** View kind */\n kind: 'component'\n /** Data sources. Keys become scope variables (e.g., \"$settings\"). */\n data?: Record<string, ToolSettingsActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Action-based data source for tool settings.\n */\nexport interface ToolSettingsActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that trigger refresh */\n refreshOn?: string[]\n}\n\n/**\n * Panel definition for right panel views\n */\nexport interface PanelDefinition {\n /** Unique panel ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Icon name (from huge-icons) */\n icon?: string\n /** Panel view schema */\n view: PanelView\n}\n\n/**\n * Panel view schema (declarative)\n */\nexport type PanelView = PanelComponentView | PanelUnknownView\n\nexport interface PanelUnknownView {\n /** View kind */\n kind: string\n /** Additional view configuration */\n [key: string]: unknown\n}\n\n/**\n * Action-based data source for declarative panels.\n * Uses actions (not tools) to fetch data.\n */\nexport interface PanelActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that should trigger a refresh of this data */\n refreshOn?: string[]\n}\n\n/**\n * Component-based panel view using the declarative DSL.\n * Data is fetched via actions, content is rendered via ExtensionComponent.\n */\nexport interface PanelComponentView {\n kind: 'component'\n /** Data sources available in the panel. Keys become variable names (e.g., \"$projects\"). */\n data?: Record<string, PanelActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Provider definition (metadata only, implementation in code)\n */\nexport interface ProviderDefinition {\n /** Provider ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Suggested default model when creating a new model configuration */\n suggestedDefaultModel?: string\n /** Default settings for this provider (e.g., { url: \"http://localhost:11434\" }) */\n defaultSettings?: Record<string, unknown>\n /** Schema for provider-specific configuration UI */\n configSchema?: ProviderConfigSchema\n}\n\n// ============================================================================\n// Prompt Contributions\n// ============================================================================\n\nexport type PromptSection = 'system' | 'behavior' | 'tools'\n\nexport interface PromptContribution {\n /** Unique ID within the extension */\n id: string\n /** Optional title for the prompt chunk */\n title?: string\n /** Prompt section placement */\n section?: PromptSection\n /** Plain text prompt content */\n text?: string\n /** Optional localized prompt content (keyed by locale, e.g. \"en\", \"sv\") */\n i18n?: Record<string, string>\n /** Optional ordering hint (lower comes first) */\n order?: number\n}\n\n// ============================================================================\n// Provider Configuration Schema\n// ============================================================================\n\n/**\n * Schema for provider-specific configuration.\n * Used to generate UI forms for configuring provider settings.\n */\nexport interface ProviderConfigSchema {\n /** Property definitions */\n properties: Record<string, ProviderConfigProperty>\n /** Display order of properties in UI (optional, defaults to object key order) */\n order?: string[]\n}\n\n/**\n * Property types for provider configuration\n */\nexport type ProviderConfigPropertyType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'password'\n | 'url'\n\n/**\n * Single property in a provider configuration schema.\n * Defines how a setting should be rendered and validated in the UI.\n */\nexport interface ProviderConfigProperty {\n /** Property type - determines UI control */\n type: ProviderConfigPropertyType\n /** Display label */\n title: string\n /** Help text shown below the input */\n description?: string\n /** Default value */\n default?: unknown\n /** Whether the field is required */\n required?: boolean\n /** Placeholder text for input fields */\n placeholder?: string\n /** For 'select' type: static options */\n options?: ProviderConfigSelectOption[]\n /** Validation rules */\n validation?: ProviderConfigValidation\n}\n\n/**\n * Option for select-type properties\n */\nexport interface ProviderConfigSelectOption {\n /** Value stored in settings */\n value: string\n /** Display label */\n label: string\n}\n\n/**\n * Validation rules for a property\n */\nexport interface ProviderConfigValidation {\n /** Regex pattern the value must match */\n pattern?: string\n /** Minimum string length */\n minLength?: number\n /** Maximum string length */\n maxLength?: number\n /** Minimum number value */\n min?: number\n /** Maximum number value */\n max?: number\n}\n\n/**\n * Tool definition (metadata only, implementation in code)\n */\nexport interface ToolDefinition {\n /** Tool ID */\n id: string\n /**\n * Display name - can be a simple string or localized strings.\n * @example \"Get Weather\"\n * @example { en: \"Get Weather\", sv: \"Hämta väder\" }\n */\n name: LocalizedString\n /**\n * Description for Stina - can be a simple string or localized strings.\n * Note: The AI always receives the English description (or fallback) for consistency.\n * Localized descriptions are used for UI display only.\n * @example \"Fetches current weather for a location\"\n * @example { en: \"Fetches current weather\", sv: \"Hämtar aktuellt väder\" }\n */\n description: LocalizedString\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Command definition\n */\nexport interface CommandDefinition {\n /** Command ID (e.g., \"weather\" for /weather) */\n id: string\n /** Display name */\n name: string\n /** Description */\n description: string\n}\n\n// ============================================================================\n// Permissions\n// ============================================================================\n\nexport type Permission =\n | NetworkPermission\n | StoragePermission\n | UserDataPermission\n | CapabilityPermission\n | SystemPermission\n\n/** Network access permissions */\nexport type NetworkPermission =\n | 'network:*'\n | `network:localhost`\n | `network:localhost:${number}`\n | `network:${string}`\n\n/** Storage permissions */\nexport type StoragePermission = 'database.own' | 'storage.local'\n\n/** User data permissions */\nexport type UserDataPermission =\n | 'user.profile.read'\n | 'user.location.read'\n | 'chat.history.read'\n | 'chat.current.read'\n\n/** Capability permissions */\nexport type CapabilityPermission =\n | 'provider.register'\n | 'tools.register'\n | 'actions.register'\n | 'settings.register'\n | 'commands.register'\n | 'panels.register'\n | 'events.emit'\n | 'scheduler.register'\n | 'chat.message.write'\n\n/** System permissions */\nexport type SystemPermission =\n | 'files.read'\n | 'files.write'\n | 'clipboard.read'\n | 'clipboard.write'\n\n// ============================================================================\n// Extension Context (API available to extensions)\n// ============================================================================\n\n/**\n * Disposable resource that can be cleaned up\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * Context provided to extension's activate function.\n *\n * ## User ID Context\n *\n * The `userId` field provides the current user context for extensions. It is set when:\n * - A tool is executed by a user\n * - An action is executed by a user\n * - A scheduled job fires (if the job was created with a userId)\n *\n * For extension activation, `userId` is undefined since activation happens at system level.\n * Extensions should check for `userId` in their tool/action handlers to access user-specific data.\n *\n * @example\n * ```typescript\n * // In a tool execute handler:\n * execute: async (params, context) => {\n * if (context.userId) {\n * // User-specific logic\n * const userData = await storage.getForUser(context.userId, 'preferences')\n * }\n * }\n * ```\n */\nexport interface ExtensionContext {\n /** Extension metadata */\n readonly extension: {\n readonly id: string\n readonly version: string\n readonly storagePath: string\n }\n\n /**\n * Current user ID if in a user context, undefined for global/system operations.\n * Set when tools or actions are executed by a user, or when a user-scoped job fires.\n */\n readonly userId?: string\n\n /** Network access (if permitted) */\n readonly network?: NetworkAPI\n\n /** Settings access (if permitted) */\n readonly settings?: SettingsAPI\n\n /** Provider registration (if permitted) */\n readonly providers?: ProvidersAPI\n\n /** Tool registration (if permitted) */\n readonly tools?: ToolsAPI\n\n /** Action registration (if permitted) */\n readonly actions?: ActionsAPI\n\n /** Event emission (if permitted) */\n readonly events?: EventsAPI\n\n /** Scheduler access (if permitted) */\n readonly scheduler?: SchedulerAPI\n\n /** User data access (if permitted) */\n readonly user?: UserAPI\n\n /** Chat access (if permitted) */\n readonly chat?: ChatAPI\n\n /** Database access (if permitted) */\n readonly database?: DatabaseAPI\n\n /** Local storage (if permitted) */\n readonly storage?: StorageAPI\n\n /** Logging (always available) */\n readonly log: LogAPI\n}\n\n/**\n * Network API for making HTTP requests\n */\nexport interface NetworkAPI {\n /**\n * Fetch a URL (permissions are enforced by host)\n */\n fetch(url: string, options?: RequestInit): Promise<Response>\n\n /**\n * Streaming fetch for responses like NDJSON or SSE.\n * Yields text chunks as they arrive from the server.\n *\n * @throws {Error} If the request fails or encounters a network error.\n * The error message will contain details about the failure.\n *\n * @example\n * ```typescript\n * try {\n * for await (const chunk of context.network.fetchStream(url, options)) {\n * // Process each chunk (may contain partial lines)\n * buffer += chunk\n * }\n * } catch (error) {\n * console.error('Streaming fetch failed:', error.message)\n * }\n * ```\n */\n fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown>\n}\n\n/**\n * Settings API for reading/writing extension settings\n */\nexport interface SettingsAPI {\n /**\n * Get all settings for this extension\n */\n getAll<T extends Record<string, unknown>>(): Promise<T>\n\n /**\n * Get a specific setting value\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a setting value\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Listen for setting changes\n */\n onChange(callback: (key: string, value: unknown) => void): Disposable\n}\n\n/**\n * Providers API for registering AI providers\n */\nexport interface ProvidersAPI {\n /**\n * Register an AI provider\n */\n register(provider: AIProvider): Disposable\n}\n\n/**\n * Tools API for registering tools\n */\nexport interface ToolsAPI {\n /**\n * Register a tool that Stina can use\n */\n register(tool: Tool): Disposable\n}\n\n/**\n * Actions API for registering UI actions\n */\nexport interface ActionsAPI {\n /**\n * Register an action that UI components can invoke\n */\n register(action: Action): Disposable\n}\n\n/**\n * Events API for notifying the host\n */\nexport interface EventsAPI {\n /**\n * Emit a named event with optional payload\n */\n emit(name: string, payload?: Record<string, unknown>): Promise<void>\n}\n\n/**\n * Scheduler schedule types\n */\nexport type SchedulerSchedule =\n | { type: 'at'; at: string }\n | { type: 'cron'; cron: string; timezone?: string }\n | { type: 'interval'; everyMs: number }\n\n/**\n * Scheduler job request\n */\nexport interface SchedulerJobRequest {\n id: string\n schedule: SchedulerSchedule\n payload?: Record<string, unknown>\n misfire?: 'run_once' | 'skip'\n /**\n * Optional user ID for user-scoped jobs.\n * If set, the job is associated with a specific user and the userId\n * will be passed to the extension when the job fires.\n */\n userId?: string\n}\n\n/**\n * Scheduler fire payload\n */\nexport interface SchedulerFirePayload {\n id: string\n payload?: Record<string, unknown>\n scheduledFor: string\n firedAt: string\n delayMs: number\n /** User ID if this is a user-scoped job, undefined if global */\n userId?: string\n}\n\n/**\n * Scheduler API for registering jobs\n */\nexport interface SchedulerAPI {\n schedule(job: SchedulerJobRequest): Promise<void>\n cancel(jobId: string): Promise<void>\n onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable\n}\n\n/**\n * User profile data\n */\nexport interface UserProfile {\n firstName?: string\n nickname?: string\n language?: string\n timezone?: string\n}\n\n/**\n * User API for profile access\n */\nexport interface UserAPI {\n getProfile(): Promise<UserProfile>\n}\n\n/**\n * Chat instruction message\n */\nexport interface ChatInstructionMessage {\n text: string\n conversationId?: string\n userId?: string\n}\n\n/**\n * Chat API for appending instructions\n */\nexport interface ChatAPI {\n appendInstruction(message: ChatInstructionMessage): Promise<void>\n}\n\n/**\n * Database API for extension-specific tables\n */\nexport interface DatabaseAPI {\n /**\n * Execute a SQL query (only extension's prefixed tables allowed)\n */\n execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>\n}\n\n/**\n * Simple key-value storage API with support for user-scoped storage.\n *\n * ## Global vs User-Scoped Storage\n *\n * Extensions have access to two types of storage:\n * - **Global storage**: Shared across all users, accessed via `get()`, `set()`, etc.\n * - **User-scoped storage**: Isolated per user, accessed via `getForUser()`, `setForUser()`, etc.\n *\n * Use global storage for extension-wide settings and user-scoped storage for\n * user preferences, session data, or any data that should be private to a user.\n *\n * @example\n * ```typescript\n * // Global storage (extension-wide)\n * await storage.set('apiEndpoint', 'https://api.example.com')\n * const endpoint = await storage.get<string>('apiEndpoint')\n *\n * // User-scoped storage (per-user)\n * if (context.userId) {\n * await storage.setForUser(context.userId, 'preferences', { theme: 'dark' })\n * const prefs = await storage.getForUser<Preferences>(context.userId, 'preferences')\n * }\n * ```\n */\nexport interface StorageAPI {\n /**\n * Get a value by key (global/extension-scoped)\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a value (global/extension-scoped)\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key (global/extension-scoped)\n */\n delete(key: string): Promise<void>\n\n /**\n * Get all keys (global/extension-scoped)\n */\n keys(): Promise<string[]>\n\n /**\n * Get a value by key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n getForUser<T>(userId: string, key: string): Promise<T | undefined>\n\n /**\n * Set a value for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n * @param value The value to store\n */\n setForUser(userId: string, key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n deleteForUser(userId: string, key: string): Promise<void>\n\n /**\n * Get all keys for a specific user (user-scoped)\n * @param userId The user ID\n */\n keysForUser(userId: string): Promise<string[]>\n}\n\n/**\n * Logging API\n */\nexport interface LogAPI {\n debug(message: string, data?: Record<string, unknown>): void\n info(message: string, data?: Record<string, unknown>): void\n warn(message: string, data?: Record<string, unknown>): void\n error(message: string, data?: Record<string, unknown>): void\n}\n\n// ============================================================================\n// AI Provider Types\n// ============================================================================\n\n/**\n * AI provider implementation\n */\nexport interface AIProvider {\n /** Provider ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n\n /**\n * Get available models from this provider\n * @param options Optional settings for the provider (e.g., URL)\n */\n getModels(options?: GetModelsOptions): Promise<ModelInfo[]>\n\n /**\n * Chat completion with streaming\n */\n chat(\n messages: ChatMessage[],\n options: ChatOptions\n ): AsyncGenerator<StreamEvent, void, unknown>\n\n /**\n * Optional: Generate embeddings\n */\n embed?(texts: string[]): Promise<number[][]>\n}\n\n/**\n * Model information\n */\nexport interface ModelInfo {\n /** Model ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Context window size */\n contextLength?: number\n}\n\n/**\n * Chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n content: string\n /** For assistant messages: tool calls made by the model */\n tool_calls?: ToolCall[]\n /** For tool messages: the ID of the tool call this is a response to */\n tool_call_id?: string\n}\n\n/**\n * A tool call made by the model\n */\nexport interface ToolCall {\n /** Unique ID for this tool call */\n id: string\n /** Tool name/ID to invoke */\n name: string\n /** Arguments for the tool (as parsed object) */\n arguments: Record<string, unknown>\n}\n\n/**\n * Options for chat completion\n */\nexport interface ChatOptions {\n /** Model to use */\n model?: string\n /** Temperature (0-1) */\n temperature?: number\n /** Maximum tokens to generate */\n maxTokens?: number\n /** Abort signal for cancellation */\n signal?: AbortSignal\n /** Provider-specific settings from model configuration */\n settings?: Record<string, unknown>\n /** Available tools for this request */\n tools?: ToolDefinition[]\n}\n\n/**\n * Options for getModels\n */\nexport interface GetModelsOptions {\n /** Provider-specific settings (e.g., URL for Ollama) */\n settings?: Record<string, unknown>\n}\n\n/**\n * Streaming events from chat\n */\nexport type StreamEvent =\n | { type: 'content'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'tool_start'; name: string; input: unknown; toolCallId: string }\n | { type: 'tool_end'; name: string; output: unknown; toolCallId: string }\n | { type: 'done'; usage?: { inputTokens: number; outputTokens: number } }\n | { type: 'error'; message: string }\n\n// ============================================================================\n// Tool Types\n// ============================================================================\n\n/**\n * Tool implementation\n */\nexport interface Tool {\n /** Tool ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n /** Description for Stina */\n description: string\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n\n /**\n * Execute the tool\n */\n execute(params: Record<string, unknown>): Promise<ToolResult>\n}\n\n/**\n * Tool execution result\n */\nexport interface ToolResult {\n /** Whether the tool succeeded */\n success: boolean\n /** Result data (for Stina to use) */\n data?: unknown\n /** Human-readable message */\n message?: string\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Action Types (for UI interactions, separate from Tools)\n// ============================================================================\n\n/**\n * Action implementation for UI interactions.\n * Actions are invoked by UI components, not by Stina (AI).\n */\nexport interface Action {\n /** Action ID (unique within the extension) */\n id: string\n\n /**\n * Execute the action\n * @param params Parameters from the UI component (with $-values already resolved)\n */\n execute(params: Record<string, unknown>): Promise<ActionResult>\n}\n\n/**\n * Action execution result\n */\nexport interface ActionResult {\n /** Whether the action succeeded */\n success: boolean\n /** Result data (returned to UI) */\n data?: unknown\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Extension Entry Point\n// ============================================================================\n\n/**\n * Extension entry point interface\n */\nexport interface ExtensionModule {\n /**\n * Called when extension is activated\n */\n activate(context: ExtensionContext): void | Disposable | Promise<void | Disposable>\n\n /**\n * Called when extension is deactivated\n */\n deactivate?(): void | Promise<void>\n}\n","/**\n * Message protocol between Extension Host and Extension Workers\n */\n\nimport type {\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolResult,\n ActionResult,\n ModelInfo,\n SchedulerFirePayload,\n} from './types.js'\n\n// ============================================================================\n// Host → Worker Messages\n// ============================================================================\n\nexport type HostToWorkerMessage =\n | ActivateMessage\n | DeactivateMessage\n | SettingsChangedMessage\n | SchedulerFireMessage\n | ProviderChatRequestMessage\n | ProviderModelsRequestMessage\n | ToolExecuteRequestMessage\n | ActionExecuteRequestMessage\n | ResponseMessage\n | StreamingFetchChunkMessage\n\nexport interface ActivateMessage {\n type: 'activate'\n id: string\n payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n }\n}\n\nexport interface DeactivateMessage {\n type: 'deactivate'\n id: string\n}\n\nexport interface SettingsChangedMessage {\n type: 'settings-changed'\n id: string\n payload: {\n key: string\n value: unknown\n }\n}\n\nexport interface SchedulerFireMessage {\n type: 'scheduler-fire'\n id: string\n payload: SchedulerFirePayload\n}\n\nexport interface ProviderChatRequestMessage {\n type: 'provider-chat-request'\n id: string\n payload: {\n providerId: string\n messages: ChatMessage[]\n options: ChatOptions\n }\n}\n\nexport interface ProviderModelsRequestMessage {\n type: 'provider-models-request'\n id: string\n payload: {\n providerId: string\n options?: GetModelsOptions\n }\n}\n\nexport interface ToolExecuteRequestMessage {\n type: 'tool-execute-request'\n id: string\n payload: {\n toolId: string\n params: Record<string, unknown>\n /** User ID if the tool is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ActionExecuteRequestMessage {\n type: 'action-execute-request'\n id: string\n payload: {\n actionId: string\n params: Record<string, unknown>\n /** User ID if the action is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ResponseMessage {\n type: 'response'\n id: string\n payload: {\n requestId: string\n success: boolean\n data?: unknown\n error?: string\n }\n}\n\n/**\n * Message sent from host to worker with streaming fetch data chunks.\n * Used for streaming network responses (e.g., NDJSON streams from Ollama).\n */\nexport interface StreamingFetchChunkMessage {\n type: 'streaming-fetch-chunk'\n id: string\n payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n }\n}\n\n// ============================================================================\n// Worker → Host Messages\n// ============================================================================\n\nexport type WorkerToHostMessage =\n | ReadyMessage\n | RequestMessage\n | ProviderRegisteredMessage\n | ToolRegisteredMessage\n | ActionRegisteredMessage\n | StreamEventMessage\n | LogMessage\n | ProviderModelsResponseMessage\n | ToolExecuteResponseMessage\n | ActionExecuteResponseMessage\n | StreamingFetchAckMessage\n\nexport interface ReadyMessage {\n type: 'ready'\n}\n\n/**\n * Message sent from worker to host to acknowledge receipt of a streaming fetch chunk.\n * This enables backpressure control to prevent unbounded memory growth.\n */\nexport interface StreamingFetchAckMessage {\n type: 'streaming-fetch-ack'\n payload: {\n requestId: string\n }\n}\n\nexport interface RequestMessage {\n type: 'request'\n id: string\n method: RequestMethod\n payload: unknown\n}\n\nexport type RequestMethod =\n | 'network.fetch'\n | 'network.fetch-stream'\n | 'settings.getAll'\n | 'settings.get'\n | 'settings.set'\n | 'user.getProfile'\n | 'events.emit'\n | 'scheduler.schedule'\n | 'scheduler.cancel'\n | 'chat.appendInstruction'\n | 'database.execute'\n | 'storage.get'\n | 'storage.set'\n | 'storage.delete'\n | 'storage.keys'\n | 'storage.getForUser'\n | 'storage.setForUser'\n | 'storage.deleteForUser'\n | 'storage.keysForUser'\n\nexport interface ProviderRegisteredMessage {\n type: 'provider-registered'\n payload: {\n id: string\n name: string\n }\n}\n\nexport interface ToolRegisteredMessage {\n type: 'tool-registered'\n payload: {\n id: string\n name: string\n description: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface ActionRegisteredMessage {\n type: 'action-registered'\n payload: {\n id: string\n }\n}\n\nexport interface StreamEventMessage {\n type: 'stream-event'\n payload: {\n requestId: string\n event: StreamEvent\n }\n}\n\nexport interface ProviderModelsResponseMessage {\n type: 'provider-models-response'\n payload: {\n requestId: string\n models: ModelInfo[]\n error?: string\n }\n}\n\nexport interface ToolExecuteResponseMessage {\n type: 'tool-execute-response'\n payload: {\n requestId: string\n result: ToolResult\n error?: string\n }\n}\n\nexport interface ActionExecuteResponseMessage {\n type: 'action-execute-response'\n payload: {\n requestId: string\n result: ActionResult\n error?: string\n }\n}\n\nexport interface LogMessage {\n type: 'log'\n payload: {\n level: 'debug' | 'info' | 'warn' | 'error'\n message: string\n data?: Record<string, unknown>\n }\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\nexport interface PendingRequest<T = unknown> {\n resolve: (value: T) => void\n reject: (error: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,SAAS,uBACd,OACA,MACA,eAAe,MACP;AACR,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,IAAI,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,KAAK,EAAE,CAAC,KAAK;AAC1E;;;ACgPO,SAAS,oBAA4B;AAC1C,SAAO,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACjE;","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SchedulerFirePayload, C as ChatMessage, a as ChatOptions, G as GetModelsOptions, b as StreamEvent, M as ModelInfo, T as ToolResult, A as ActionResult } from './types-
|
|
2
|
-
export { a7 as AIProvider, aa as Action, X as ActionsAPI, ac as AllowedCSSProperty, ao as ButtonProps, I as CapabilityPermission, a2 as ChatAPI, a3 as ChatInstructionMessage, aF as CheckboxProps, aC as CollapsibleProps, w as CommandDefinition, a4 as DatabaseAPI, aq as DateTimeInputProps, O as Disposable, av as DividerProps, Y as EventsAPI, ah as ExtensionActionCall, ai as ExtensionActionRef, ag as ExtensionComponentChildren, ae as ExtensionComponentData, af as ExtensionComponentIterator, ad as ExtensionComponentStyle, K as ExtensionContext, c as ExtensionContributions, aj as ExtensionDataSource, E as ExtensionManifest, ab as ExtensionModule, ak as ExtensionPanelDefinition, au as GridProps, al as HeaderProps, at as HorizontalStackProps, ay as IconButtonProps, ax as IconButtonType, aw as IconProps, am as LabelProps, L as LocalizedString, a6 as LogAPI, aG as MarkdownProps, aH as ModalProps, Q as NetworkAPI, N as NetworkPermission, az as PanelAction, p as PanelActionDataSource, o as PanelComponentView, m as PanelDefinition, aA as PanelProps, q as PanelUnknownView, n as PanelView, an as ParagraphProps, F as Permission, aE as PillProps, aD as PillVariant, P as Platform, t as PromptContribution, u as PromptSection, y as ProviderConfigProperty, z as ProviderConfigPropertyType, x as ProviderConfigSchema, B as ProviderConfigSelectOption, D as ProviderConfigValidation, s as ProviderDefinition, V as ProvidersAPI, Z as SchedulerAPI, _ as SchedulerJobRequest, $ as SchedulerSchedule, ar as SelectProps, f as SettingCreateMapping, d as SettingDefinition, e as SettingOptionsMapping, R as SettingsAPI, a5 as StorageAPI, H as StoragePermission, J as SystemPermission, ap as TextInputProps, aB as ToggleProps, a9 as Tool, a8 as ToolCall, v as ToolDefinition, l as ToolSettingsActionDataSource, k as ToolSettingsComponentView, j as ToolSettingsListMapping, i as ToolSettingsListView, h as ToolSettingsView, g as ToolSettingsViewDefinition, W as ToolsAPI, a0 as UserAPI, U as UserDataPermission, a1 as UserProfile, as as VerticalStackProps, r as resolveLocalizedString } from './types-
|
|
1
|
+
import { S as SchedulerFirePayload, C as ChatMessage, a as ChatOptions, G as GetModelsOptions, b as StreamEvent, M as ModelInfo, T as ToolResult, A as ActionResult } from './types-CvfONPis.cjs';
|
|
2
|
+
export { a7 as AIProvider, aa as Action, X as ActionsAPI, ac as AllowedCSSProperty, ao as ButtonProps, I as CapabilityPermission, a2 as ChatAPI, a3 as ChatInstructionMessage, aF as CheckboxProps, aC as CollapsibleProps, w as CommandDefinition, a4 as DatabaseAPI, aq as DateTimeInputProps, O as Disposable, av as DividerProps, Y as EventsAPI, ah as ExtensionActionCall, ai as ExtensionActionRef, ag as ExtensionComponentChildren, ae as ExtensionComponentData, af as ExtensionComponentIterator, ad as ExtensionComponentStyle, K as ExtensionContext, c as ExtensionContributions, aj as ExtensionDataSource, E as ExtensionManifest, ab as ExtensionModule, ak as ExtensionPanelDefinition, au as GridProps, al as HeaderProps, at as HorizontalStackProps, ay as IconButtonProps, ax as IconButtonType, aw as IconProps, am as LabelProps, L as LocalizedString, a6 as LogAPI, aG as MarkdownProps, aH as ModalProps, Q as NetworkAPI, N as NetworkPermission, az as PanelAction, p as PanelActionDataSource, o as PanelComponentView, m as PanelDefinition, aA as PanelProps, q as PanelUnknownView, n as PanelView, an as ParagraphProps, F as Permission, aE as PillProps, aD as PillVariant, P as Platform, t as PromptContribution, u as PromptSection, y as ProviderConfigProperty, z as ProviderConfigPropertyType, x as ProviderConfigSchema, B as ProviderConfigSelectOption, D as ProviderConfigValidation, s as ProviderDefinition, V as ProvidersAPI, Z as SchedulerAPI, _ as SchedulerJobRequest, $ as SchedulerSchedule, ar as SelectProps, f as SettingCreateMapping, d as SettingDefinition, e as SettingOptionsMapping, R as SettingsAPI, a5 as StorageAPI, H as StoragePermission, J as SystemPermission, ap as TextInputProps, aB as ToggleProps, a9 as Tool, a8 as ToolCall, v as ToolDefinition, l as ToolSettingsActionDataSource, k as ToolSettingsComponentView, j as ToolSettingsListMapping, i as ToolSettingsListView, h as ToolSettingsView, g as ToolSettingsViewDefinition, W as ToolsAPI, a0 as UserAPI, U as UserDataPermission, a1 as UserProfile, as as VerticalStackProps, r as resolveLocalizedString } from './types-CvfONPis.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Message protocol between Extension Host and Extension Workers
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SchedulerFirePayload, C as ChatMessage, a as ChatOptions, G as GetModelsOptions, b as StreamEvent, M as ModelInfo, T as ToolResult, A as ActionResult } from './types-
|
|
2
|
-
export { a7 as AIProvider, aa as Action, X as ActionsAPI, ac as AllowedCSSProperty, ao as ButtonProps, I as CapabilityPermission, a2 as ChatAPI, a3 as ChatInstructionMessage, aF as CheckboxProps, aC as CollapsibleProps, w as CommandDefinition, a4 as DatabaseAPI, aq as DateTimeInputProps, O as Disposable, av as DividerProps, Y as EventsAPI, ah as ExtensionActionCall, ai as ExtensionActionRef, ag as ExtensionComponentChildren, ae as ExtensionComponentData, af as ExtensionComponentIterator, ad as ExtensionComponentStyle, K as ExtensionContext, c as ExtensionContributions, aj as ExtensionDataSource, E as ExtensionManifest, ab as ExtensionModule, ak as ExtensionPanelDefinition, au as GridProps, al as HeaderProps, at as HorizontalStackProps, ay as IconButtonProps, ax as IconButtonType, aw as IconProps, am as LabelProps, L as LocalizedString, a6 as LogAPI, aG as MarkdownProps, aH as ModalProps, Q as NetworkAPI, N as NetworkPermission, az as PanelAction, p as PanelActionDataSource, o as PanelComponentView, m as PanelDefinition, aA as PanelProps, q as PanelUnknownView, n as PanelView, an as ParagraphProps, F as Permission, aE as PillProps, aD as PillVariant, P as Platform, t as PromptContribution, u as PromptSection, y as ProviderConfigProperty, z as ProviderConfigPropertyType, x as ProviderConfigSchema, B as ProviderConfigSelectOption, D as ProviderConfigValidation, s as ProviderDefinition, V as ProvidersAPI, Z as SchedulerAPI, _ as SchedulerJobRequest, $ as SchedulerSchedule, ar as SelectProps, f as SettingCreateMapping, d as SettingDefinition, e as SettingOptionsMapping, R as SettingsAPI, a5 as StorageAPI, H as StoragePermission, J as SystemPermission, ap as TextInputProps, aB as ToggleProps, a9 as Tool, a8 as ToolCall, v as ToolDefinition, l as ToolSettingsActionDataSource, k as ToolSettingsComponentView, j as ToolSettingsListMapping, i as ToolSettingsListView, h as ToolSettingsView, g as ToolSettingsViewDefinition, W as ToolsAPI, a0 as UserAPI, U as UserDataPermission, a1 as UserProfile, as as VerticalStackProps, r as resolveLocalizedString } from './types-
|
|
1
|
+
import { S as SchedulerFirePayload, C as ChatMessage, a as ChatOptions, G as GetModelsOptions, b as StreamEvent, M as ModelInfo, T as ToolResult, A as ActionResult } from './types-CvfONPis.js';
|
|
2
|
+
export { a7 as AIProvider, aa as Action, X as ActionsAPI, ac as AllowedCSSProperty, ao as ButtonProps, I as CapabilityPermission, a2 as ChatAPI, a3 as ChatInstructionMessage, aF as CheckboxProps, aC as CollapsibleProps, w as CommandDefinition, a4 as DatabaseAPI, aq as DateTimeInputProps, O as Disposable, av as DividerProps, Y as EventsAPI, ah as ExtensionActionCall, ai as ExtensionActionRef, ag as ExtensionComponentChildren, ae as ExtensionComponentData, af as ExtensionComponentIterator, ad as ExtensionComponentStyle, K as ExtensionContext, c as ExtensionContributions, aj as ExtensionDataSource, E as ExtensionManifest, ab as ExtensionModule, ak as ExtensionPanelDefinition, au as GridProps, al as HeaderProps, at as HorizontalStackProps, ay as IconButtonProps, ax as IconButtonType, aw as IconProps, am as LabelProps, L as LocalizedString, a6 as LogAPI, aG as MarkdownProps, aH as ModalProps, Q as NetworkAPI, N as NetworkPermission, az as PanelAction, p as PanelActionDataSource, o as PanelComponentView, m as PanelDefinition, aA as PanelProps, q as PanelUnknownView, n as PanelView, an as ParagraphProps, F as Permission, aE as PillProps, aD as PillVariant, P as Platform, t as PromptContribution, u as PromptSection, y as ProviderConfigProperty, z as ProviderConfigPropertyType, x as ProviderConfigSchema, B as ProviderConfigSelectOption, D as ProviderConfigValidation, s as ProviderDefinition, V as ProvidersAPI, Z as SchedulerAPI, _ as SchedulerJobRequest, $ as SchedulerSchedule, ar as SelectProps, f as SettingCreateMapping, d as SettingDefinition, e as SettingOptionsMapping, R as SettingsAPI, a5 as StorageAPI, H as StoragePermission, J as SystemPermission, ap as TextInputProps, aB as ToggleProps, a9 as Tool, a8 as ToolCall, v as ToolDefinition, l as ToolSettingsActionDataSource, k as ToolSettingsComponentView, j as ToolSettingsListMapping, i as ToolSettingsListView, h as ToolSettingsView, g as ToolSettingsViewDefinition, W as ToolsAPI, a0 as UserAPI, U as UserDataPermission, a1 as UserProfile, as as VerticalStackProps, r as resolveLocalizedString } from './types-CvfONPis.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Message protocol between Extension Host and Extension Workers
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * A string that can be either a simple string or a map of language codes to localized strings.\n * When a simple string is provided, it's used as the default/fallback value.\n * When a map is provided, the appropriate language is selected at runtime.\n *\n * @example\n * // Simple string (backwards compatible)\n * name: \"Get Weather\"\n *\n * @example\n * // Localized strings\n * name: { en: \"Get Weather\", sv: \"Hämta väder\", de: \"Wetter abrufen\" }\n */\nexport type LocalizedString = string | Record<string, string>\n\n/**\n * Resolves a LocalizedString to an actual string value.\n * @param value The LocalizedString to resolve\n * @param lang The preferred language code (e.g., \"sv\", \"en\")\n * @param fallbackLang The fallback language code (defaults to \"en\")\n * @returns The resolved string value\n */\nexport function resolveLocalizedString(\n value: LocalizedString,\n lang: string,\n fallbackLang = 'en'\n): string {\n if (typeof value === 'string') {\n return value\n }\n // Try preferred language first, then fallback language, then first available, then empty string\n return value[lang] ?? value[fallbackLang] ?? Object.values(value)[0] ?? ''\n}\n\n/**\n * Extension manifest format (manifest.json)\n */\nexport interface ExtensionManifest {\n /** Unique identifier (e.g., \"ollama-provider\") */\n id: string\n /** Human-readable name */\n name: string\n /** Version string (semver) */\n version: string\n /** Short description */\n description: string\n /** Author information */\n author: {\n name: string\n url?: string\n }\n /** Repository URL */\n repository?: string\n /** License identifier */\n license?: string\n /** Minimum Stina version required */\n engines?: {\n stina: string\n }\n /** Supported platforms */\n platforms?: Platform[]\n /** Entry point file (relative to extension root) */\n main: string\n /** Required permissions */\n permissions: Permission[]\n /** What the extension contributes */\n contributes?: ExtensionContributions\n}\n\nexport type Platform = 'web' | 'electron' | 'tui'\n\n/**\n * What an extension can contribute to Stina\n */\nexport interface ExtensionContributions {\n /** User-configurable settings */\n settings?: SettingDefinition[]\n /** Tool settings views for UI */\n toolSettings?: ToolSettingsViewDefinition[]\n /** Right panel contributions */\n panels?: PanelDefinition[]\n /** AI providers */\n providers?: ProviderDefinition[]\n /** Tools for Stina to use */\n tools?: ToolDefinition[]\n /** Slash commands */\n commands?: CommandDefinition[]\n /** Prompt contributions for the system prompt */\n prompts?: PromptContribution[]\n}\n\n/**\n * Setting definition for the UI\n */\nexport interface SettingDefinition {\n /** Setting ID (namespaced automatically) */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** Setting type */\n type: 'string' | 'number' | 'boolean' | 'select'\n /** Default value */\n default?: unknown\n /** For select type: available options */\n options?: { value: string; label: string }[]\n /** For select type: load options from tool */\n optionsToolId?: string\n /** Params for options tool */\n optionsParams?: Record<string, unknown>\n /** Mapping for options tool response */\n optionsMapping?: SettingOptionsMapping\n /** Tool ID for creating a new option */\n createToolId?: string\n /** Label for create action */\n createLabel?: string\n /** Fields for create form */\n createFields?: SettingDefinition[]\n /** Static params always sent to create tool */\n createParams?: Record<string, unknown>\n /** Mapping for create tool response */\n createMapping?: SettingCreateMapping\n /** Validation rules */\n validation?: {\n required?: boolean\n min?: number\n max?: number\n pattern?: string\n }\n}\n\n/**\n * Mapping for select field options from tool response\n */\nexport interface SettingOptionsMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for option value */\n valueKey: string\n /** Key for option label */\n labelKey: string\n /** Optional key for description */\n descriptionKey?: string\n}\n\n/**\n * Mapping for create tool response\n */\nexport interface SettingCreateMapping {\n /** Key for result data object */\n resultKey?: string\n /** Key for option value (defaults to \"id\") */\n valueKey: string\n}\n\n/**\n * Tool settings view definition (UI schema)\n */\nexport interface ToolSettingsViewDefinition {\n /** Unique view ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** View configuration */\n view: ToolSettingsView\n /** Fields for create/edit forms (uses SettingDefinition) */\n fields?: SettingDefinition[]\n}\n\n/**\n * Tool settings view types\n */\nexport type ToolSettingsView = ToolSettingsListView | ToolSettingsComponentView\n\n/**\n * List view backed by tools\n */\nexport interface ToolSettingsListView {\n /** View kind */\n kind: 'list'\n /** Tool ID for listing items */\n listToolId: string\n /** Tool ID for fetching details (optional) */\n getToolId?: string\n /** Tool ID for creating/updating items (optional) */\n upsertToolId?: string\n /** Tool ID for deleting items (optional) */\n deleteToolId?: string\n /** Mapping from tool data to UI fields */\n mapping: ToolSettingsListMapping\n /** Param name for search query (default: \"query\") */\n searchParam?: string\n /** Param name for limit (default: \"limit\") */\n limitParam?: string\n /** Param name for get/delete ID (default: \"id\") */\n idParam?: string\n /** Static params always sent to list tool */\n listParams?: Record<string, unknown>\n}\n\n/**\n * Mapping from tool list data to UI fields\n */\nexport interface ToolSettingsListMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for total count in tool result data */\n countKey?: string\n /** Key for item ID */\n idKey: string\n /** Key for item label */\n labelKey: string\n /** Key for item description */\n descriptionKey?: string\n /** Key for secondary label */\n secondaryKey?: string\n}\n\n/**\n * Component-based tool settings view using the declarative DSL.\n * Reuses the same structure as PanelComponentView for consistency.\n */\nexport interface ToolSettingsComponentView {\n /** View kind */\n kind: 'component'\n /** Data sources. Keys become scope variables (e.g., \"$settings\"). */\n data?: Record<string, ToolSettingsActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Action-based data source for tool settings.\n */\nexport interface ToolSettingsActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that trigger refresh */\n refreshOn?: string[]\n}\n\n/**\n * Panel definition for right panel views\n */\nexport interface PanelDefinition {\n /** Unique panel ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Icon name (from huge-icons) */\n icon?: string\n /** Panel view schema */\n view: PanelView\n}\n\n/**\n * Panel view schema (declarative)\n */\nexport type PanelView = PanelComponentView | PanelUnknownView\n\nexport interface PanelUnknownView {\n /** View kind */\n kind: string\n /** Additional view configuration */\n [key: string]: unknown\n}\n\n/**\n * Action-based data source for declarative panels.\n * Uses actions (not tools) to fetch data.\n */\nexport interface PanelActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that should trigger a refresh of this data */\n refreshOn?: string[]\n}\n\n/**\n * Component-based panel view using the declarative DSL.\n * Data is fetched via actions, content is rendered via ExtensionComponent.\n */\nexport interface PanelComponentView {\n kind: 'component'\n /** Data sources available in the panel. Keys become variable names (e.g., \"$projects\"). */\n data?: Record<string, PanelActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Provider definition (metadata only, implementation in code)\n */\nexport interface ProviderDefinition {\n /** Provider ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Suggested default model when creating a new model configuration */\n suggestedDefaultModel?: string\n /** Default settings for this provider (e.g., { url: \"http://localhost:11434\" }) */\n defaultSettings?: Record<string, unknown>\n /** Schema for provider-specific configuration UI */\n configSchema?: ProviderConfigSchema\n}\n\n// ============================================================================\n// Prompt Contributions\n// ============================================================================\n\nexport type PromptSection = 'system' | 'behavior' | 'tools'\n\nexport interface PromptContribution {\n /** Unique ID within the extension */\n id: string\n /** Optional title for the prompt chunk */\n title?: string\n /** Prompt section placement */\n section?: PromptSection\n /** Plain text prompt content */\n text?: string\n /** Optional localized prompt content (keyed by locale, e.g. \"en\", \"sv\") */\n i18n?: Record<string, string>\n /** Optional ordering hint (lower comes first) */\n order?: number\n}\n\n// ============================================================================\n// Provider Configuration Schema\n// ============================================================================\n\n/**\n * Schema for provider-specific configuration.\n * Used to generate UI forms for configuring provider settings.\n */\nexport interface ProviderConfigSchema {\n /** Property definitions */\n properties: Record<string, ProviderConfigProperty>\n /** Display order of properties in UI (optional, defaults to object key order) */\n order?: string[]\n}\n\n/**\n * Property types for provider configuration\n */\nexport type ProviderConfigPropertyType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'password'\n | 'url'\n\n/**\n * Single property in a provider configuration schema.\n * Defines how a setting should be rendered and validated in the UI.\n */\nexport interface ProviderConfigProperty {\n /** Property type - determines UI control */\n type: ProviderConfigPropertyType\n /** Display label */\n title: string\n /** Help text shown below the input */\n description?: string\n /** Default value */\n default?: unknown\n /** Whether the field is required */\n required?: boolean\n /** Placeholder text for input fields */\n placeholder?: string\n /** For 'select' type: static options */\n options?: ProviderConfigSelectOption[]\n /** Validation rules */\n validation?: ProviderConfigValidation\n}\n\n/**\n * Option for select-type properties\n */\nexport interface ProviderConfigSelectOption {\n /** Value stored in settings */\n value: string\n /** Display label */\n label: string\n}\n\n/**\n * Validation rules for a property\n */\nexport interface ProviderConfigValidation {\n /** Regex pattern the value must match */\n pattern?: string\n /** Minimum string length */\n minLength?: number\n /** Maximum string length */\n maxLength?: number\n /** Minimum number value */\n min?: number\n /** Maximum number value */\n max?: number\n}\n\n/**\n * Tool definition (metadata only, implementation in code)\n */\nexport interface ToolDefinition {\n /** Tool ID */\n id: string\n /**\n * Display name - can be a simple string or localized strings.\n * @example \"Get Weather\"\n * @example { en: \"Get Weather\", sv: \"Hämta väder\" }\n */\n name: LocalizedString\n /**\n * Description for Stina - can be a simple string or localized strings.\n * Note: The AI always receives the English description (or fallback) for consistency.\n * Localized descriptions are used for UI display only.\n * @example \"Fetches current weather for a location\"\n * @example { en: \"Fetches current weather\", sv: \"Hämtar aktuellt väder\" }\n */\n description: LocalizedString\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Command definition\n */\nexport interface CommandDefinition {\n /** Command ID (e.g., \"weather\" for /weather) */\n id: string\n /** Display name */\n name: string\n /** Description */\n description: string\n}\n\n// ============================================================================\n// Permissions\n// ============================================================================\n\nexport type Permission =\n | NetworkPermission\n | StoragePermission\n | UserDataPermission\n | CapabilityPermission\n | SystemPermission\n\n/** Network access permissions */\nexport type NetworkPermission =\n | 'network:*'\n | `network:localhost`\n | `network:localhost:${number}`\n | `network:${string}`\n\n/** Storage permissions */\nexport type StoragePermission = 'database.own' | 'storage.local'\n\n/** User data permissions */\nexport type UserDataPermission =\n | 'user.profile.read'\n | 'user.location.read'\n | 'chat.history.read'\n | 'chat.current.read'\n\n/** Capability permissions */\nexport type CapabilityPermission =\n | 'provider.register'\n | 'tools.register'\n | 'actions.register'\n | 'settings.register'\n | 'commands.register'\n | 'panels.register'\n | 'events.emit'\n | 'scheduler.register'\n | 'chat.message.write'\n\n/** System permissions */\nexport type SystemPermission =\n | 'files.read'\n | 'files.write'\n | 'clipboard.read'\n | 'clipboard.write'\n\n// ============================================================================\n// Extension Context (API available to extensions)\n// ============================================================================\n\n/**\n * Disposable resource that can be cleaned up\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * Context provided to extension's activate function.\n *\n * ## User ID Context\n *\n * The `userId` field provides the current user context for extensions. It is set when:\n * - A tool is executed by a user\n * - An action is executed by a user\n * - A scheduled job fires (if the job was created with a userId)\n *\n * For extension activation, `userId` is undefined since activation happens at system level.\n * Extensions should check for `userId` in their tool/action handlers to access user-specific data.\n *\n * @example\n * ```typescript\n * // In a tool execute handler:\n * execute: async (params, context) => {\n * if (context.userId) {\n * // User-specific logic\n * const userData = await storage.getForUser(context.userId, 'preferences')\n * }\n * }\n * ```\n */\nexport interface ExtensionContext {\n /** Extension metadata */\n readonly extension: {\n readonly id: string\n readonly version: string\n readonly storagePath: string\n }\n\n /**\n * Current user ID if in a user context, undefined for global/system operations.\n * Set when tools or actions are executed by a user, or when a user-scoped job fires.\n */\n readonly userId?: string\n\n /** Network access (if permitted) */\n readonly network?: NetworkAPI\n\n /** Settings access (if permitted) */\n readonly settings?: SettingsAPI\n\n /** Provider registration (if permitted) */\n readonly providers?: ProvidersAPI\n\n /** Tool registration (if permitted) */\n readonly tools?: ToolsAPI\n\n /** Action registration (if permitted) */\n readonly actions?: ActionsAPI\n\n /** Event emission (if permitted) */\n readonly events?: EventsAPI\n\n /** Scheduler access (if permitted) */\n readonly scheduler?: SchedulerAPI\n\n /** User data access (if permitted) */\n readonly user?: UserAPI\n\n /** Chat access (if permitted) */\n readonly chat?: ChatAPI\n\n /** Database access (if permitted) */\n readonly database?: DatabaseAPI\n\n /** Local storage (if permitted) */\n readonly storage?: StorageAPI\n\n /** Logging (always available) */\n readonly log: LogAPI\n}\n\n/**\n * Network API for making HTTP requests\n */\nexport interface NetworkAPI {\n /**\n * Fetch a URL (permissions are enforced by host)\n */\n fetch(url: string, options?: RequestInit): Promise<Response>\n\n /**\n * Streaming fetch for responses like NDJSON or SSE.\n * Yields text chunks as they arrive from the server.\n *\n * @throws {Error} If the request fails or encounters a network error.\n * The error message will contain details about the failure.\n *\n * @example\n * ```typescript\n * try {\n * for await (const chunk of context.network.fetchStream(url, options)) {\n * // Process each chunk (may contain partial lines)\n * buffer += chunk\n * }\n * } catch (error) {\n * console.error('Streaming fetch failed:', error.message)\n * }\n * ```\n */\n fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown>\n}\n\n/**\n * Settings API for reading/writing extension settings\n */\nexport interface SettingsAPI {\n /**\n * Get all settings for this extension\n */\n getAll<T extends Record<string, unknown>>(): Promise<T>\n\n /**\n * Get a specific setting value\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a setting value\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Listen for setting changes\n */\n onChange(callback: (key: string, value: unknown) => void): Disposable\n}\n\n/**\n * Providers API for registering AI providers\n */\nexport interface ProvidersAPI {\n /**\n * Register an AI provider\n */\n register(provider: AIProvider): Disposable\n}\n\n/**\n * Tools API for registering tools\n */\nexport interface ToolsAPI {\n /**\n * Register a tool that Stina can use\n */\n register(tool: Tool): Disposable\n}\n\n/**\n * Actions API for registering UI actions\n */\nexport interface ActionsAPI {\n /**\n * Register an action that UI components can invoke\n */\n register(action: Action): Disposable\n}\n\n/**\n * Events API for notifying the host\n */\nexport interface EventsAPI {\n /**\n * Emit a named event with optional payload\n */\n emit(name: string, payload?: Record<string, unknown>): Promise<void>\n}\n\n/**\n * Scheduler schedule types\n */\nexport type SchedulerSchedule =\n | { type: 'at'; at: string }\n | { type: 'cron'; cron: string; timezone?: string }\n | { type: 'interval'; everyMs: number }\n\n/**\n * Scheduler job request\n */\nexport interface SchedulerJobRequest {\n id: string\n schedule: SchedulerSchedule\n payload?: Record<string, unknown>\n misfire?: 'run_once' | 'skip'\n /**\n * Optional user ID for user-scoped jobs.\n * If set, the job is associated with a specific user and the userId\n * will be passed to the extension when the job fires.\n */\n userId?: string\n}\n\n/**\n * Scheduler fire payload\n */\nexport interface SchedulerFirePayload {\n id: string\n payload?: Record<string, unknown>\n scheduledFor: string\n firedAt: string\n delayMs: number\n /** User ID if this is a user-scoped job, undefined if global */\n userId?: string\n}\n\n/**\n * Scheduler API for registering jobs\n */\nexport interface SchedulerAPI {\n schedule(job: SchedulerJobRequest): Promise<void>\n cancel(jobId: string): Promise<void>\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable\n}\n\n/**\n * User profile data\n */\nexport interface UserProfile {\n firstName?: string\n nickname?: string\n language?: string\n timezone?: string\n}\n\n/**\n * User API for profile access\n */\nexport interface UserAPI {\n getProfile(): Promise<UserProfile>\n}\n\n/**\n * Chat instruction message\n */\nexport interface ChatInstructionMessage {\n text: string\n conversationId?: string\n}\n\n/**\n * Chat API for appending instructions\n */\nexport interface ChatAPI {\n appendInstruction(message: ChatInstructionMessage): Promise<void>\n}\n\n/**\n * Database API for extension-specific tables\n */\nexport interface DatabaseAPI {\n /**\n * Execute a SQL query (only extension's prefixed tables allowed)\n */\n execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>\n}\n\n/**\n * Simple key-value storage API with support for user-scoped storage.\n *\n * ## Global vs User-Scoped Storage\n *\n * Extensions have access to two types of storage:\n * - **Global storage**: Shared across all users, accessed via `get()`, `set()`, etc.\n * - **User-scoped storage**: Isolated per user, accessed via `getForUser()`, `setForUser()`, etc.\n *\n * Use global storage for extension-wide settings and user-scoped storage for\n * user preferences, session data, or any data that should be private to a user.\n *\n * @example\n * ```typescript\n * // Global storage (extension-wide)\n * await storage.set('apiEndpoint', 'https://api.example.com')\n * const endpoint = await storage.get<string>('apiEndpoint')\n *\n * // User-scoped storage (per-user)\n * if (context.userId) {\n * await storage.setForUser(context.userId, 'preferences', { theme: 'dark' })\n * const prefs = await storage.getForUser<Preferences>(context.userId, 'preferences')\n * }\n * ```\n */\nexport interface StorageAPI {\n /**\n * Get a value by key (global/extension-scoped)\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a value (global/extension-scoped)\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key (global/extension-scoped)\n */\n delete(key: string): Promise<void>\n\n /**\n * Get all keys (global/extension-scoped)\n */\n keys(): Promise<string[]>\n\n /**\n * Get a value by key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n getForUser<T>(userId: string, key: string): Promise<T | undefined>\n\n /**\n * Set a value for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n * @param value The value to store\n */\n setForUser(userId: string, key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n deleteForUser(userId: string, key: string): Promise<void>\n\n /**\n * Get all keys for a specific user (user-scoped)\n * @param userId The user ID\n */\n keysForUser(userId: string): Promise<string[]>\n}\n\n/**\n * Logging API\n */\nexport interface LogAPI {\n debug(message: string, data?: Record<string, unknown>): void\n info(message: string, data?: Record<string, unknown>): void\n warn(message: string, data?: Record<string, unknown>): void\n error(message: string, data?: Record<string, unknown>): void\n}\n\n// ============================================================================\n// AI Provider Types\n// ============================================================================\n\n/**\n * AI provider implementation\n */\nexport interface AIProvider {\n /** Provider ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n\n /**\n * Get available models from this provider\n * @param options Optional settings for the provider (e.g., URL)\n */\n getModels(options?: GetModelsOptions): Promise<ModelInfo[]>\n\n /**\n * Chat completion with streaming\n */\n chat(\n messages: ChatMessage[],\n options: ChatOptions\n ): AsyncGenerator<StreamEvent, void, unknown>\n\n /**\n * Optional: Generate embeddings\n */\n embed?(texts: string[]): Promise<number[][]>\n}\n\n/**\n * Model information\n */\nexport interface ModelInfo {\n /** Model ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Context window size */\n contextLength?: number\n}\n\n/**\n * Chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n content: string\n /** For assistant messages: tool calls made by the model */\n tool_calls?: ToolCall[]\n /** For tool messages: the ID of the tool call this is a response to */\n tool_call_id?: string\n}\n\n/**\n * A tool call made by the model\n */\nexport interface ToolCall {\n /** Unique ID for this tool call */\n id: string\n /** Tool name/ID to invoke */\n name: string\n /** Arguments for the tool (as parsed object) */\n arguments: Record<string, unknown>\n}\n\n/**\n * Options for chat completion\n */\nexport interface ChatOptions {\n /** Model to use */\n model?: string\n /** Temperature (0-1) */\n temperature?: number\n /** Maximum tokens to generate */\n maxTokens?: number\n /** Abort signal for cancellation */\n signal?: AbortSignal\n /** Provider-specific settings from model configuration */\n settings?: Record<string, unknown>\n /** Available tools for this request */\n tools?: ToolDefinition[]\n}\n\n/**\n * Options for getModels\n */\nexport interface GetModelsOptions {\n /** Provider-specific settings (e.g., URL for Ollama) */\n settings?: Record<string, unknown>\n}\n\n/**\n * Streaming events from chat\n */\nexport type StreamEvent =\n | { type: 'content'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'tool_start'; name: string; input: unknown; toolCallId: string }\n | { type: 'tool_end'; name: string; output: unknown; toolCallId: string }\n | { type: 'done'; usage?: { inputTokens: number; outputTokens: number } }\n | { type: 'error'; message: string }\n\n// ============================================================================\n// Tool Types\n// ============================================================================\n\n/**\n * Tool implementation\n */\nexport interface Tool {\n /** Tool ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n /** Description for Stina */\n description: string\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n\n /**\n * Execute the tool\n */\n execute(params: Record<string, unknown>): Promise<ToolResult>\n}\n\n/**\n * Tool execution result\n */\nexport interface ToolResult {\n /** Whether the tool succeeded */\n success: boolean\n /** Result data (for Stina to use) */\n data?: unknown\n /** Human-readable message */\n message?: string\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Action Types (for UI interactions, separate from Tools)\n// ============================================================================\n\n/**\n * Action implementation for UI interactions.\n * Actions are invoked by UI components, not by Stina (AI).\n */\nexport interface Action {\n /** Action ID (unique within the extension) */\n id: string\n\n /**\n * Execute the action\n * @param params Parameters from the UI component (with $-values already resolved)\n */\n execute(params: Record<string, unknown>): Promise<ActionResult>\n}\n\n/**\n * Action execution result\n */\nexport interface ActionResult {\n /** Whether the action succeeded */\n success: boolean\n /** Result data (returned to UI) */\n data?: unknown\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Extension Entry Point\n// ============================================================================\n\n/**\n * Extension entry point interface\n */\nexport interface ExtensionModule {\n /**\n * Called when extension is activated\n */\n activate(context: ExtensionContext): void | Disposable | Promise<void | Disposable>\n\n /**\n * Called when extension is deactivated\n */\n deactivate?(): void | Promise<void>\n}\n"],"mappings":";;;;;AAsBO,SAAS,uBACd,OACA,MACA,eAAe,MACP;AACR,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,IAAI,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,KAAK,EAAE,CAAC,KAAK;AAC1E;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * A string that can be either a simple string or a map of language codes to localized strings.\n * When a simple string is provided, it's used as the default/fallback value.\n * When a map is provided, the appropriate language is selected at runtime.\n *\n * @example\n * // Simple string (backwards compatible)\n * name: \"Get Weather\"\n *\n * @example\n * // Localized strings\n * name: { en: \"Get Weather\", sv: \"Hämta väder\", de: \"Wetter abrufen\" }\n */\nexport type LocalizedString = string | Record<string, string>\n\n/**\n * Resolves a LocalizedString to an actual string value.\n * @param value The LocalizedString to resolve\n * @param lang The preferred language code (e.g., \"sv\", \"en\")\n * @param fallbackLang The fallback language code (defaults to \"en\")\n * @returns The resolved string value\n */\nexport function resolveLocalizedString(\n value: LocalizedString,\n lang: string,\n fallbackLang = 'en'\n): string {\n if (typeof value === 'string') {\n return value\n }\n // Try preferred language first, then fallback language, then first available, then empty string\n return value[lang] ?? value[fallbackLang] ?? Object.values(value)[0] ?? ''\n}\n\n/**\n * Extension manifest format (manifest.json)\n */\nexport interface ExtensionManifest {\n /** Unique identifier (e.g., \"ollama-provider\") */\n id: string\n /** Human-readable name */\n name: string\n /** Version string (semver) */\n version: string\n /** Short description */\n description: string\n /** Author information */\n author: {\n name: string\n url?: string\n }\n /** Repository URL */\n repository?: string\n /** License identifier */\n license?: string\n /** Minimum Stina version required */\n engines?: {\n stina: string\n }\n /** Supported platforms */\n platforms?: Platform[]\n /** Entry point file (relative to extension root) */\n main: string\n /** Required permissions */\n permissions: Permission[]\n /** What the extension contributes */\n contributes?: ExtensionContributions\n}\n\nexport type Platform = 'web' | 'electron' | 'tui'\n\n/**\n * What an extension can contribute to Stina\n */\nexport interface ExtensionContributions {\n /** User-configurable settings */\n settings?: SettingDefinition[]\n /** Tool settings views for UI */\n toolSettings?: ToolSettingsViewDefinition[]\n /** Right panel contributions */\n panels?: PanelDefinition[]\n /** AI providers */\n providers?: ProviderDefinition[]\n /** Tools for Stina to use */\n tools?: ToolDefinition[]\n /** Slash commands */\n commands?: CommandDefinition[]\n /** Prompt contributions for the system prompt */\n prompts?: PromptContribution[]\n}\n\n/**\n * Setting definition for the UI\n */\nexport interface SettingDefinition {\n /** Setting ID (namespaced automatically) */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** Setting type */\n type: 'string' | 'number' | 'boolean' | 'select'\n /** Default value */\n default?: unknown\n /** For select type: available options */\n options?: { value: string; label: string }[]\n /** For select type: load options from tool */\n optionsToolId?: string\n /** Params for options tool */\n optionsParams?: Record<string, unknown>\n /** Mapping for options tool response */\n optionsMapping?: SettingOptionsMapping\n /** Tool ID for creating a new option */\n createToolId?: string\n /** Label for create action */\n createLabel?: string\n /** Fields for create form */\n createFields?: SettingDefinition[]\n /** Static params always sent to create tool */\n createParams?: Record<string, unknown>\n /** Mapping for create tool response */\n createMapping?: SettingCreateMapping\n /** Validation rules */\n validation?: {\n required?: boolean\n min?: number\n max?: number\n pattern?: string\n }\n}\n\n/**\n * Mapping for select field options from tool response\n */\nexport interface SettingOptionsMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for option value */\n valueKey: string\n /** Key for option label */\n labelKey: string\n /** Optional key for description */\n descriptionKey?: string\n}\n\n/**\n * Mapping for create tool response\n */\nexport interface SettingCreateMapping {\n /** Key for result data object */\n resultKey?: string\n /** Key for option value (defaults to \"id\") */\n valueKey: string\n}\n\n/**\n * Tool settings view definition (UI schema)\n */\nexport interface ToolSettingsViewDefinition {\n /** Unique view ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** View configuration */\n view: ToolSettingsView\n /** Fields for create/edit forms (uses SettingDefinition) */\n fields?: SettingDefinition[]\n}\n\n/**\n * Tool settings view types\n */\nexport type ToolSettingsView = ToolSettingsListView | ToolSettingsComponentView\n\n/**\n * List view backed by tools\n */\nexport interface ToolSettingsListView {\n /** View kind */\n kind: 'list'\n /** Tool ID for listing items */\n listToolId: string\n /** Tool ID for fetching details (optional) */\n getToolId?: string\n /** Tool ID for creating/updating items (optional) */\n upsertToolId?: string\n /** Tool ID for deleting items (optional) */\n deleteToolId?: string\n /** Mapping from tool data to UI fields */\n mapping: ToolSettingsListMapping\n /** Param name for search query (default: \"query\") */\n searchParam?: string\n /** Param name for limit (default: \"limit\") */\n limitParam?: string\n /** Param name for get/delete ID (default: \"id\") */\n idParam?: string\n /** Static params always sent to list tool */\n listParams?: Record<string, unknown>\n}\n\n/**\n * Mapping from tool list data to UI fields\n */\nexport interface ToolSettingsListMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for total count in tool result data */\n countKey?: string\n /** Key for item ID */\n idKey: string\n /** Key for item label */\n labelKey: string\n /** Key for item description */\n descriptionKey?: string\n /** Key for secondary label */\n secondaryKey?: string\n}\n\n/**\n * Component-based tool settings view using the declarative DSL.\n * Reuses the same structure as PanelComponentView for consistency.\n */\nexport interface ToolSettingsComponentView {\n /** View kind */\n kind: 'component'\n /** Data sources. Keys become scope variables (e.g., \"$settings\"). */\n data?: Record<string, ToolSettingsActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Action-based data source for tool settings.\n */\nexport interface ToolSettingsActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that trigger refresh */\n refreshOn?: string[]\n}\n\n/**\n * Panel definition for right panel views\n */\nexport interface PanelDefinition {\n /** Unique panel ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Icon name (from huge-icons) */\n icon?: string\n /** Panel view schema */\n view: PanelView\n}\n\n/**\n * Panel view schema (declarative)\n */\nexport type PanelView = PanelComponentView | PanelUnknownView\n\nexport interface PanelUnknownView {\n /** View kind */\n kind: string\n /** Additional view configuration */\n [key: string]: unknown\n}\n\n/**\n * Action-based data source for declarative panels.\n * Uses actions (not tools) to fetch data.\n */\nexport interface PanelActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that should trigger a refresh of this data */\n refreshOn?: string[]\n}\n\n/**\n * Component-based panel view using the declarative DSL.\n * Data is fetched via actions, content is rendered via ExtensionComponent.\n */\nexport interface PanelComponentView {\n kind: 'component'\n /** Data sources available in the panel. Keys become variable names (e.g., \"$projects\"). */\n data?: Record<string, PanelActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Provider definition (metadata only, implementation in code)\n */\nexport interface ProviderDefinition {\n /** Provider ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Suggested default model when creating a new model configuration */\n suggestedDefaultModel?: string\n /** Default settings for this provider (e.g., { url: \"http://localhost:11434\" }) */\n defaultSettings?: Record<string, unknown>\n /** Schema for provider-specific configuration UI */\n configSchema?: ProviderConfigSchema\n}\n\n// ============================================================================\n// Prompt Contributions\n// ============================================================================\n\nexport type PromptSection = 'system' | 'behavior' | 'tools'\n\nexport interface PromptContribution {\n /** Unique ID within the extension */\n id: string\n /** Optional title for the prompt chunk */\n title?: string\n /** Prompt section placement */\n section?: PromptSection\n /** Plain text prompt content */\n text?: string\n /** Optional localized prompt content (keyed by locale, e.g. \"en\", \"sv\") */\n i18n?: Record<string, string>\n /** Optional ordering hint (lower comes first) */\n order?: number\n}\n\n// ============================================================================\n// Provider Configuration Schema\n// ============================================================================\n\n/**\n * Schema for provider-specific configuration.\n * Used to generate UI forms for configuring provider settings.\n */\nexport interface ProviderConfigSchema {\n /** Property definitions */\n properties: Record<string, ProviderConfigProperty>\n /** Display order of properties in UI (optional, defaults to object key order) */\n order?: string[]\n}\n\n/**\n * Property types for provider configuration\n */\nexport type ProviderConfigPropertyType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'password'\n | 'url'\n\n/**\n * Single property in a provider configuration schema.\n * Defines how a setting should be rendered and validated in the UI.\n */\nexport interface ProviderConfigProperty {\n /** Property type - determines UI control */\n type: ProviderConfigPropertyType\n /** Display label */\n title: string\n /** Help text shown below the input */\n description?: string\n /** Default value */\n default?: unknown\n /** Whether the field is required */\n required?: boolean\n /** Placeholder text for input fields */\n placeholder?: string\n /** For 'select' type: static options */\n options?: ProviderConfigSelectOption[]\n /** Validation rules */\n validation?: ProviderConfigValidation\n}\n\n/**\n * Option for select-type properties\n */\nexport interface ProviderConfigSelectOption {\n /** Value stored in settings */\n value: string\n /** Display label */\n label: string\n}\n\n/**\n * Validation rules for a property\n */\nexport interface ProviderConfigValidation {\n /** Regex pattern the value must match */\n pattern?: string\n /** Minimum string length */\n minLength?: number\n /** Maximum string length */\n maxLength?: number\n /** Minimum number value */\n min?: number\n /** Maximum number value */\n max?: number\n}\n\n/**\n * Tool definition (metadata only, implementation in code)\n */\nexport interface ToolDefinition {\n /** Tool ID */\n id: string\n /**\n * Display name - can be a simple string or localized strings.\n * @example \"Get Weather\"\n * @example { en: \"Get Weather\", sv: \"Hämta väder\" }\n */\n name: LocalizedString\n /**\n * Description for Stina - can be a simple string or localized strings.\n * Note: The AI always receives the English description (or fallback) for consistency.\n * Localized descriptions are used for UI display only.\n * @example \"Fetches current weather for a location\"\n * @example { en: \"Fetches current weather\", sv: \"Hämtar aktuellt väder\" }\n */\n description: LocalizedString\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Command definition\n */\nexport interface CommandDefinition {\n /** Command ID (e.g., \"weather\" for /weather) */\n id: string\n /** Display name */\n name: string\n /** Description */\n description: string\n}\n\n// ============================================================================\n// Permissions\n// ============================================================================\n\nexport type Permission =\n | NetworkPermission\n | StoragePermission\n | UserDataPermission\n | CapabilityPermission\n | SystemPermission\n\n/** Network access permissions */\nexport type NetworkPermission =\n | 'network:*'\n | `network:localhost`\n | `network:localhost:${number}`\n | `network:${string}`\n\n/** Storage permissions */\nexport type StoragePermission = 'database.own' | 'storage.local'\n\n/** User data permissions */\nexport type UserDataPermission =\n | 'user.profile.read'\n | 'user.location.read'\n | 'chat.history.read'\n | 'chat.current.read'\n\n/** Capability permissions */\nexport type CapabilityPermission =\n | 'provider.register'\n | 'tools.register'\n | 'actions.register'\n | 'settings.register'\n | 'commands.register'\n | 'panels.register'\n | 'events.emit'\n | 'scheduler.register'\n | 'chat.message.write'\n\n/** System permissions */\nexport type SystemPermission =\n | 'files.read'\n | 'files.write'\n | 'clipboard.read'\n | 'clipboard.write'\n\n// ============================================================================\n// Extension Context (API available to extensions)\n// ============================================================================\n\n/**\n * Disposable resource that can be cleaned up\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * Context provided to extension's activate function.\n *\n * ## User ID Context\n *\n * The `userId` field provides the current user context for extensions. It is set when:\n * - A tool is executed by a user\n * - An action is executed by a user\n * - A scheduled job fires (if the job was created with a userId)\n *\n * For extension activation, `userId` is undefined since activation happens at system level.\n * Extensions should check for `userId` in their tool/action handlers to access user-specific data.\n *\n * @example\n * ```typescript\n * // In a tool execute handler:\n * execute: async (params, context) => {\n * if (context.userId) {\n * // User-specific logic\n * const userData = await storage.getForUser(context.userId, 'preferences')\n * }\n * }\n * ```\n */\nexport interface ExtensionContext {\n /** Extension metadata */\n readonly extension: {\n readonly id: string\n readonly version: string\n readonly storagePath: string\n }\n\n /**\n * Current user ID if in a user context, undefined for global/system operations.\n * Set when tools or actions are executed by a user, or when a user-scoped job fires.\n */\n readonly userId?: string\n\n /** Network access (if permitted) */\n readonly network?: NetworkAPI\n\n /** Settings access (if permitted) */\n readonly settings?: SettingsAPI\n\n /** Provider registration (if permitted) */\n readonly providers?: ProvidersAPI\n\n /** Tool registration (if permitted) */\n readonly tools?: ToolsAPI\n\n /** Action registration (if permitted) */\n readonly actions?: ActionsAPI\n\n /** Event emission (if permitted) */\n readonly events?: EventsAPI\n\n /** Scheduler access (if permitted) */\n readonly scheduler?: SchedulerAPI\n\n /** User data access (if permitted) */\n readonly user?: UserAPI\n\n /** Chat access (if permitted) */\n readonly chat?: ChatAPI\n\n /** Database access (if permitted) */\n readonly database?: DatabaseAPI\n\n /** Local storage (if permitted) */\n readonly storage?: StorageAPI\n\n /** Logging (always available) */\n readonly log: LogAPI\n}\n\n/**\n * Network API for making HTTP requests\n */\nexport interface NetworkAPI {\n /**\n * Fetch a URL (permissions are enforced by host)\n */\n fetch(url: string, options?: RequestInit): Promise<Response>\n\n /**\n * Streaming fetch for responses like NDJSON or SSE.\n * Yields text chunks as they arrive from the server.\n *\n * @throws {Error} If the request fails or encounters a network error.\n * The error message will contain details about the failure.\n *\n * @example\n * ```typescript\n * try {\n * for await (const chunk of context.network.fetchStream(url, options)) {\n * // Process each chunk (may contain partial lines)\n * buffer += chunk\n * }\n * } catch (error) {\n * console.error('Streaming fetch failed:', error.message)\n * }\n * ```\n */\n fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown>\n}\n\n/**\n * Settings API for reading/writing extension settings\n */\nexport interface SettingsAPI {\n /**\n * Get all settings for this extension\n */\n getAll<T extends Record<string, unknown>>(): Promise<T>\n\n /**\n * Get a specific setting value\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a setting value\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Listen for setting changes\n */\n onChange(callback: (key: string, value: unknown) => void): Disposable\n}\n\n/**\n * Providers API for registering AI providers\n */\nexport interface ProvidersAPI {\n /**\n * Register an AI provider\n */\n register(provider: AIProvider): Disposable\n}\n\n/**\n * Tools API for registering tools\n */\nexport interface ToolsAPI {\n /**\n * Register a tool that Stina can use\n */\n register(tool: Tool): Disposable\n}\n\n/**\n * Actions API for registering UI actions\n */\nexport interface ActionsAPI {\n /**\n * Register an action that UI components can invoke\n */\n register(action: Action): Disposable\n}\n\n/**\n * Events API for notifying the host\n */\nexport interface EventsAPI {\n /**\n * Emit a named event with optional payload\n */\n emit(name: string, payload?: Record<string, unknown>): Promise<void>\n}\n\n/**\n * Scheduler schedule types\n */\nexport type SchedulerSchedule =\n | { type: 'at'; at: string }\n | { type: 'cron'; cron: string; timezone?: string }\n | { type: 'interval'; everyMs: number }\n\n/**\n * Scheduler job request\n */\nexport interface SchedulerJobRequest {\n id: string\n schedule: SchedulerSchedule\n payload?: Record<string, unknown>\n misfire?: 'run_once' | 'skip'\n /**\n * Optional user ID for user-scoped jobs.\n * If set, the job is associated with a specific user and the userId\n * will be passed to the extension when the job fires.\n */\n userId?: string\n}\n\n/**\n * Scheduler fire payload\n */\nexport interface SchedulerFirePayload {\n id: string\n payload?: Record<string, unknown>\n scheduledFor: string\n firedAt: string\n delayMs: number\n /** User ID if this is a user-scoped job, undefined if global */\n userId?: string\n}\n\n/**\n * Scheduler API for registering jobs\n */\nexport interface SchedulerAPI {\n schedule(job: SchedulerJobRequest): Promise<void>\n cancel(jobId: string): Promise<void>\n onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable\n}\n\n/**\n * User profile data\n */\nexport interface UserProfile {\n firstName?: string\n nickname?: string\n language?: string\n timezone?: string\n}\n\n/**\n * User API for profile access\n */\nexport interface UserAPI {\n getProfile(): Promise<UserProfile>\n}\n\n/**\n * Chat instruction message\n */\nexport interface ChatInstructionMessage {\n text: string\n conversationId?: string\n userId?: string\n}\n\n/**\n * Chat API for appending instructions\n */\nexport interface ChatAPI {\n appendInstruction(message: ChatInstructionMessage): Promise<void>\n}\n\n/**\n * Database API for extension-specific tables\n */\nexport interface DatabaseAPI {\n /**\n * Execute a SQL query (only extension's prefixed tables allowed)\n */\n execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>\n}\n\n/**\n * Simple key-value storage API with support for user-scoped storage.\n *\n * ## Global vs User-Scoped Storage\n *\n * Extensions have access to two types of storage:\n * - **Global storage**: Shared across all users, accessed via `get()`, `set()`, etc.\n * - **User-scoped storage**: Isolated per user, accessed via `getForUser()`, `setForUser()`, etc.\n *\n * Use global storage for extension-wide settings and user-scoped storage for\n * user preferences, session data, or any data that should be private to a user.\n *\n * @example\n * ```typescript\n * // Global storage (extension-wide)\n * await storage.set('apiEndpoint', 'https://api.example.com')\n * const endpoint = await storage.get<string>('apiEndpoint')\n *\n * // User-scoped storage (per-user)\n * if (context.userId) {\n * await storage.setForUser(context.userId, 'preferences', { theme: 'dark' })\n * const prefs = await storage.getForUser<Preferences>(context.userId, 'preferences')\n * }\n * ```\n */\nexport interface StorageAPI {\n /**\n * Get a value by key (global/extension-scoped)\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a value (global/extension-scoped)\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key (global/extension-scoped)\n */\n delete(key: string): Promise<void>\n\n /**\n * Get all keys (global/extension-scoped)\n */\n keys(): Promise<string[]>\n\n /**\n * Get a value by key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n getForUser<T>(userId: string, key: string): Promise<T | undefined>\n\n /**\n * Set a value for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n * @param value The value to store\n */\n setForUser(userId: string, key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n deleteForUser(userId: string, key: string): Promise<void>\n\n /**\n * Get all keys for a specific user (user-scoped)\n * @param userId The user ID\n */\n keysForUser(userId: string): Promise<string[]>\n}\n\n/**\n * Logging API\n */\nexport interface LogAPI {\n debug(message: string, data?: Record<string, unknown>): void\n info(message: string, data?: Record<string, unknown>): void\n warn(message: string, data?: Record<string, unknown>): void\n error(message: string, data?: Record<string, unknown>): void\n}\n\n// ============================================================================\n// AI Provider Types\n// ============================================================================\n\n/**\n * AI provider implementation\n */\nexport interface AIProvider {\n /** Provider ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n\n /**\n * Get available models from this provider\n * @param options Optional settings for the provider (e.g., URL)\n */\n getModels(options?: GetModelsOptions): Promise<ModelInfo[]>\n\n /**\n * Chat completion with streaming\n */\n chat(\n messages: ChatMessage[],\n options: ChatOptions\n ): AsyncGenerator<StreamEvent, void, unknown>\n\n /**\n * Optional: Generate embeddings\n */\n embed?(texts: string[]): Promise<number[][]>\n}\n\n/**\n * Model information\n */\nexport interface ModelInfo {\n /** Model ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Context window size */\n contextLength?: number\n}\n\n/**\n * Chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n content: string\n /** For assistant messages: tool calls made by the model */\n tool_calls?: ToolCall[]\n /** For tool messages: the ID of the tool call this is a response to */\n tool_call_id?: string\n}\n\n/**\n * A tool call made by the model\n */\nexport interface ToolCall {\n /** Unique ID for this tool call */\n id: string\n /** Tool name/ID to invoke */\n name: string\n /** Arguments for the tool (as parsed object) */\n arguments: Record<string, unknown>\n}\n\n/**\n * Options for chat completion\n */\nexport interface ChatOptions {\n /** Model to use */\n model?: string\n /** Temperature (0-1) */\n temperature?: number\n /** Maximum tokens to generate */\n maxTokens?: number\n /** Abort signal for cancellation */\n signal?: AbortSignal\n /** Provider-specific settings from model configuration */\n settings?: Record<string, unknown>\n /** Available tools for this request */\n tools?: ToolDefinition[]\n}\n\n/**\n * Options for getModels\n */\nexport interface GetModelsOptions {\n /** Provider-specific settings (e.g., URL for Ollama) */\n settings?: Record<string, unknown>\n}\n\n/**\n * Streaming events from chat\n */\nexport type StreamEvent =\n | { type: 'content'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'tool_start'; name: string; input: unknown; toolCallId: string }\n | { type: 'tool_end'; name: string; output: unknown; toolCallId: string }\n | { type: 'done'; usage?: { inputTokens: number; outputTokens: number } }\n | { type: 'error'; message: string }\n\n// ============================================================================\n// Tool Types\n// ============================================================================\n\n/**\n * Tool implementation\n */\nexport interface Tool {\n /** Tool ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n /** Description for Stina */\n description: string\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n\n /**\n * Execute the tool\n */\n execute(params: Record<string, unknown>): Promise<ToolResult>\n}\n\n/**\n * Tool execution result\n */\nexport interface ToolResult {\n /** Whether the tool succeeded */\n success: boolean\n /** Result data (for Stina to use) */\n data?: unknown\n /** Human-readable message */\n message?: string\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Action Types (for UI interactions, separate from Tools)\n// ============================================================================\n\n/**\n * Action implementation for UI interactions.\n * Actions are invoked by UI components, not by Stina (AI).\n */\nexport interface Action {\n /** Action ID (unique within the extension) */\n id: string\n\n /**\n * Execute the action\n * @param params Parameters from the UI component (with $-values already resolved)\n */\n execute(params: Record<string, unknown>): Promise<ActionResult>\n}\n\n/**\n * Action execution result\n */\nexport interface ActionResult {\n /** Whether the action succeeded */\n success: boolean\n /** Result data (returned to UI) */\n data?: unknown\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Extension Entry Point\n// ============================================================================\n\n/**\n * Extension entry point interface\n */\nexport interface ExtensionModule {\n /**\n * Called when extension is activated\n */\n activate(context: ExtensionContext): void | Disposable | Promise<void | Disposable>\n\n /**\n * Called when extension is deactivated\n */\n deactivate?(): void | Promise<void>\n}\n"],"mappings":";;;;;AAsBO,SAAS,uBACd,OACA,MACA,eAAe,MACP;AACR,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,IAAI,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,KAAK,EAAE,CAAC,KAAK;AAC1E;","names":[]}
|
package/dist/runtime.cjs
CHANGED
|
@@ -90,7 +90,7 @@ async function handleHostMessage(message) {
|
|
|
90
90
|
handleSettingsChanged(message.payload.key, message.payload.value);
|
|
91
91
|
break;
|
|
92
92
|
case "scheduler-fire":
|
|
93
|
-
handleSchedulerFire(message.payload);
|
|
93
|
+
await handleSchedulerFire(message.payload);
|
|
94
94
|
break;
|
|
95
95
|
case "provider-chat-request":
|
|
96
96
|
await handleProviderChatRequest(message.id, message.payload);
|
|
@@ -192,18 +192,19 @@ function handleSettingsChanged(key, value) {
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
|
-
function handleSchedulerFire(payload) {
|
|
195
|
+
async function handleSchedulerFire(payload) {
|
|
196
196
|
if (extensionContext && payload.userId) {
|
|
197
197
|
;
|
|
198
198
|
extensionContext.userId = payload.userId;
|
|
199
199
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
200
|
+
const results = await Promise.allSettled(
|
|
201
|
+
schedulerCallbacks.map((callback) => callback(payload))
|
|
202
|
+
);
|
|
203
|
+
results.forEach((result, index) => {
|
|
204
|
+
if (result.status === "rejected") {
|
|
205
|
+
console.error(`Error in scheduler callback ${index}:`, result.reason);
|
|
205
206
|
}
|
|
206
|
-
}
|
|
207
|
+
});
|
|
207
208
|
if (extensionContext) {
|
|
208
209
|
;
|
|
209
210
|
extensionContext.userId = void 0;
|
|
@@ -548,7 +549,9 @@ function buildContext(extensionId, extensionVersion, storagePath, permissions) {
|
|
|
548
549
|
if (hasPermission("scheduler.register")) {
|
|
549
550
|
const schedulerApi = {
|
|
550
551
|
async schedule(job) {
|
|
551
|
-
|
|
552
|
+
const userId = extensionContext.userId;
|
|
553
|
+
const jobWithUser = userId && !job.userId ? { ...job, userId } : job;
|
|
554
|
+
await sendRequest("scheduler.schedule", { job: jobWithUser });
|
|
552
555
|
},
|
|
553
556
|
async cancel(jobId) {
|
|
554
557
|
await sendRequest("scheduler.cancel", { jobId });
|
|
@@ -576,7 +579,11 @@ function buildContext(extensionId, extensionVersion, storagePath, permissions) {
|
|
|
576
579
|
if (hasPermission("chat.message.write")) {
|
|
577
580
|
const chatApi = {
|
|
578
581
|
async appendInstruction(message) {
|
|
579
|
-
|
|
582
|
+
const contextUserId = extensionContext.userId;
|
|
583
|
+
await sendRequest("chat.appendInstruction", {
|
|
584
|
+
...message,
|
|
585
|
+
userId: message.userId ?? contextUserId
|
|
586
|
+
});
|
|
580
587
|
}
|
|
581
588
|
};
|
|
582
589
|
context.chat = chatApi;
|
package/dist/runtime.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime.ts","../src/messages.ts"],"sourcesContent":["/**\n * Extension Runtime - Runs inside the worker\n *\n * This module handles communication with the Extension Host and provides\n * the ExtensionContext to the extension's activate function.\n */\n\nimport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n AIProvider,\n Tool,\n Action,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n} from './types.js'\n\nimport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n RequestMessage,\n PendingRequest,\n} from './messages.js'\n\nimport { generateMessageId } from './messages.js'\n\n// ============================================================================\n// Environment Detection and Message Port\n// ============================================================================\n\n/**\n * Detect if we're in Node.js Worker Thread or Web Worker\n * and get the appropriate message port\n */\ninterface MessagePort {\n postMessage(message: WorkerToHostMessage): void\n onMessage(handler: (message: HostToWorkerMessage) => void): void\n}\n\nfunction getMessagePort(): MessagePort {\n // Check if we're in Node.js Worker Thread\n if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js Worker Thread - import parentPort dynamically\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parentPort } = require('node:worker_threads')\n return {\n postMessage: (message) => parentPort?.postMessage(message),\n onMessage: (handler) => parentPort?.on('message', handler),\n }\n }\n\n // Web Worker - use self\n return {\n postMessage: (message) => self.postMessage(message),\n onMessage: (handler) => {\n self.addEventListener('message', (event: MessageEvent<HostToWorkerMessage>) => {\n handler(event.data)\n })\n },\n }\n}\n\nconst messagePort = getMessagePort()\n\n// ============================================================================\n// Global State\n// ============================================================================\n\nlet extensionModule: ExtensionModule | null = null\nlet extensionDisposable: Disposable | null = null\nlet extensionContext: ExtensionContext | null = null\n\nconst pendingRequests = new Map<string, PendingRequest>()\nconst registeredProviders = new Map<string, AIProvider>()\nconst registeredTools = new Map<string, Tool>()\nconst registeredActions = new Map<string, Action>()\nconst settingsCallbacks: Array<(key: string, value: unknown) => void> = []\nconst schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void> = []\n\n/**\n * Tracking for streaming fetch requests.\n * Each request stores incoming chunks and signals when new data arrives.\n */\ninterface StreamingFetchRequest {\n chunks: string[]\n done: boolean\n error?: string\n resolve?: () => void\n}\nconst streamingFetchRequests = new Map<string, StreamingFetchRequest>()\n\nconst REQUEST_TIMEOUT = 30000 // 30 seconds\n\n// ============================================================================\n// Message Handling\n// ============================================================================\n\n/**\n * Send a message to the host\n */\nfunction postMessage(message: WorkerToHostMessage): void {\n messagePort.postMessage(message)\n}\n\n/**\n * Send a request to the host and wait for response\n */\nasync function sendRequest<T>(method: RequestMessage['method'], payload: unknown): Promise<T> {\n const id = generateMessageId()\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n pendingRequests.delete(id)\n reject(new Error(`Request ${method} timed out`))\n }, REQUEST_TIMEOUT)\n\n pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timeout })\n\n postMessage({\n type: 'request',\n id,\n method,\n payload,\n })\n })\n}\n\n/**\n * Handle messages from the host\n */\nasync function handleHostMessage(message: HostToWorkerMessage): Promise<void> {\n switch (message.type) {\n case 'activate':\n await handleActivate(message.payload)\n break\n\n case 'deactivate':\n await handleDeactivate()\n break\n\n case 'settings-changed':\n handleSettingsChanged(message.payload.key, message.payload.value)\n break\n\n case 'scheduler-fire':\n handleSchedulerFire(message.payload)\n break\n\n case 'provider-chat-request':\n await handleProviderChatRequest(message.id, message.payload)\n break\n\n case 'provider-models-request':\n await handleProviderModelsRequest(message.id, message.payload)\n break\n\n case 'tool-execute-request':\n await handleToolExecuteRequest(message.id, message.payload)\n break\n\n case 'action-execute-request':\n await handleActionExecuteRequest(message.id, message.payload)\n break\n\n case 'response':\n handleResponse(message.payload)\n break\n\n case 'streaming-fetch-chunk':\n handleStreamingFetchChunk(message.payload)\n break\n }\n}\n\n/**\n * Handle incoming streaming fetch chunks from the host\n */\nfunction handleStreamingFetchChunk(payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n}): void {\n const request = streamingFetchRequests.get(payload.requestId)\n if (!request) return\n\n if (payload.error) {\n request.error = payload.error\n request.done = true\n } else if (payload.chunk) {\n request.chunks.push(payload.chunk)\n }\n\n if (payload.done) {\n request.done = true\n }\n\n // Signal that new data is available\n if (request.resolve) {\n const resolve = request.resolve\n request.resolve = undefined\n resolve()\n }\n\n // Send acknowledgment for backpressure control\n postMessage({\n type: 'streaming-fetch-ack',\n payload: { requestId: payload.requestId },\n })\n}\n\nfunction handleResponse(payload: { requestId: string; success: boolean; data?: unknown; error?: string }): void {\n const pending = pendingRequests.get(payload.requestId)\n if (!pending) return\n\n clearTimeout(pending.timeout)\n pendingRequests.delete(payload.requestId)\n\n if (payload.success) {\n pending.resolve(payload.data)\n } else {\n pending.reject(new Error(payload.error || 'Unknown error'))\n }\n}\n\n// ============================================================================\n// Activation / Deactivation\n// ============================================================================\n\nasync function handleActivate(payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n}): Promise<void> {\n const { extensionId, extensionVersion, storagePath, permissions } = payload\n\n // Build the context based on permissions\n extensionContext = buildContext(extensionId, extensionVersion, storagePath, permissions)\n\n // Import and activate the extension\n try {\n // The actual extension code should be bundled and available\n // This is called after the extension code has been loaded into the worker\n if (extensionModule?.activate) {\n const result = await extensionModule.activate(extensionContext)\n if (result && 'dispose' in result) {\n extensionDisposable = result\n }\n }\n } catch (error) {\n extensionContext.log.error('Failed to activate extension', {\n error: error instanceof Error ? error.message : String(error),\n })\n throw error\n }\n}\n\nasync function handleDeactivate(): Promise<void> {\n try {\n if (extensionModule?.deactivate) {\n await extensionModule.deactivate()\n }\n if (extensionDisposable) {\n extensionDisposable.dispose()\n }\n } catch (error) {\n console.error('Error during deactivation:', error)\n } finally {\n extensionModule = null\n extensionDisposable = null\n extensionContext = null\n registeredProviders.clear()\n registeredTools.clear()\n registeredActions.clear()\n settingsCallbacks.length = 0\n schedulerCallbacks.length = 0\n }\n}\n\nfunction handleSettingsChanged(key: string, value: unknown): void {\n for (const callback of settingsCallbacks) {\n try {\n callback(key, value)\n } catch (error) {\n console.error('Error in settings change callback:', error)\n }\n }\n}\n\nfunction handleSchedulerFire(payload: SchedulerFirePayload): void {\n // Set the userId in context if this is a user-scoped job\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n for (const callback of schedulerCallbacks) {\n try {\n callback(payload)\n } catch (error) {\n console.error('Error in scheduler callback:', error)\n }\n }\n\n // Reset userId after all callbacks\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n}\n\n// ============================================================================\n// Provider / Tool Requests\n// ============================================================================\n\nasync function handleProviderChatRequest(\n requestId: string,\n payload: { providerId: string; messages: ChatMessage[]; options: ChatOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n postMessage({\n type: 'request',\n id: generateMessageId(),\n method: 'network.fetch', // Dummy, we need a proper error response\n payload: { error: `Provider ${payload.providerId} not found` },\n })\n return\n }\n\n try {\n const generator = provider.chat(payload.messages, payload.options)\n let sawDone = false\n let sawError = false\n\n for await (const event of generator) {\n if (event.type === 'done') {\n sawDone = true\n } else if (event.type === 'error') {\n sawError = true\n }\n postMessage({\n type: 'stream-event',\n payload: { requestId, event },\n })\n }\n\n if (!sawDone && !sawError) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: { type: 'done' },\n },\n })\n }\n } catch (error) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: {\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n },\n },\n })\n }\n}\n\nasync function handleProviderModelsRequest(\n requestId: string,\n payload: { providerId: string; options?: GetModelsOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n // Send error response\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: `Provider ${payload.providerId} not found`,\n },\n })\n return\n }\n\n try {\n // Pass options to getModels so provider can use settings (e.g., URL for Ollama)\n const models = await provider.getModels(payload.options)\n // Send response with models\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models,\n },\n })\n } catch (error) {\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: error instanceof Error ? error.message : String(error),\n },\n })\n }\n}\n\nasync function handleToolExecuteRequest(\n requestId: string,\n payload: { toolId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const tool = registeredTools.get(payload.toolId)\n if (!tool) {\n // Send error response\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Tool ${payload.toolId} not found` },\n error: `Tool ${payload.toolId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n // Create a new context with the userId for this execution\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await tool.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n // Send response with result\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\nasync function handleActionExecuteRequest(\n requestId: string,\n payload: { actionId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const action = registeredActions.get(payload.actionId)\n if (!action) {\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Action ${payload.actionId} not found` },\n error: `Action ${payload.actionId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await action.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\n// ============================================================================\n// Context Building\n// ============================================================================\n\nfunction buildContext(\n extensionId: string,\n extensionVersion: string,\n storagePath: string,\n permissions: string[]\n): ExtensionContext {\n const hasPermission = (perm: string): boolean => {\n return permissions.some((p) => {\n if (p === perm) return true\n if (p.endsWith(':*') && perm.startsWith(p.slice(0, -1))) return true\n return false\n })\n }\n\n const log: LogAPI = {\n debug: (message, data) => postMessage({ type: 'log', payload: { level: 'debug', message, data } }),\n info: (message, data) => postMessage({ type: 'log', payload: { level: 'info', message, data } }),\n warn: (message, data) => postMessage({ type: 'log', payload: { level: 'warn', message, data } }),\n error: (message, data) => postMessage({ type: 'log', payload: { level: 'error', message, data } }),\n }\n\n const context: ExtensionContext = {\n extension: {\n id: extensionId,\n version: extensionVersion,\n storagePath,\n },\n log,\n }\n\n // Add network API if permitted\n if (permissions.some((p) => p.startsWith('network:'))) {\n const networkApi: NetworkAPI = {\n async fetch(url: string, options?: RequestInit): Promise<Response> {\n const result = await sendRequest<{ status: number; statusText: string; headers: Record<string, string>; body: string }>('network.fetch', { url, options })\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers: result.headers,\n })\n },\n\n async *fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown> {\n const requestId = generateMessageId()\n\n // Set up streaming request tracking\n const request: StreamingFetchRequest = {\n chunks: [],\n done: false,\n }\n streamingFetchRequests.set(requestId, request)\n\n // Send the streaming fetch request to the host\n postMessage({\n type: 'request',\n id: requestId,\n method: 'network.fetch-stream',\n payload: { url, options, requestId },\n })\n\n try {\n // Yield chunks as they arrive\n while (!request.done) {\n // Wait for new data\n await new Promise<void>((resolve) => {\n const resolver = () => {\n // Clear the stored resolver before resolving to avoid\n // stale callbacks being invoked for a new wait iteration.\n if (request.resolve === resolver) {\n request.resolve = undefined\n }\n resolve()\n }\n\n request.resolve = resolver\n\n // Check if we already have data or completion\n if (request.chunks.length > 0 || request.done) {\n resolver()\n }\n })\n\n // Check for errors first, before yielding any chunks\n if (request.error) {\n throw new Error(request.error)\n }\n\n // Yield all available chunks\n while (request.chunks.length > 0) {\n yield request.chunks.shift()!\n }\n }\n } finally {\n streamingFetchRequests.delete(requestId)\n }\n },\n }\n ;(context as { network: NetworkAPI }).network = networkApi\n }\n\n // Add settings API if permitted\n if (hasPermission('settings.register')) {\n const settingsApi: SettingsAPI = {\n async getAll<T extends Record<string, unknown>>(): Promise<T> {\n return sendRequest<T>('settings.getAll', {})\n },\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('settings.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('settings.set', { key, value })\n },\n onChange(callback: (key: string, value: unknown) => void): Disposable {\n settingsCallbacks.push(callback)\n return {\n dispose: () => {\n const index = settingsCallbacks.indexOf(callback)\n if (index >= 0) settingsCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { settings: SettingsAPI }).settings = settingsApi\n }\n\n // Add providers API if permitted\n if (hasPermission('provider.register')) {\n const providersApi: ProvidersAPI = {\n register(provider: AIProvider): Disposable {\n registeredProviders.set(provider.id, provider)\n postMessage({\n type: 'provider-registered',\n payload: { id: provider.id, name: provider.name },\n })\n return {\n dispose: () => {\n registeredProviders.delete(provider.id)\n },\n }\n },\n }\n ;(context as { providers: ProvidersAPI }).providers = providersApi\n }\n\n // Add tools API if permitted\n if (hasPermission('tools.register')) {\n const toolsApi: ToolsAPI = {\n register(tool: Tool): Disposable {\n registeredTools.set(tool.id, tool)\n postMessage({\n type: 'tool-registered',\n payload: {\n id: tool.id,\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n })\n return {\n dispose: () => {\n registeredTools.delete(tool.id)\n },\n }\n },\n }\n ;(context as { tools: ToolsAPI }).tools = toolsApi\n }\n\n // Add actions API if permitted\n if (hasPermission('actions.register')) {\n const actionsApi: ActionsAPI = {\n register(action: Action): Disposable {\n registeredActions.set(action.id, action)\n postMessage({\n type: 'action-registered',\n payload: {\n id: action.id,\n },\n })\n return {\n dispose: () => {\n registeredActions.delete(action.id)\n },\n }\n },\n }\n ;(context as { actions: ActionsAPI }).actions = actionsApi\n }\n\n // Add events API if permitted\n if (hasPermission('events.emit')) {\n const eventsApi: EventsAPI = {\n async emit(name: string, payload?: Record<string, unknown>): Promise<void> {\n await sendRequest<void>('events.emit', { name, payload })\n },\n }\n ;(context as { events: EventsAPI }).events = eventsApi\n }\n\n // Add scheduler API if permitted\n if (hasPermission('scheduler.register')) {\n const schedulerApi: SchedulerAPI = {\n async schedule(job: SchedulerJobRequest): Promise<void> {\n await sendRequest<void>('scheduler.schedule', { job })\n },\n async cancel(jobId: string): Promise<void> {\n await sendRequest<void>('scheduler.cancel', { jobId })\n },\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable {\n schedulerCallbacks.push(callback)\n return {\n dispose: () => {\n const index = schedulerCallbacks.indexOf(callback)\n if (index >= 0) schedulerCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { scheduler: SchedulerAPI }).scheduler = schedulerApi\n }\n\n // Add user profile API if permitted\n if (hasPermission('user.profile.read')) {\n const userApi: UserAPI = {\n async getProfile(): Promise<UserProfile> {\n return sendRequest<UserProfile>('user.getProfile', {})\n },\n }\n ;(context as { user: UserAPI }).user = userApi\n }\n\n // Add chat API if permitted\n if (hasPermission('chat.message.write')) {\n const chatApi: ChatAPI = {\n async appendInstruction(message: ChatInstructionMessage): Promise<void> {\n await sendRequest<void>('chat.appendInstruction', message)\n },\n }\n ;(context as { chat: ChatAPI }).chat = chatApi\n }\n\n // Add database API if permitted\n if (hasPermission('database.own')) {\n const databaseApi: DatabaseAPI = {\n async execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {\n return sendRequest<T[]>('database.execute', { sql, params })\n },\n }\n ;(context as { database: DatabaseAPI }).database = databaseApi\n }\n\n // Add storage API if permitted\n if (hasPermission('storage.local')) {\n const storageApi: StorageAPI = {\n // Global/extension-scoped storage\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.set', { key, value })\n },\n async delete(key: string): Promise<void> {\n return sendRequest<void>('storage.delete', { key })\n },\n async keys(): Promise<string[]> {\n return sendRequest<string[]>('storage.keys', {})\n },\n // User-scoped storage\n async getForUser<T>(userId: string, key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.getForUser', { userId, key })\n },\n async setForUser(userId: string, key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.setForUser', { userId, key, value })\n },\n async deleteForUser(userId: string, key: string): Promise<void> {\n return sendRequest<void>('storage.deleteForUser', { userId, key })\n },\n async keysForUser(userId: string): Promise<string[]> {\n return sendRequest<string[]>('storage.keysForUser', { userId })\n },\n }\n ;(context as { storage: StorageAPI }).storage = storageApi\n }\n\n return context\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the extension runtime\n * This should be called by the extension's entry point\n */\nexport function initializeExtension(module: ExtensionModule): void {\n extensionModule = module\n\n // Set up message listener using the appropriate message port\n messagePort.onMessage(async (message: HostToWorkerMessage) => {\n try {\n await handleHostMessage(message)\n } catch (error) {\n console.error('Error handling message:', error)\n }\n })\n\n // Signal that we're ready\n postMessage({ type: 'ready' })\n}\n\n// Re-export types for extensions to use\nexport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n AIProvider,\n Tool,\n ToolDefinition,\n ToolResult,\n ToolCall,\n Action,\n ActionResult,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n} from './types.js'\n","/**\n * Message protocol between Extension Host and Extension Workers\n */\n\nimport type {\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolResult,\n ActionResult,\n ModelInfo,\n SchedulerFirePayload,\n} from './types.js'\n\n// ============================================================================\n// Host → Worker Messages\n// ============================================================================\n\nexport type HostToWorkerMessage =\n | ActivateMessage\n | DeactivateMessage\n | SettingsChangedMessage\n | SchedulerFireMessage\n | ProviderChatRequestMessage\n | ProviderModelsRequestMessage\n | ToolExecuteRequestMessage\n | ActionExecuteRequestMessage\n | ResponseMessage\n | StreamingFetchChunkMessage\n\nexport interface ActivateMessage {\n type: 'activate'\n id: string\n payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n }\n}\n\nexport interface DeactivateMessage {\n type: 'deactivate'\n id: string\n}\n\nexport interface SettingsChangedMessage {\n type: 'settings-changed'\n id: string\n payload: {\n key: string\n value: unknown\n }\n}\n\nexport interface SchedulerFireMessage {\n type: 'scheduler-fire'\n id: string\n payload: SchedulerFirePayload\n}\n\nexport interface ProviderChatRequestMessage {\n type: 'provider-chat-request'\n id: string\n payload: {\n providerId: string\n messages: ChatMessage[]\n options: ChatOptions\n }\n}\n\nexport interface ProviderModelsRequestMessage {\n type: 'provider-models-request'\n id: string\n payload: {\n providerId: string\n options?: GetModelsOptions\n }\n}\n\nexport interface ToolExecuteRequestMessage {\n type: 'tool-execute-request'\n id: string\n payload: {\n toolId: string\n params: Record<string, unknown>\n /** User ID if the tool is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ActionExecuteRequestMessage {\n type: 'action-execute-request'\n id: string\n payload: {\n actionId: string\n params: Record<string, unknown>\n /** User ID if the action is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ResponseMessage {\n type: 'response'\n id: string\n payload: {\n requestId: string\n success: boolean\n data?: unknown\n error?: string\n }\n}\n\n/**\n * Message sent from host to worker with streaming fetch data chunks.\n * Used for streaming network responses (e.g., NDJSON streams from Ollama).\n */\nexport interface StreamingFetchChunkMessage {\n type: 'streaming-fetch-chunk'\n id: string\n payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n }\n}\n\n// ============================================================================\n// Worker → Host Messages\n// ============================================================================\n\nexport type WorkerToHostMessage =\n | ReadyMessage\n | RequestMessage\n | ProviderRegisteredMessage\n | ToolRegisteredMessage\n | ActionRegisteredMessage\n | StreamEventMessage\n | LogMessage\n | ProviderModelsResponseMessage\n | ToolExecuteResponseMessage\n | ActionExecuteResponseMessage\n | StreamingFetchAckMessage\n\nexport interface ReadyMessage {\n type: 'ready'\n}\n\n/**\n * Message sent from worker to host to acknowledge receipt of a streaming fetch chunk.\n * This enables backpressure control to prevent unbounded memory growth.\n */\nexport interface StreamingFetchAckMessage {\n type: 'streaming-fetch-ack'\n payload: {\n requestId: string\n }\n}\n\nexport interface RequestMessage {\n type: 'request'\n id: string\n method: RequestMethod\n payload: unknown\n}\n\nexport type RequestMethod =\n | 'network.fetch'\n | 'network.fetch-stream'\n | 'settings.getAll'\n | 'settings.get'\n | 'settings.set'\n | 'user.getProfile'\n | 'events.emit'\n | 'scheduler.schedule'\n | 'scheduler.cancel'\n | 'chat.appendInstruction'\n | 'database.execute'\n | 'storage.get'\n | 'storage.set'\n | 'storage.delete'\n | 'storage.keys'\n | 'storage.getForUser'\n | 'storage.setForUser'\n | 'storage.deleteForUser'\n | 'storage.keysForUser'\n\nexport interface ProviderRegisteredMessage {\n type: 'provider-registered'\n payload: {\n id: string\n name: string\n }\n}\n\nexport interface ToolRegisteredMessage {\n type: 'tool-registered'\n payload: {\n id: string\n name: string\n description: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface ActionRegisteredMessage {\n type: 'action-registered'\n payload: {\n id: string\n }\n}\n\nexport interface StreamEventMessage {\n type: 'stream-event'\n payload: {\n requestId: string\n event: StreamEvent\n }\n}\n\nexport interface ProviderModelsResponseMessage {\n type: 'provider-models-response'\n payload: {\n requestId: string\n models: ModelInfo[]\n error?: string\n }\n}\n\nexport interface ToolExecuteResponseMessage {\n type: 'tool-execute-response'\n payload: {\n requestId: string\n result: ToolResult\n error?: string\n }\n}\n\nexport interface ActionExecuteResponseMessage {\n type: 'action-execute-response'\n payload: {\n requestId: string\n result: ActionResult\n error?: string\n }\n}\n\nexport interface LogMessage {\n type: 'log'\n payload: {\n level: 'debug' | 'info' | 'warn' | 'error'\n message: string\n data?: Record<string, unknown>\n }\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\nexport interface PendingRequest<T = unknown> {\n resolve: (value: T) => void\n reject: (error: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n}\n"],"mappings":"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","names":["module"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime.ts","../src/messages.ts"],"sourcesContent":["/**\n * Extension Runtime - Runs inside the worker\n *\n * This module handles communication with the Extension Host and provides\n * the ExtensionContext to the extension's activate function.\n */\n\nimport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n AIProvider,\n Tool,\n Action,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n} from './types.js'\n\nimport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n RequestMessage,\n PendingRequest,\n} from './messages.js'\n\nimport { generateMessageId } from './messages.js'\n\n// ============================================================================\n// Environment Detection and Message Port\n// ============================================================================\n\n/**\n * Detect if we're in Node.js Worker Thread or Web Worker\n * and get the appropriate message port\n */\ninterface MessagePort {\n postMessage(message: WorkerToHostMessage): void\n onMessage(handler: (message: HostToWorkerMessage) => void): void\n}\n\nfunction getMessagePort(): MessagePort {\n // Check if we're in Node.js Worker Thread\n if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js Worker Thread - import parentPort dynamically\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parentPort } = require('node:worker_threads')\n return {\n postMessage: (message) => parentPort?.postMessage(message),\n onMessage: (handler) => parentPort?.on('message', handler),\n }\n }\n\n // Web Worker - use self\n return {\n postMessage: (message) => self.postMessage(message),\n onMessage: (handler) => {\n self.addEventListener('message', (event: MessageEvent<HostToWorkerMessage>) => {\n handler(event.data)\n })\n },\n }\n}\n\nconst messagePort = getMessagePort()\n\n// ============================================================================\n// Global State\n// ============================================================================\n\nlet extensionModule: ExtensionModule | null = null\nlet extensionDisposable: Disposable | null = null\nlet extensionContext: ExtensionContext | null = null\n\nconst pendingRequests = new Map<string, PendingRequest>()\nconst registeredProviders = new Map<string, AIProvider>()\nconst registeredTools = new Map<string, Tool>()\nconst registeredActions = new Map<string, Action>()\nconst settingsCallbacks: Array<(key: string, value: unknown) => void> = []\nconst schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void | Promise<void>> = []\n\n/**\n * Tracking for streaming fetch requests.\n * Each request stores incoming chunks and signals when new data arrives.\n */\ninterface StreamingFetchRequest {\n chunks: string[]\n done: boolean\n error?: string\n resolve?: () => void\n}\nconst streamingFetchRequests = new Map<string, StreamingFetchRequest>()\n\nconst REQUEST_TIMEOUT = 30000 // 30 seconds\n\n// ============================================================================\n// Message Handling\n// ============================================================================\n\n/**\n * Send a message to the host\n */\nfunction postMessage(message: WorkerToHostMessage): void {\n messagePort.postMessage(message)\n}\n\n/**\n * Send a request to the host and wait for response\n */\nasync function sendRequest<T>(method: RequestMessage['method'], payload: unknown): Promise<T> {\n const id = generateMessageId()\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n pendingRequests.delete(id)\n reject(new Error(`Request ${method} timed out`))\n }, REQUEST_TIMEOUT)\n\n pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timeout })\n\n postMessage({\n type: 'request',\n id,\n method,\n payload,\n })\n })\n}\n\n/**\n * Handle messages from the host\n */\nasync function handleHostMessage(message: HostToWorkerMessage): Promise<void> {\n switch (message.type) {\n case 'activate':\n await handleActivate(message.payload)\n break\n\n case 'deactivate':\n await handleDeactivate()\n break\n\n case 'settings-changed':\n handleSettingsChanged(message.payload.key, message.payload.value)\n break\n\n case 'scheduler-fire':\n await handleSchedulerFire(message.payload)\n break\n\n case 'provider-chat-request':\n await handleProviderChatRequest(message.id, message.payload)\n break\n\n case 'provider-models-request':\n await handleProviderModelsRequest(message.id, message.payload)\n break\n\n case 'tool-execute-request':\n await handleToolExecuteRequest(message.id, message.payload)\n break\n\n case 'action-execute-request':\n await handleActionExecuteRequest(message.id, message.payload)\n break\n\n case 'response':\n handleResponse(message.payload)\n break\n\n case 'streaming-fetch-chunk':\n handleStreamingFetchChunk(message.payload)\n break\n }\n}\n\n/**\n * Handle incoming streaming fetch chunks from the host\n */\nfunction handleStreamingFetchChunk(payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n}): void {\n const request = streamingFetchRequests.get(payload.requestId)\n if (!request) return\n\n if (payload.error) {\n request.error = payload.error\n request.done = true\n } else if (payload.chunk) {\n request.chunks.push(payload.chunk)\n }\n\n if (payload.done) {\n request.done = true\n }\n\n // Signal that new data is available\n if (request.resolve) {\n const resolve = request.resolve\n request.resolve = undefined\n resolve()\n }\n\n // Send acknowledgment for backpressure control\n postMessage({\n type: 'streaming-fetch-ack',\n payload: { requestId: payload.requestId },\n })\n}\n\nfunction handleResponse(payload: { requestId: string; success: boolean; data?: unknown; error?: string }): void {\n const pending = pendingRequests.get(payload.requestId)\n if (!pending) return\n\n clearTimeout(pending.timeout)\n pendingRequests.delete(payload.requestId)\n\n if (payload.success) {\n pending.resolve(payload.data)\n } else {\n pending.reject(new Error(payload.error || 'Unknown error'))\n }\n}\n\n// ============================================================================\n// Activation / Deactivation\n// ============================================================================\n\nasync function handleActivate(payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n}): Promise<void> {\n const { extensionId, extensionVersion, storagePath, permissions } = payload\n\n // Build the context based on permissions\n extensionContext = buildContext(extensionId, extensionVersion, storagePath, permissions)\n\n // Import and activate the extension\n try {\n // The actual extension code should be bundled and available\n // This is called after the extension code has been loaded into the worker\n if (extensionModule?.activate) {\n const result = await extensionModule.activate(extensionContext)\n if (result && 'dispose' in result) {\n extensionDisposable = result\n }\n }\n } catch (error) {\n extensionContext.log.error('Failed to activate extension', {\n error: error instanceof Error ? error.message : String(error),\n })\n throw error\n }\n}\n\nasync function handleDeactivate(): Promise<void> {\n try {\n if (extensionModule?.deactivate) {\n await extensionModule.deactivate()\n }\n if (extensionDisposable) {\n extensionDisposable.dispose()\n }\n } catch (error) {\n console.error('Error during deactivation:', error)\n } finally {\n extensionModule = null\n extensionDisposable = null\n extensionContext = null\n registeredProviders.clear()\n registeredTools.clear()\n registeredActions.clear()\n settingsCallbacks.length = 0\n schedulerCallbacks.length = 0\n }\n}\n\nfunction handleSettingsChanged(key: string, value: unknown): void {\n for (const callback of settingsCallbacks) {\n try {\n callback(key, value)\n } catch (error) {\n console.error('Error in settings change callback:', error)\n }\n }\n}\n\nasync function handleSchedulerFire(payload: SchedulerFirePayload): Promise<void> {\n // Set the userId in context if this is a user-scoped job\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n // Run callbacks concurrently to avoid blocking\n const results = await Promise.allSettled(\n schedulerCallbacks.map((callback) => callback(payload)),\n )\n\n // Log any errors\n results.forEach((result, index) => {\n if (result.status === 'rejected') {\n console.error(`Error in scheduler callback ${index}:`, result.reason)\n }\n })\n\n // Reset userId after all callbacks have completed\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n}\n\n// ============================================================================\n// Provider / Tool Requests\n// ============================================================================\n\nasync function handleProviderChatRequest(\n requestId: string,\n payload: { providerId: string; messages: ChatMessage[]; options: ChatOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n postMessage({\n type: 'request',\n id: generateMessageId(),\n method: 'network.fetch', // Dummy, we need a proper error response\n payload: { error: `Provider ${payload.providerId} not found` },\n })\n return\n }\n\n try {\n const generator = provider.chat(payload.messages, payload.options)\n let sawDone = false\n let sawError = false\n\n for await (const event of generator) {\n if (event.type === 'done') {\n sawDone = true\n } else if (event.type === 'error') {\n sawError = true\n }\n postMessage({\n type: 'stream-event',\n payload: { requestId, event },\n })\n }\n\n if (!sawDone && !sawError) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: { type: 'done' },\n },\n })\n }\n } catch (error) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: {\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n },\n },\n })\n }\n}\n\nasync function handleProviderModelsRequest(\n requestId: string,\n payload: { providerId: string; options?: GetModelsOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n // Send error response\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: `Provider ${payload.providerId} not found`,\n },\n })\n return\n }\n\n try {\n // Pass options to getModels so provider can use settings (e.g., URL for Ollama)\n const models = await provider.getModels(payload.options)\n // Send response with models\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models,\n },\n })\n } catch (error) {\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: error instanceof Error ? error.message : String(error),\n },\n })\n }\n}\n\nasync function handleToolExecuteRequest(\n requestId: string,\n payload: { toolId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const tool = registeredTools.get(payload.toolId)\n if (!tool) {\n // Send error response\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Tool ${payload.toolId} not found` },\n error: `Tool ${payload.toolId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n // Create a new context with the userId for this execution\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await tool.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n // Send response with result\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\nasync function handleActionExecuteRequest(\n requestId: string,\n payload: { actionId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const action = registeredActions.get(payload.actionId)\n if (!action) {\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Action ${payload.actionId} not found` },\n error: `Action ${payload.actionId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await action.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\n// ============================================================================\n// Context Building\n// ============================================================================\n\nfunction buildContext(\n extensionId: string,\n extensionVersion: string,\n storagePath: string,\n permissions: string[]\n): ExtensionContext {\n const hasPermission = (perm: string): boolean => {\n return permissions.some((p) => {\n if (p === perm) return true\n if (p.endsWith(':*') && perm.startsWith(p.slice(0, -1))) return true\n return false\n })\n }\n\n const log: LogAPI = {\n debug: (message, data) => postMessage({ type: 'log', payload: { level: 'debug', message, data } }),\n info: (message, data) => postMessage({ type: 'log', payload: { level: 'info', message, data } }),\n warn: (message, data) => postMessage({ type: 'log', payload: { level: 'warn', message, data } }),\n error: (message, data) => postMessage({ type: 'log', payload: { level: 'error', message, data } }),\n }\n\n const context: ExtensionContext = {\n extension: {\n id: extensionId,\n version: extensionVersion,\n storagePath,\n },\n log,\n }\n\n // Add network API if permitted\n if (permissions.some((p) => p.startsWith('network:'))) {\n const networkApi: NetworkAPI = {\n async fetch(url: string, options?: RequestInit): Promise<Response> {\n const result = await sendRequest<{ status: number; statusText: string; headers: Record<string, string>; body: string }>('network.fetch', { url, options })\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers: result.headers,\n })\n },\n\n async *fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown> {\n const requestId = generateMessageId()\n\n // Set up streaming request tracking\n const request: StreamingFetchRequest = {\n chunks: [],\n done: false,\n }\n streamingFetchRequests.set(requestId, request)\n\n // Send the streaming fetch request to the host\n postMessage({\n type: 'request',\n id: requestId,\n method: 'network.fetch-stream',\n payload: { url, options, requestId },\n })\n\n try {\n // Yield chunks as they arrive\n while (!request.done) {\n // Wait for new data\n await new Promise<void>((resolve) => {\n const resolver = () => {\n // Clear the stored resolver before resolving to avoid\n // stale callbacks being invoked for a new wait iteration.\n if (request.resolve === resolver) {\n request.resolve = undefined\n }\n resolve()\n }\n\n request.resolve = resolver\n\n // Check if we already have data or completion\n if (request.chunks.length > 0 || request.done) {\n resolver()\n }\n })\n\n // Check for errors first, before yielding any chunks\n if (request.error) {\n throw new Error(request.error)\n }\n\n // Yield all available chunks\n while (request.chunks.length > 0) {\n yield request.chunks.shift()!\n }\n }\n } finally {\n streamingFetchRequests.delete(requestId)\n }\n },\n }\n ;(context as { network: NetworkAPI }).network = networkApi\n }\n\n // Add settings API if permitted\n if (hasPermission('settings.register')) {\n const settingsApi: SettingsAPI = {\n async getAll<T extends Record<string, unknown>>(): Promise<T> {\n return sendRequest<T>('settings.getAll', {})\n },\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('settings.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('settings.set', { key, value })\n },\n onChange(callback: (key: string, value: unknown) => void): Disposable {\n settingsCallbacks.push(callback)\n return {\n dispose: () => {\n const index = settingsCallbacks.indexOf(callback)\n if (index >= 0) settingsCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { settings: SettingsAPI }).settings = settingsApi\n }\n\n // Add providers API if permitted\n if (hasPermission('provider.register')) {\n const providersApi: ProvidersAPI = {\n register(provider: AIProvider): Disposable {\n registeredProviders.set(provider.id, provider)\n postMessage({\n type: 'provider-registered',\n payload: { id: provider.id, name: provider.name },\n })\n return {\n dispose: () => {\n registeredProviders.delete(provider.id)\n },\n }\n },\n }\n ;(context as { providers: ProvidersAPI }).providers = providersApi\n }\n\n // Add tools API if permitted\n if (hasPermission('tools.register')) {\n const toolsApi: ToolsAPI = {\n register(tool: Tool): Disposable {\n registeredTools.set(tool.id, tool)\n postMessage({\n type: 'tool-registered',\n payload: {\n id: tool.id,\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n })\n return {\n dispose: () => {\n registeredTools.delete(tool.id)\n },\n }\n },\n }\n ;(context as { tools: ToolsAPI }).tools = toolsApi\n }\n\n // Add actions API if permitted\n if (hasPermission('actions.register')) {\n const actionsApi: ActionsAPI = {\n register(action: Action): Disposable {\n registeredActions.set(action.id, action)\n postMessage({\n type: 'action-registered',\n payload: {\n id: action.id,\n },\n })\n return {\n dispose: () => {\n registeredActions.delete(action.id)\n },\n }\n },\n }\n ;(context as { actions: ActionsAPI }).actions = actionsApi\n }\n\n // Add events API if permitted\n if (hasPermission('events.emit')) {\n const eventsApi: EventsAPI = {\n async emit(name: string, payload?: Record<string, unknown>): Promise<void> {\n await sendRequest<void>('events.emit', { name, payload })\n },\n }\n ;(context as { events: EventsAPI }).events = eventsApi\n }\n\n // Add scheduler API if permitted\n if (hasPermission('scheduler.register')) {\n const schedulerApi: SchedulerAPI = {\n async schedule(job: SchedulerJobRequest): Promise<void> {\n // Automatically include userId from context if not explicitly set\n const userId = (extensionContext as { userId?: string }).userId\n const jobWithUser = userId && !job.userId ? { ...job, userId } : job\n await sendRequest<void>('scheduler.schedule', { job: jobWithUser })\n },\n async cancel(jobId: string): Promise<void> {\n await sendRequest<void>('scheduler.cancel', { jobId })\n },\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable {\n schedulerCallbacks.push(callback)\n return {\n dispose: () => {\n const index = schedulerCallbacks.indexOf(callback)\n if (index >= 0) schedulerCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { scheduler: SchedulerAPI }).scheduler = schedulerApi\n }\n\n // Add user profile API if permitted\n if (hasPermission('user.profile.read')) {\n const userApi: UserAPI = {\n async getProfile(): Promise<UserProfile> {\n return sendRequest<UserProfile>('user.getProfile', {})\n },\n }\n ;(context as { user: UserAPI }).user = userApi\n }\n\n // Add chat API if permitted\n if (hasPermission('chat.message.write')) {\n const chatApi: ChatAPI = {\n async appendInstruction(message: ChatInstructionMessage): Promise<void> {\n const contextUserId = (extensionContext as { userId?: string }).userId\n await sendRequest<void>('chat.appendInstruction', {\n ...message,\n userId: message.userId ?? contextUserId,\n })\n },\n }\n ;(context as { chat: ChatAPI }).chat = chatApi\n }\n\n // Add database API if permitted\n if (hasPermission('database.own')) {\n const databaseApi: DatabaseAPI = {\n async execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {\n return sendRequest<T[]>('database.execute', { sql, params })\n },\n }\n ;(context as { database: DatabaseAPI }).database = databaseApi\n }\n\n // Add storage API if permitted\n if (hasPermission('storage.local')) {\n const storageApi: StorageAPI = {\n // Global/extension-scoped storage\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.set', { key, value })\n },\n async delete(key: string): Promise<void> {\n return sendRequest<void>('storage.delete', { key })\n },\n async keys(): Promise<string[]> {\n return sendRequest<string[]>('storage.keys', {})\n },\n // User-scoped storage\n async getForUser<T>(userId: string, key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.getForUser', { userId, key })\n },\n async setForUser(userId: string, key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.setForUser', { userId, key, value })\n },\n async deleteForUser(userId: string, key: string): Promise<void> {\n return sendRequest<void>('storage.deleteForUser', { userId, key })\n },\n async keysForUser(userId: string): Promise<string[]> {\n return sendRequest<string[]>('storage.keysForUser', { userId })\n },\n }\n ;(context as { storage: StorageAPI }).storage = storageApi\n }\n\n return context\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the extension runtime\n * This should be called by the extension's entry point\n */\nexport function initializeExtension(module: ExtensionModule): void {\n extensionModule = module\n\n // Set up message listener using the appropriate message port\n messagePort.onMessage(async (message: HostToWorkerMessage) => {\n try {\n await handleHostMessage(message)\n } catch (error) {\n console.error('Error handling message:', error)\n }\n })\n\n // Signal that we're ready\n postMessage({ type: 'ready' })\n}\n\n// Re-export types for extensions to use\nexport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n AIProvider,\n Tool,\n ToolDefinition,\n ToolResult,\n ToolCall,\n Action,\n ActionResult,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n} from './types.js'\n","/**\n * Message protocol between Extension Host and Extension Workers\n */\n\nimport type {\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolResult,\n ActionResult,\n ModelInfo,\n SchedulerFirePayload,\n} from './types.js'\n\n// ============================================================================\n// Host → Worker Messages\n// ============================================================================\n\nexport type HostToWorkerMessage =\n | ActivateMessage\n | DeactivateMessage\n | SettingsChangedMessage\n | SchedulerFireMessage\n | ProviderChatRequestMessage\n | ProviderModelsRequestMessage\n | ToolExecuteRequestMessage\n | ActionExecuteRequestMessage\n | ResponseMessage\n | StreamingFetchChunkMessage\n\nexport interface ActivateMessage {\n type: 'activate'\n id: string\n payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n }\n}\n\nexport interface DeactivateMessage {\n type: 'deactivate'\n id: string\n}\n\nexport interface SettingsChangedMessage {\n type: 'settings-changed'\n id: string\n payload: {\n key: string\n value: unknown\n }\n}\n\nexport interface SchedulerFireMessage {\n type: 'scheduler-fire'\n id: string\n payload: SchedulerFirePayload\n}\n\nexport interface ProviderChatRequestMessage {\n type: 'provider-chat-request'\n id: string\n payload: {\n providerId: string\n messages: ChatMessage[]\n options: ChatOptions\n }\n}\n\nexport interface ProviderModelsRequestMessage {\n type: 'provider-models-request'\n id: string\n payload: {\n providerId: string\n options?: GetModelsOptions\n }\n}\n\nexport interface ToolExecuteRequestMessage {\n type: 'tool-execute-request'\n id: string\n payload: {\n toolId: string\n params: Record<string, unknown>\n /** User ID if the tool is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ActionExecuteRequestMessage {\n type: 'action-execute-request'\n id: string\n payload: {\n actionId: string\n params: Record<string, unknown>\n /** User ID if the action is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ResponseMessage {\n type: 'response'\n id: string\n payload: {\n requestId: string\n success: boolean\n data?: unknown\n error?: string\n }\n}\n\n/**\n * Message sent from host to worker with streaming fetch data chunks.\n * Used for streaming network responses (e.g., NDJSON streams from Ollama).\n */\nexport interface StreamingFetchChunkMessage {\n type: 'streaming-fetch-chunk'\n id: string\n payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n }\n}\n\n// ============================================================================\n// Worker → Host Messages\n// ============================================================================\n\nexport type WorkerToHostMessage =\n | ReadyMessage\n | RequestMessage\n | ProviderRegisteredMessage\n | ToolRegisteredMessage\n | ActionRegisteredMessage\n | StreamEventMessage\n | LogMessage\n | ProviderModelsResponseMessage\n | ToolExecuteResponseMessage\n | ActionExecuteResponseMessage\n | StreamingFetchAckMessage\n\nexport interface ReadyMessage {\n type: 'ready'\n}\n\n/**\n * Message sent from worker to host to acknowledge receipt of a streaming fetch chunk.\n * This enables backpressure control to prevent unbounded memory growth.\n */\nexport interface StreamingFetchAckMessage {\n type: 'streaming-fetch-ack'\n payload: {\n requestId: string\n }\n}\n\nexport interface RequestMessage {\n type: 'request'\n id: string\n method: RequestMethod\n payload: unknown\n}\n\nexport type RequestMethod =\n | 'network.fetch'\n | 'network.fetch-stream'\n | 'settings.getAll'\n | 'settings.get'\n | 'settings.set'\n | 'user.getProfile'\n | 'events.emit'\n | 'scheduler.schedule'\n | 'scheduler.cancel'\n | 'chat.appendInstruction'\n | 'database.execute'\n | 'storage.get'\n | 'storage.set'\n | 'storage.delete'\n | 'storage.keys'\n | 'storage.getForUser'\n | 'storage.setForUser'\n | 'storage.deleteForUser'\n | 'storage.keysForUser'\n\nexport interface ProviderRegisteredMessage {\n type: 'provider-registered'\n payload: {\n id: string\n name: string\n }\n}\n\nexport interface ToolRegisteredMessage {\n type: 'tool-registered'\n payload: {\n id: string\n name: string\n description: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface ActionRegisteredMessage {\n type: 'action-registered'\n payload: {\n id: string\n }\n}\n\nexport interface StreamEventMessage {\n type: 'stream-event'\n payload: {\n requestId: string\n event: StreamEvent\n }\n}\n\nexport interface ProviderModelsResponseMessage {\n type: 'provider-models-response'\n payload: {\n requestId: string\n models: ModelInfo[]\n error?: string\n }\n}\n\nexport interface ToolExecuteResponseMessage {\n type: 'tool-execute-response'\n payload: {\n requestId: string\n result: ToolResult\n error?: string\n }\n}\n\nexport interface ActionExecuteResponseMessage {\n type: 'action-execute-response'\n payload: {\n requestId: string\n result: ActionResult\n error?: string\n }\n}\n\nexport interface LogMessage {\n type: 'log'\n payload: {\n level: 'debug' | 'info' | 'warn' | 'error'\n message: string\n data?: Record<string, unknown>\n }\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\nexport interface PendingRequest<T = unknown> {\n resolve: (value: T) => void\n reject: (error: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n}\n"],"mappings":"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","names":["module"]}
|
package/dist/runtime.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ab as ExtensionModule } from './types-
|
|
2
|
-
export { a7 as AIProvider, aa as Action, A as ActionResult, C as ChatMessage, a as ChatOptions, O as Disposable, K as ExtensionContext, G as GetModelsOptions, M as ModelInfo, b as StreamEvent, a9 as Tool, a8 as ToolCall, v as ToolDefinition, T as ToolResult } from './types-
|
|
1
|
+
import { ab as ExtensionModule } from './types-CvfONPis.cjs';
|
|
2
|
+
export { a7 as AIProvider, aa as Action, A as ActionResult, C as ChatMessage, a as ChatOptions, O as Disposable, K as ExtensionContext, G as GetModelsOptions, M as ModelInfo, b as StreamEvent, a9 as Tool, a8 as ToolCall, v as ToolDefinition, T as ToolResult } from './types-CvfONPis.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Extension Runtime - Runs inside the worker
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ab as ExtensionModule } from './types-
|
|
2
|
-
export { a7 as AIProvider, aa as Action, A as ActionResult, C as ChatMessage, a as ChatOptions, O as Disposable, K as ExtensionContext, G as GetModelsOptions, M as ModelInfo, b as StreamEvent, a9 as Tool, a8 as ToolCall, v as ToolDefinition, T as ToolResult } from './types-
|
|
1
|
+
import { ab as ExtensionModule } from './types-CvfONPis.js';
|
|
2
|
+
export { a7 as AIProvider, aa as Action, A as ActionResult, C as ChatMessage, a as ChatOptions, O as Disposable, K as ExtensionContext, G as GetModelsOptions, M as ModelInfo, b as StreamEvent, a9 as Tool, a8 as ToolCall, v as ToolDefinition, T as ToolResult } from './types-CvfONPis.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Extension Runtime - Runs inside the worker
|
package/dist/runtime.js
CHANGED
|
@@ -64,7 +64,7 @@ async function handleHostMessage(message) {
|
|
|
64
64
|
handleSettingsChanged(message.payload.key, message.payload.value);
|
|
65
65
|
break;
|
|
66
66
|
case "scheduler-fire":
|
|
67
|
-
handleSchedulerFire(message.payload);
|
|
67
|
+
await handleSchedulerFire(message.payload);
|
|
68
68
|
break;
|
|
69
69
|
case "provider-chat-request":
|
|
70
70
|
await handleProviderChatRequest(message.id, message.payload);
|
|
@@ -166,18 +166,19 @@ function handleSettingsChanged(key, value) {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
-
function handleSchedulerFire(payload) {
|
|
169
|
+
async function handleSchedulerFire(payload) {
|
|
170
170
|
if (extensionContext && payload.userId) {
|
|
171
171
|
;
|
|
172
172
|
extensionContext.userId = payload.userId;
|
|
173
173
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
const results = await Promise.allSettled(
|
|
175
|
+
schedulerCallbacks.map((callback) => callback(payload))
|
|
176
|
+
);
|
|
177
|
+
results.forEach((result, index) => {
|
|
178
|
+
if (result.status === "rejected") {
|
|
179
|
+
console.error(`Error in scheduler callback ${index}:`, result.reason);
|
|
179
180
|
}
|
|
180
|
-
}
|
|
181
|
+
});
|
|
181
182
|
if (extensionContext) {
|
|
182
183
|
;
|
|
183
184
|
extensionContext.userId = void 0;
|
|
@@ -522,7 +523,9 @@ function buildContext(extensionId, extensionVersion, storagePath, permissions) {
|
|
|
522
523
|
if (hasPermission("scheduler.register")) {
|
|
523
524
|
const schedulerApi = {
|
|
524
525
|
async schedule(job) {
|
|
525
|
-
|
|
526
|
+
const userId = extensionContext.userId;
|
|
527
|
+
const jobWithUser = userId && !job.userId ? { ...job, userId } : job;
|
|
528
|
+
await sendRequest("scheduler.schedule", { job: jobWithUser });
|
|
526
529
|
},
|
|
527
530
|
async cancel(jobId) {
|
|
528
531
|
await sendRequest("scheduler.cancel", { jobId });
|
|
@@ -550,7 +553,11 @@ function buildContext(extensionId, extensionVersion, storagePath, permissions) {
|
|
|
550
553
|
if (hasPermission("chat.message.write")) {
|
|
551
554
|
const chatApi = {
|
|
552
555
|
async appendInstruction(message) {
|
|
553
|
-
|
|
556
|
+
const contextUserId = extensionContext.userId;
|
|
557
|
+
await sendRequest("chat.appendInstruction", {
|
|
558
|
+
...message,
|
|
559
|
+
userId: message.userId ?? contextUserId
|
|
560
|
+
});
|
|
554
561
|
}
|
|
555
562
|
};
|
|
556
563
|
context.chat = chatApi;
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * Extension Runtime - Runs inside the worker\n *\n * This module handles communication with the Extension Host and provides\n * the ExtensionContext to the extension's activate function.\n */\n\nimport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n AIProvider,\n Tool,\n Action,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n} from './types.js'\n\nimport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n RequestMessage,\n PendingRequest,\n} from './messages.js'\n\nimport { generateMessageId } from './messages.js'\n\n// ============================================================================\n// Environment Detection and Message Port\n// ============================================================================\n\n/**\n * Detect if we're in Node.js Worker Thread or Web Worker\n * and get the appropriate message port\n */\ninterface MessagePort {\n postMessage(message: WorkerToHostMessage): void\n onMessage(handler: (message: HostToWorkerMessage) => void): void\n}\n\nfunction getMessagePort(): MessagePort {\n // Check if we're in Node.js Worker Thread\n if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js Worker Thread - import parentPort dynamically\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parentPort } = require('node:worker_threads')\n return {\n postMessage: (message) => parentPort?.postMessage(message),\n onMessage: (handler) => parentPort?.on('message', handler),\n }\n }\n\n // Web Worker - use self\n return {\n postMessage: (message) => self.postMessage(message),\n onMessage: (handler) => {\n self.addEventListener('message', (event: MessageEvent<HostToWorkerMessage>) => {\n handler(event.data)\n })\n },\n }\n}\n\nconst messagePort = getMessagePort()\n\n// ============================================================================\n// Global State\n// ============================================================================\n\nlet extensionModule: ExtensionModule | null = null\nlet extensionDisposable: Disposable | null = null\nlet extensionContext: ExtensionContext | null = null\n\nconst pendingRequests = new Map<string, PendingRequest>()\nconst registeredProviders = new Map<string, AIProvider>()\nconst registeredTools = new Map<string, Tool>()\nconst registeredActions = new Map<string, Action>()\nconst settingsCallbacks: Array<(key: string, value: unknown) => void> = []\nconst schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void> = []\n\n/**\n * Tracking for streaming fetch requests.\n * Each request stores incoming chunks and signals when new data arrives.\n */\ninterface StreamingFetchRequest {\n chunks: string[]\n done: boolean\n error?: string\n resolve?: () => void\n}\nconst streamingFetchRequests = new Map<string, StreamingFetchRequest>()\n\nconst REQUEST_TIMEOUT = 30000 // 30 seconds\n\n// ============================================================================\n// Message Handling\n// ============================================================================\n\n/**\n * Send a message to the host\n */\nfunction postMessage(message: WorkerToHostMessage): void {\n messagePort.postMessage(message)\n}\n\n/**\n * Send a request to the host and wait for response\n */\nasync function sendRequest<T>(method: RequestMessage['method'], payload: unknown): Promise<T> {\n const id = generateMessageId()\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n pendingRequests.delete(id)\n reject(new Error(`Request ${method} timed out`))\n }, REQUEST_TIMEOUT)\n\n pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timeout })\n\n postMessage({\n type: 'request',\n id,\n method,\n payload,\n })\n })\n}\n\n/**\n * Handle messages from the host\n */\nasync function handleHostMessage(message: HostToWorkerMessage): Promise<void> {\n switch (message.type) {\n case 'activate':\n await handleActivate(message.payload)\n break\n\n case 'deactivate':\n await handleDeactivate()\n break\n\n case 'settings-changed':\n handleSettingsChanged(message.payload.key, message.payload.value)\n break\n\n case 'scheduler-fire':\n handleSchedulerFire(message.payload)\n break\n\n case 'provider-chat-request':\n await handleProviderChatRequest(message.id, message.payload)\n break\n\n case 'provider-models-request':\n await handleProviderModelsRequest(message.id, message.payload)\n break\n\n case 'tool-execute-request':\n await handleToolExecuteRequest(message.id, message.payload)\n break\n\n case 'action-execute-request':\n await handleActionExecuteRequest(message.id, message.payload)\n break\n\n case 'response':\n handleResponse(message.payload)\n break\n\n case 'streaming-fetch-chunk':\n handleStreamingFetchChunk(message.payload)\n break\n }\n}\n\n/**\n * Handle incoming streaming fetch chunks from the host\n */\nfunction handleStreamingFetchChunk(payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n}): void {\n const request = streamingFetchRequests.get(payload.requestId)\n if (!request) return\n\n if (payload.error) {\n request.error = payload.error\n request.done = true\n } else if (payload.chunk) {\n request.chunks.push(payload.chunk)\n }\n\n if (payload.done) {\n request.done = true\n }\n\n // Signal that new data is available\n if (request.resolve) {\n const resolve = request.resolve\n request.resolve = undefined\n resolve()\n }\n\n // Send acknowledgment for backpressure control\n postMessage({\n type: 'streaming-fetch-ack',\n payload: { requestId: payload.requestId },\n })\n}\n\nfunction handleResponse(payload: { requestId: string; success: boolean; data?: unknown; error?: string }): void {\n const pending = pendingRequests.get(payload.requestId)\n if (!pending) return\n\n clearTimeout(pending.timeout)\n pendingRequests.delete(payload.requestId)\n\n if (payload.success) {\n pending.resolve(payload.data)\n } else {\n pending.reject(new Error(payload.error || 'Unknown error'))\n }\n}\n\n// ============================================================================\n// Activation / Deactivation\n// ============================================================================\n\nasync function handleActivate(payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n}): Promise<void> {\n const { extensionId, extensionVersion, storagePath, permissions } = payload\n\n // Build the context based on permissions\n extensionContext = buildContext(extensionId, extensionVersion, storagePath, permissions)\n\n // Import and activate the extension\n try {\n // The actual extension code should be bundled and available\n // This is called after the extension code has been loaded into the worker\n if (extensionModule?.activate) {\n const result = await extensionModule.activate(extensionContext)\n if (result && 'dispose' in result) {\n extensionDisposable = result\n }\n }\n } catch (error) {\n extensionContext.log.error('Failed to activate extension', {\n error: error instanceof Error ? error.message : String(error),\n })\n throw error\n }\n}\n\nasync function handleDeactivate(): Promise<void> {\n try {\n if (extensionModule?.deactivate) {\n await extensionModule.deactivate()\n }\n if (extensionDisposable) {\n extensionDisposable.dispose()\n }\n } catch (error) {\n console.error('Error during deactivation:', error)\n } finally {\n extensionModule = null\n extensionDisposable = null\n extensionContext = null\n registeredProviders.clear()\n registeredTools.clear()\n registeredActions.clear()\n settingsCallbacks.length = 0\n schedulerCallbacks.length = 0\n }\n}\n\nfunction handleSettingsChanged(key: string, value: unknown): void {\n for (const callback of settingsCallbacks) {\n try {\n callback(key, value)\n } catch (error) {\n console.error('Error in settings change callback:', error)\n }\n }\n}\n\nfunction handleSchedulerFire(payload: SchedulerFirePayload): void {\n // Set the userId in context if this is a user-scoped job\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n for (const callback of schedulerCallbacks) {\n try {\n callback(payload)\n } catch (error) {\n console.error('Error in scheduler callback:', error)\n }\n }\n\n // Reset userId after all callbacks\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n}\n\n// ============================================================================\n// Provider / Tool Requests\n// ============================================================================\n\nasync function handleProviderChatRequest(\n requestId: string,\n payload: { providerId: string; messages: ChatMessage[]; options: ChatOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n postMessage({\n type: 'request',\n id: generateMessageId(),\n method: 'network.fetch', // Dummy, we need a proper error response\n payload: { error: `Provider ${payload.providerId} not found` },\n })\n return\n }\n\n try {\n const generator = provider.chat(payload.messages, payload.options)\n let sawDone = false\n let sawError = false\n\n for await (const event of generator) {\n if (event.type === 'done') {\n sawDone = true\n } else if (event.type === 'error') {\n sawError = true\n }\n postMessage({\n type: 'stream-event',\n payload: { requestId, event },\n })\n }\n\n if (!sawDone && !sawError) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: { type: 'done' },\n },\n })\n }\n } catch (error) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: {\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n },\n },\n })\n }\n}\n\nasync function handleProviderModelsRequest(\n requestId: string,\n payload: { providerId: string; options?: GetModelsOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n // Send error response\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: `Provider ${payload.providerId} not found`,\n },\n })\n return\n }\n\n try {\n // Pass options to getModels so provider can use settings (e.g., URL for Ollama)\n const models = await provider.getModels(payload.options)\n // Send response with models\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models,\n },\n })\n } catch (error) {\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: error instanceof Error ? error.message : String(error),\n },\n })\n }\n}\n\nasync function handleToolExecuteRequest(\n requestId: string,\n payload: { toolId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const tool = registeredTools.get(payload.toolId)\n if (!tool) {\n // Send error response\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Tool ${payload.toolId} not found` },\n error: `Tool ${payload.toolId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n // Create a new context with the userId for this execution\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await tool.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n // Send response with result\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\nasync function handleActionExecuteRequest(\n requestId: string,\n payload: { actionId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const action = registeredActions.get(payload.actionId)\n if (!action) {\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Action ${payload.actionId} not found` },\n error: `Action ${payload.actionId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await action.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\n// ============================================================================\n// Context Building\n// ============================================================================\n\nfunction buildContext(\n extensionId: string,\n extensionVersion: string,\n storagePath: string,\n permissions: string[]\n): ExtensionContext {\n const hasPermission = (perm: string): boolean => {\n return permissions.some((p) => {\n if (p === perm) return true\n if (p.endsWith(':*') && perm.startsWith(p.slice(0, -1))) return true\n return false\n })\n }\n\n const log: LogAPI = {\n debug: (message, data) => postMessage({ type: 'log', payload: { level: 'debug', message, data } }),\n info: (message, data) => postMessage({ type: 'log', payload: { level: 'info', message, data } }),\n warn: (message, data) => postMessage({ type: 'log', payload: { level: 'warn', message, data } }),\n error: (message, data) => postMessage({ type: 'log', payload: { level: 'error', message, data } }),\n }\n\n const context: ExtensionContext = {\n extension: {\n id: extensionId,\n version: extensionVersion,\n storagePath,\n },\n log,\n }\n\n // Add network API if permitted\n if (permissions.some((p) => p.startsWith('network:'))) {\n const networkApi: NetworkAPI = {\n async fetch(url: string, options?: RequestInit): Promise<Response> {\n const result = await sendRequest<{ status: number; statusText: string; headers: Record<string, string>; body: string }>('network.fetch', { url, options })\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers: result.headers,\n })\n },\n\n async *fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown> {\n const requestId = generateMessageId()\n\n // Set up streaming request tracking\n const request: StreamingFetchRequest = {\n chunks: [],\n done: false,\n }\n streamingFetchRequests.set(requestId, request)\n\n // Send the streaming fetch request to the host\n postMessage({\n type: 'request',\n id: requestId,\n method: 'network.fetch-stream',\n payload: { url, options, requestId },\n })\n\n try {\n // Yield chunks as they arrive\n while (!request.done) {\n // Wait for new data\n await new Promise<void>((resolve) => {\n const resolver = () => {\n // Clear the stored resolver before resolving to avoid\n // stale callbacks being invoked for a new wait iteration.\n if (request.resolve === resolver) {\n request.resolve = undefined\n }\n resolve()\n }\n\n request.resolve = resolver\n\n // Check if we already have data or completion\n if (request.chunks.length > 0 || request.done) {\n resolver()\n }\n })\n\n // Check for errors first, before yielding any chunks\n if (request.error) {\n throw new Error(request.error)\n }\n\n // Yield all available chunks\n while (request.chunks.length > 0) {\n yield request.chunks.shift()!\n }\n }\n } finally {\n streamingFetchRequests.delete(requestId)\n }\n },\n }\n ;(context as { network: NetworkAPI }).network = networkApi\n }\n\n // Add settings API if permitted\n if (hasPermission('settings.register')) {\n const settingsApi: SettingsAPI = {\n async getAll<T extends Record<string, unknown>>(): Promise<T> {\n return sendRequest<T>('settings.getAll', {})\n },\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('settings.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('settings.set', { key, value })\n },\n onChange(callback: (key: string, value: unknown) => void): Disposable {\n settingsCallbacks.push(callback)\n return {\n dispose: () => {\n const index = settingsCallbacks.indexOf(callback)\n if (index >= 0) settingsCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { settings: SettingsAPI }).settings = settingsApi\n }\n\n // Add providers API if permitted\n if (hasPermission('provider.register')) {\n const providersApi: ProvidersAPI = {\n register(provider: AIProvider): Disposable {\n registeredProviders.set(provider.id, provider)\n postMessage({\n type: 'provider-registered',\n payload: { id: provider.id, name: provider.name },\n })\n return {\n dispose: () => {\n registeredProviders.delete(provider.id)\n },\n }\n },\n }\n ;(context as { providers: ProvidersAPI }).providers = providersApi\n }\n\n // Add tools API if permitted\n if (hasPermission('tools.register')) {\n const toolsApi: ToolsAPI = {\n register(tool: Tool): Disposable {\n registeredTools.set(tool.id, tool)\n postMessage({\n type: 'tool-registered',\n payload: {\n id: tool.id,\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n })\n return {\n dispose: () => {\n registeredTools.delete(tool.id)\n },\n }\n },\n }\n ;(context as { tools: ToolsAPI }).tools = toolsApi\n }\n\n // Add actions API if permitted\n if (hasPermission('actions.register')) {\n const actionsApi: ActionsAPI = {\n register(action: Action): Disposable {\n registeredActions.set(action.id, action)\n postMessage({\n type: 'action-registered',\n payload: {\n id: action.id,\n },\n })\n return {\n dispose: () => {\n registeredActions.delete(action.id)\n },\n }\n },\n }\n ;(context as { actions: ActionsAPI }).actions = actionsApi\n }\n\n // Add events API if permitted\n if (hasPermission('events.emit')) {\n const eventsApi: EventsAPI = {\n async emit(name: string, payload?: Record<string, unknown>): Promise<void> {\n await sendRequest<void>('events.emit', { name, payload })\n },\n }\n ;(context as { events: EventsAPI }).events = eventsApi\n }\n\n // Add scheduler API if permitted\n if (hasPermission('scheduler.register')) {\n const schedulerApi: SchedulerAPI = {\n async schedule(job: SchedulerJobRequest): Promise<void> {\n await sendRequest<void>('scheduler.schedule', { job })\n },\n async cancel(jobId: string): Promise<void> {\n await sendRequest<void>('scheduler.cancel', { jobId })\n },\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable {\n schedulerCallbacks.push(callback)\n return {\n dispose: () => {\n const index = schedulerCallbacks.indexOf(callback)\n if (index >= 0) schedulerCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { scheduler: SchedulerAPI }).scheduler = schedulerApi\n }\n\n // Add user profile API if permitted\n if (hasPermission('user.profile.read')) {\n const userApi: UserAPI = {\n async getProfile(): Promise<UserProfile> {\n return sendRequest<UserProfile>('user.getProfile', {})\n },\n }\n ;(context as { user: UserAPI }).user = userApi\n }\n\n // Add chat API if permitted\n if (hasPermission('chat.message.write')) {\n const chatApi: ChatAPI = {\n async appendInstruction(message: ChatInstructionMessage): Promise<void> {\n await sendRequest<void>('chat.appendInstruction', message)\n },\n }\n ;(context as { chat: ChatAPI }).chat = chatApi\n }\n\n // Add database API if permitted\n if (hasPermission('database.own')) {\n const databaseApi: DatabaseAPI = {\n async execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {\n return sendRequest<T[]>('database.execute', { sql, params })\n },\n }\n ;(context as { database: DatabaseAPI }).database = databaseApi\n }\n\n // Add storage API if permitted\n if (hasPermission('storage.local')) {\n const storageApi: StorageAPI = {\n // Global/extension-scoped storage\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.set', { key, value })\n },\n async delete(key: string): Promise<void> {\n return sendRequest<void>('storage.delete', { key })\n },\n async keys(): Promise<string[]> {\n return sendRequest<string[]>('storage.keys', {})\n },\n // User-scoped storage\n async getForUser<T>(userId: string, key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.getForUser', { userId, key })\n },\n async setForUser(userId: string, key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.setForUser', { userId, key, value })\n },\n async deleteForUser(userId: string, key: string): Promise<void> {\n return sendRequest<void>('storage.deleteForUser', { userId, key })\n },\n async keysForUser(userId: string): Promise<string[]> {\n return sendRequest<string[]>('storage.keysForUser', { userId })\n },\n }\n ;(context as { storage: StorageAPI }).storage = storageApi\n }\n\n return context\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the extension runtime\n * This should be called by the extension's entry point\n */\nexport function initializeExtension(module: ExtensionModule): void {\n extensionModule = module\n\n // Set up message listener using the appropriate message port\n messagePort.onMessage(async (message: HostToWorkerMessage) => {\n try {\n await handleHostMessage(message)\n } catch (error) {\n console.error('Error handling message:', error)\n }\n })\n\n // Signal that we're ready\n postMessage({ type: 'ready' })\n}\n\n// Re-export types for extensions to use\nexport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n AIProvider,\n Tool,\n ToolDefinition,\n ToolResult,\n ToolCall,\n Action,\n ActionResult,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n} from './types.js'\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * Extension Runtime - Runs inside the worker\n *\n * This module handles communication with the Extension Host and provides\n * the ExtensionContext to the extension's activate function.\n */\n\nimport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n AIProvider,\n Tool,\n Action,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n} from './types.js'\n\nimport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n RequestMessage,\n PendingRequest,\n} from './messages.js'\n\nimport { generateMessageId } from './messages.js'\n\n// ============================================================================\n// Environment Detection and Message Port\n// ============================================================================\n\n/**\n * Detect if we're in Node.js Worker Thread or Web Worker\n * and get the appropriate message port\n */\ninterface MessagePort {\n postMessage(message: WorkerToHostMessage): void\n onMessage(handler: (message: HostToWorkerMessage) => void): void\n}\n\nfunction getMessagePort(): MessagePort {\n // Check if we're in Node.js Worker Thread\n if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js Worker Thread - import parentPort dynamically\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parentPort } = require('node:worker_threads')\n return {\n postMessage: (message) => parentPort?.postMessage(message),\n onMessage: (handler) => parentPort?.on('message', handler),\n }\n }\n\n // Web Worker - use self\n return {\n postMessage: (message) => self.postMessage(message),\n onMessage: (handler) => {\n self.addEventListener('message', (event: MessageEvent<HostToWorkerMessage>) => {\n handler(event.data)\n })\n },\n }\n}\n\nconst messagePort = getMessagePort()\n\n// ============================================================================\n// Global State\n// ============================================================================\n\nlet extensionModule: ExtensionModule | null = null\nlet extensionDisposable: Disposable | null = null\nlet extensionContext: ExtensionContext | null = null\n\nconst pendingRequests = new Map<string, PendingRequest>()\nconst registeredProviders = new Map<string, AIProvider>()\nconst registeredTools = new Map<string, Tool>()\nconst registeredActions = new Map<string, Action>()\nconst settingsCallbacks: Array<(key: string, value: unknown) => void> = []\nconst schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void | Promise<void>> = []\n\n/**\n * Tracking for streaming fetch requests.\n * Each request stores incoming chunks and signals when new data arrives.\n */\ninterface StreamingFetchRequest {\n chunks: string[]\n done: boolean\n error?: string\n resolve?: () => void\n}\nconst streamingFetchRequests = new Map<string, StreamingFetchRequest>()\n\nconst REQUEST_TIMEOUT = 30000 // 30 seconds\n\n// ============================================================================\n// Message Handling\n// ============================================================================\n\n/**\n * Send a message to the host\n */\nfunction postMessage(message: WorkerToHostMessage): void {\n messagePort.postMessage(message)\n}\n\n/**\n * Send a request to the host and wait for response\n */\nasync function sendRequest<T>(method: RequestMessage['method'], payload: unknown): Promise<T> {\n const id = generateMessageId()\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n pendingRequests.delete(id)\n reject(new Error(`Request ${method} timed out`))\n }, REQUEST_TIMEOUT)\n\n pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timeout })\n\n postMessage({\n type: 'request',\n id,\n method,\n payload,\n })\n })\n}\n\n/**\n * Handle messages from the host\n */\nasync function handleHostMessage(message: HostToWorkerMessage): Promise<void> {\n switch (message.type) {\n case 'activate':\n await handleActivate(message.payload)\n break\n\n case 'deactivate':\n await handleDeactivate()\n break\n\n case 'settings-changed':\n handleSettingsChanged(message.payload.key, message.payload.value)\n break\n\n case 'scheduler-fire':\n await handleSchedulerFire(message.payload)\n break\n\n case 'provider-chat-request':\n await handleProviderChatRequest(message.id, message.payload)\n break\n\n case 'provider-models-request':\n await handleProviderModelsRequest(message.id, message.payload)\n break\n\n case 'tool-execute-request':\n await handleToolExecuteRequest(message.id, message.payload)\n break\n\n case 'action-execute-request':\n await handleActionExecuteRequest(message.id, message.payload)\n break\n\n case 'response':\n handleResponse(message.payload)\n break\n\n case 'streaming-fetch-chunk':\n handleStreamingFetchChunk(message.payload)\n break\n }\n}\n\n/**\n * Handle incoming streaming fetch chunks from the host\n */\nfunction handleStreamingFetchChunk(payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n}): void {\n const request = streamingFetchRequests.get(payload.requestId)\n if (!request) return\n\n if (payload.error) {\n request.error = payload.error\n request.done = true\n } else if (payload.chunk) {\n request.chunks.push(payload.chunk)\n }\n\n if (payload.done) {\n request.done = true\n }\n\n // Signal that new data is available\n if (request.resolve) {\n const resolve = request.resolve\n request.resolve = undefined\n resolve()\n }\n\n // Send acknowledgment for backpressure control\n postMessage({\n type: 'streaming-fetch-ack',\n payload: { requestId: payload.requestId },\n })\n}\n\nfunction handleResponse(payload: { requestId: string; success: boolean; data?: unknown; error?: string }): void {\n const pending = pendingRequests.get(payload.requestId)\n if (!pending) return\n\n clearTimeout(pending.timeout)\n pendingRequests.delete(payload.requestId)\n\n if (payload.success) {\n pending.resolve(payload.data)\n } else {\n pending.reject(new Error(payload.error || 'Unknown error'))\n }\n}\n\n// ============================================================================\n// Activation / Deactivation\n// ============================================================================\n\nasync function handleActivate(payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n}): Promise<void> {\n const { extensionId, extensionVersion, storagePath, permissions } = payload\n\n // Build the context based on permissions\n extensionContext = buildContext(extensionId, extensionVersion, storagePath, permissions)\n\n // Import and activate the extension\n try {\n // The actual extension code should be bundled and available\n // This is called after the extension code has been loaded into the worker\n if (extensionModule?.activate) {\n const result = await extensionModule.activate(extensionContext)\n if (result && 'dispose' in result) {\n extensionDisposable = result\n }\n }\n } catch (error) {\n extensionContext.log.error('Failed to activate extension', {\n error: error instanceof Error ? error.message : String(error),\n })\n throw error\n }\n}\n\nasync function handleDeactivate(): Promise<void> {\n try {\n if (extensionModule?.deactivate) {\n await extensionModule.deactivate()\n }\n if (extensionDisposable) {\n extensionDisposable.dispose()\n }\n } catch (error) {\n console.error('Error during deactivation:', error)\n } finally {\n extensionModule = null\n extensionDisposable = null\n extensionContext = null\n registeredProviders.clear()\n registeredTools.clear()\n registeredActions.clear()\n settingsCallbacks.length = 0\n schedulerCallbacks.length = 0\n }\n}\n\nfunction handleSettingsChanged(key: string, value: unknown): void {\n for (const callback of settingsCallbacks) {\n try {\n callback(key, value)\n } catch (error) {\n console.error('Error in settings change callback:', error)\n }\n }\n}\n\nasync function handleSchedulerFire(payload: SchedulerFirePayload): Promise<void> {\n // Set the userId in context if this is a user-scoped job\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n // Run callbacks concurrently to avoid blocking\n const results = await Promise.allSettled(\n schedulerCallbacks.map((callback) => callback(payload)),\n )\n\n // Log any errors\n results.forEach((result, index) => {\n if (result.status === 'rejected') {\n console.error(`Error in scheduler callback ${index}:`, result.reason)\n }\n })\n\n // Reset userId after all callbacks have completed\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n}\n\n// ============================================================================\n// Provider / Tool Requests\n// ============================================================================\n\nasync function handleProviderChatRequest(\n requestId: string,\n payload: { providerId: string; messages: ChatMessage[]; options: ChatOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n postMessage({\n type: 'request',\n id: generateMessageId(),\n method: 'network.fetch', // Dummy, we need a proper error response\n payload: { error: `Provider ${payload.providerId} not found` },\n })\n return\n }\n\n try {\n const generator = provider.chat(payload.messages, payload.options)\n let sawDone = false\n let sawError = false\n\n for await (const event of generator) {\n if (event.type === 'done') {\n sawDone = true\n } else if (event.type === 'error') {\n sawError = true\n }\n postMessage({\n type: 'stream-event',\n payload: { requestId, event },\n })\n }\n\n if (!sawDone && !sawError) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: { type: 'done' },\n },\n })\n }\n } catch (error) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: {\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n },\n },\n })\n }\n}\n\nasync function handleProviderModelsRequest(\n requestId: string,\n payload: { providerId: string; options?: GetModelsOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n // Send error response\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: `Provider ${payload.providerId} not found`,\n },\n })\n return\n }\n\n try {\n // Pass options to getModels so provider can use settings (e.g., URL for Ollama)\n const models = await provider.getModels(payload.options)\n // Send response with models\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models,\n },\n })\n } catch (error) {\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: error instanceof Error ? error.message : String(error),\n },\n })\n }\n}\n\nasync function handleToolExecuteRequest(\n requestId: string,\n payload: { toolId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const tool = registeredTools.get(payload.toolId)\n if (!tool) {\n // Send error response\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Tool ${payload.toolId} not found` },\n error: `Tool ${payload.toolId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n // Create a new context with the userId for this execution\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await tool.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n // Send response with result\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\nasync function handleActionExecuteRequest(\n requestId: string,\n payload: { actionId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const action = registeredActions.get(payload.actionId)\n if (!action) {\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Action ${payload.actionId} not found` },\n error: `Action ${payload.actionId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await action.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\n// ============================================================================\n// Context Building\n// ============================================================================\n\nfunction buildContext(\n extensionId: string,\n extensionVersion: string,\n storagePath: string,\n permissions: string[]\n): ExtensionContext {\n const hasPermission = (perm: string): boolean => {\n return permissions.some((p) => {\n if (p === perm) return true\n if (p.endsWith(':*') && perm.startsWith(p.slice(0, -1))) return true\n return false\n })\n }\n\n const log: LogAPI = {\n debug: (message, data) => postMessage({ type: 'log', payload: { level: 'debug', message, data } }),\n info: (message, data) => postMessage({ type: 'log', payload: { level: 'info', message, data } }),\n warn: (message, data) => postMessage({ type: 'log', payload: { level: 'warn', message, data } }),\n error: (message, data) => postMessage({ type: 'log', payload: { level: 'error', message, data } }),\n }\n\n const context: ExtensionContext = {\n extension: {\n id: extensionId,\n version: extensionVersion,\n storagePath,\n },\n log,\n }\n\n // Add network API if permitted\n if (permissions.some((p) => p.startsWith('network:'))) {\n const networkApi: NetworkAPI = {\n async fetch(url: string, options?: RequestInit): Promise<Response> {\n const result = await sendRequest<{ status: number; statusText: string; headers: Record<string, string>; body: string }>('network.fetch', { url, options })\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers: result.headers,\n })\n },\n\n async *fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown> {\n const requestId = generateMessageId()\n\n // Set up streaming request tracking\n const request: StreamingFetchRequest = {\n chunks: [],\n done: false,\n }\n streamingFetchRequests.set(requestId, request)\n\n // Send the streaming fetch request to the host\n postMessage({\n type: 'request',\n id: requestId,\n method: 'network.fetch-stream',\n payload: { url, options, requestId },\n })\n\n try {\n // Yield chunks as they arrive\n while (!request.done) {\n // Wait for new data\n await new Promise<void>((resolve) => {\n const resolver = () => {\n // Clear the stored resolver before resolving to avoid\n // stale callbacks being invoked for a new wait iteration.\n if (request.resolve === resolver) {\n request.resolve = undefined\n }\n resolve()\n }\n\n request.resolve = resolver\n\n // Check if we already have data or completion\n if (request.chunks.length > 0 || request.done) {\n resolver()\n }\n })\n\n // Check for errors first, before yielding any chunks\n if (request.error) {\n throw new Error(request.error)\n }\n\n // Yield all available chunks\n while (request.chunks.length > 0) {\n yield request.chunks.shift()!\n }\n }\n } finally {\n streamingFetchRequests.delete(requestId)\n }\n },\n }\n ;(context as { network: NetworkAPI }).network = networkApi\n }\n\n // Add settings API if permitted\n if (hasPermission('settings.register')) {\n const settingsApi: SettingsAPI = {\n async getAll<T extends Record<string, unknown>>(): Promise<T> {\n return sendRequest<T>('settings.getAll', {})\n },\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('settings.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('settings.set', { key, value })\n },\n onChange(callback: (key: string, value: unknown) => void): Disposable {\n settingsCallbacks.push(callback)\n return {\n dispose: () => {\n const index = settingsCallbacks.indexOf(callback)\n if (index >= 0) settingsCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { settings: SettingsAPI }).settings = settingsApi\n }\n\n // Add providers API if permitted\n if (hasPermission('provider.register')) {\n const providersApi: ProvidersAPI = {\n register(provider: AIProvider): Disposable {\n registeredProviders.set(provider.id, provider)\n postMessage({\n type: 'provider-registered',\n payload: { id: provider.id, name: provider.name },\n })\n return {\n dispose: () => {\n registeredProviders.delete(provider.id)\n },\n }\n },\n }\n ;(context as { providers: ProvidersAPI }).providers = providersApi\n }\n\n // Add tools API if permitted\n if (hasPermission('tools.register')) {\n const toolsApi: ToolsAPI = {\n register(tool: Tool): Disposable {\n registeredTools.set(tool.id, tool)\n postMessage({\n type: 'tool-registered',\n payload: {\n id: tool.id,\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n })\n return {\n dispose: () => {\n registeredTools.delete(tool.id)\n },\n }\n },\n }\n ;(context as { tools: ToolsAPI }).tools = toolsApi\n }\n\n // Add actions API if permitted\n if (hasPermission('actions.register')) {\n const actionsApi: ActionsAPI = {\n register(action: Action): Disposable {\n registeredActions.set(action.id, action)\n postMessage({\n type: 'action-registered',\n payload: {\n id: action.id,\n },\n })\n return {\n dispose: () => {\n registeredActions.delete(action.id)\n },\n }\n },\n }\n ;(context as { actions: ActionsAPI }).actions = actionsApi\n }\n\n // Add events API if permitted\n if (hasPermission('events.emit')) {\n const eventsApi: EventsAPI = {\n async emit(name: string, payload?: Record<string, unknown>): Promise<void> {\n await sendRequest<void>('events.emit', { name, payload })\n },\n }\n ;(context as { events: EventsAPI }).events = eventsApi\n }\n\n // Add scheduler API if permitted\n if (hasPermission('scheduler.register')) {\n const schedulerApi: SchedulerAPI = {\n async schedule(job: SchedulerJobRequest): Promise<void> {\n // Automatically include userId from context if not explicitly set\n const userId = (extensionContext as { userId?: string }).userId\n const jobWithUser = userId && !job.userId ? { ...job, userId } : job\n await sendRequest<void>('scheduler.schedule', { job: jobWithUser })\n },\n async cancel(jobId: string): Promise<void> {\n await sendRequest<void>('scheduler.cancel', { jobId })\n },\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable {\n schedulerCallbacks.push(callback)\n return {\n dispose: () => {\n const index = schedulerCallbacks.indexOf(callback)\n if (index >= 0) schedulerCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { scheduler: SchedulerAPI }).scheduler = schedulerApi\n }\n\n // Add user profile API if permitted\n if (hasPermission('user.profile.read')) {\n const userApi: UserAPI = {\n async getProfile(): Promise<UserProfile> {\n return sendRequest<UserProfile>('user.getProfile', {})\n },\n }\n ;(context as { user: UserAPI }).user = userApi\n }\n\n // Add chat API if permitted\n if (hasPermission('chat.message.write')) {\n const chatApi: ChatAPI = {\n async appendInstruction(message: ChatInstructionMessage): Promise<void> {\n const contextUserId = (extensionContext as { userId?: string }).userId\n await sendRequest<void>('chat.appendInstruction', {\n ...message,\n userId: message.userId ?? contextUserId,\n })\n },\n }\n ;(context as { chat: ChatAPI }).chat = chatApi\n }\n\n // Add database API if permitted\n if (hasPermission('database.own')) {\n const databaseApi: DatabaseAPI = {\n async execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {\n return sendRequest<T[]>('database.execute', { sql, params })\n },\n }\n ;(context as { database: DatabaseAPI }).database = databaseApi\n }\n\n // Add storage API if permitted\n if (hasPermission('storage.local')) {\n const storageApi: StorageAPI = {\n // Global/extension-scoped storage\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.set', { key, value })\n },\n async delete(key: string): Promise<void> {\n return sendRequest<void>('storage.delete', { key })\n },\n async keys(): Promise<string[]> {\n return sendRequest<string[]>('storage.keys', {})\n },\n // User-scoped storage\n async getForUser<T>(userId: string, key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.getForUser', { userId, key })\n },\n async setForUser(userId: string, key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.setForUser', { userId, key, value })\n },\n async deleteForUser(userId: string, key: string): Promise<void> {\n return sendRequest<void>('storage.deleteForUser', { userId, key })\n },\n async keysForUser(userId: string): Promise<string[]> {\n return sendRequest<string[]>('storage.keysForUser', { userId })\n },\n }\n ;(context as { storage: StorageAPI }).storage = storageApi\n }\n\n return context\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the extension runtime\n * This should be called by the extension's entry point\n */\nexport function initializeExtension(module: ExtensionModule): void {\n extensionModule = module\n\n // Set up message listener using the appropriate message port\n messagePort.onMessage(async (message: HostToWorkerMessage) => {\n try {\n await handleHostMessage(message)\n } catch (error) {\n console.error('Error handling message:', error)\n }\n })\n\n // Signal that we're ready\n postMessage({ type: 'ready' })\n}\n\n// Re-export types for extensions to use\nexport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n AIProvider,\n Tool,\n ToolDefinition,\n ToolResult,\n ToolCall,\n Action,\n ActionResult,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n} from './types.js'\n"],"mappings":"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","names":[]}
|
|
@@ -915,7 +915,7 @@ interface SchedulerFirePayload {
|
|
|
915
915
|
interface SchedulerAPI {
|
|
916
916
|
schedule(job: SchedulerJobRequest): Promise<void>;
|
|
917
917
|
cancel(jobId: string): Promise<void>;
|
|
918
|
-
onFire(callback: (payload: SchedulerFirePayload) => void): Disposable;
|
|
918
|
+
onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable;
|
|
919
919
|
}
|
|
920
920
|
/**
|
|
921
921
|
* User profile data
|
|
@@ -938,6 +938,7 @@ interface UserAPI {
|
|
|
938
938
|
interface ChatInstructionMessage {
|
|
939
939
|
text: string;
|
|
940
940
|
conversationId?: string;
|
|
941
|
+
userId?: string;
|
|
941
942
|
}
|
|
942
943
|
/**
|
|
943
944
|
* Chat API for appending instructions
|
|
@@ -915,7 +915,7 @@ interface SchedulerFirePayload {
|
|
|
915
915
|
interface SchedulerAPI {
|
|
916
916
|
schedule(job: SchedulerJobRequest): Promise<void>;
|
|
917
917
|
cancel(jobId: string): Promise<void>;
|
|
918
|
-
onFire(callback: (payload: SchedulerFirePayload) => void): Disposable;
|
|
918
|
+
onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable;
|
|
919
919
|
}
|
|
920
920
|
/**
|
|
921
921
|
* User profile data
|
|
@@ -938,6 +938,7 @@ interface UserAPI {
|
|
|
938
938
|
interface ChatInstructionMessage {
|
|
939
939
|
text: string;
|
|
940
940
|
conversationId?: string;
|
|
941
|
+
userId?: string;
|
|
941
942
|
}
|
|
942
943
|
/**
|
|
943
944
|
* Chat API for appending instructions
|
package/package.json
CHANGED
package/src/runtime.ts
CHANGED
|
@@ -93,7 +93,7 @@ const registeredProviders = new Map<string, AIProvider>()
|
|
|
93
93
|
const registeredTools = new Map<string, Tool>()
|
|
94
94
|
const registeredActions = new Map<string, Action>()
|
|
95
95
|
const settingsCallbacks: Array<(key: string, value: unknown) => void> = []
|
|
96
|
-
const schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void
|
|
96
|
+
const schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void | Promise<void>> = []
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
99
|
* Tracking for streaming fetch requests.
|
|
@@ -161,7 +161,7 @@ async function handleHostMessage(message: HostToWorkerMessage): Promise<void> {
|
|
|
161
161
|
break
|
|
162
162
|
|
|
163
163
|
case 'scheduler-fire':
|
|
164
|
-
handleSchedulerFire(message.payload)
|
|
164
|
+
await handleSchedulerFire(message.payload)
|
|
165
165
|
break
|
|
166
166
|
|
|
167
167
|
case 'provider-chat-request':
|
|
@@ -307,21 +307,25 @@ function handleSettingsChanged(key: string, value: unknown): void {
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
function handleSchedulerFire(payload: SchedulerFirePayload): void {
|
|
310
|
+
async function handleSchedulerFire(payload: SchedulerFirePayload): Promise<void> {
|
|
311
311
|
// Set the userId in context if this is a user-scoped job
|
|
312
312
|
if (extensionContext && payload.userId) {
|
|
313
313
|
;(extensionContext as { userId?: string }).userId = payload.userId
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
316
|
+
// Run callbacks concurrently to avoid blocking
|
|
317
|
+
const results = await Promise.allSettled(
|
|
318
|
+
schedulerCallbacks.map((callback) => callback(payload)),
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
// Log any errors
|
|
322
|
+
results.forEach((result, index) => {
|
|
323
|
+
if (result.status === 'rejected') {
|
|
324
|
+
console.error(`Error in scheduler callback ${index}:`, result.reason)
|
|
321
325
|
}
|
|
322
|
-
}
|
|
326
|
+
})
|
|
323
327
|
|
|
324
|
-
// Reset userId after all callbacks
|
|
328
|
+
// Reset userId after all callbacks have completed
|
|
325
329
|
if (extensionContext) {
|
|
326
330
|
;(extensionContext as { userId?: string }).userId = undefined
|
|
327
331
|
}
|
|
@@ -747,7 +751,10 @@ function buildContext(
|
|
|
747
751
|
if (hasPermission('scheduler.register')) {
|
|
748
752
|
const schedulerApi: SchedulerAPI = {
|
|
749
753
|
async schedule(job: SchedulerJobRequest): Promise<void> {
|
|
750
|
-
|
|
754
|
+
// Automatically include userId from context if not explicitly set
|
|
755
|
+
const userId = (extensionContext as { userId?: string }).userId
|
|
756
|
+
const jobWithUser = userId && !job.userId ? { ...job, userId } : job
|
|
757
|
+
await sendRequest<void>('scheduler.schedule', { job: jobWithUser })
|
|
751
758
|
},
|
|
752
759
|
async cancel(jobId: string): Promise<void> {
|
|
753
760
|
await sendRequest<void>('scheduler.cancel', { jobId })
|
|
@@ -779,7 +786,11 @@ function buildContext(
|
|
|
779
786
|
if (hasPermission('chat.message.write')) {
|
|
780
787
|
const chatApi: ChatAPI = {
|
|
781
788
|
async appendInstruction(message: ChatInstructionMessage): Promise<void> {
|
|
782
|
-
|
|
789
|
+
const contextUserId = (extensionContext as { userId?: string }).userId
|
|
790
|
+
await sendRequest<void>('chat.appendInstruction', {
|
|
791
|
+
...message,
|
|
792
|
+
userId: message.userId ?? contextUserId,
|
|
793
|
+
})
|
|
783
794
|
},
|
|
784
795
|
}
|
|
785
796
|
;(context as { chat: ChatAPI }).chat = chatApi
|
package/src/types.ts
CHANGED
|
@@ -717,7 +717,7 @@ export interface SchedulerFirePayload {
|
|
|
717
717
|
export interface SchedulerAPI {
|
|
718
718
|
schedule(job: SchedulerJobRequest): Promise<void>
|
|
719
719
|
cancel(jobId: string): Promise<void>
|
|
720
|
-
onFire(callback: (payload: SchedulerFirePayload) => void): Disposable
|
|
720
|
+
onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable
|
|
721
721
|
}
|
|
722
722
|
|
|
723
723
|
/**
|
|
@@ -743,6 +743,7 @@ export interface UserAPI {
|
|
|
743
743
|
export interface ChatInstructionMessage {
|
|
744
744
|
text: string
|
|
745
745
|
conversationId?: string
|
|
746
|
+
userId?: string
|
|
746
747
|
}
|
|
747
748
|
|
|
748
749
|
/**
|