@xopcai/xopc 0.0.23 → 0.0.24
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/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/agents-CiZMJZRp.js +216 -0
- package/dist/gateway/static/root/assets/agents-CiZMJZRp.js.map +1 -0
- package/dist/gateway/static/root/assets/apps-page-tZz69XM3.js +2 -0
- package/dist/gateway/static/root/assets/apps-page-tZz69XM3.js.map +1 -0
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js → attachment-preview-renderer-CxMJMbD2.js} +4 -4
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js.map → attachment-preview-renderer-CxMJMbD2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js → attachment-process-heavy-EFXPGfWk.js} +6 -6
- package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js.map → attachment-process-heavy-EFXPGfWk.js.map} +1 -1
- package/dist/gateway/static/root/assets/{attachment-utils-core-Dt6UxMPV.js → attachment-utils-core-ECbeoa9H.js} +1 -1
- package/dist/gateway/static/root/assets/attachment-utils-core-ECbeoa9H.js.map +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BAvk9-aK.js +9 -0
- package/dist/gateway/static/root/assets/{channels-settings-CGzrrBlT.js.map → channels-settings-BAvk9-aK.js.map} +1 -1
- package/dist/gateway/static/root/assets/cn-BMCV0OMB.js +2 -0
- package/dist/gateway/static/root/assets/cn-BMCV0OMB.js.map +1 -0
- package/dist/gateway/static/root/assets/cron-page-CANqvhK7.js +2 -0
- package/dist/gateway/static/root/assets/{cron-page-BGCdDf2w.js.map → cron-page-CANqvhK7.js.map} +1 -1
- package/dist/gateway/static/root/assets/cron-utils-DyOO6TdN.js +3 -0
- package/dist/gateway/static/root/assets/{cron-utils-Dnks4wWv.js.map → cron-utils-DyOO6TdN.js.map} +1 -1
- package/dist/gateway/static/root/assets/dist-Brod9LF3.js +2 -0
- package/dist/gateway/static/root/assets/{dist-BG1ChbY9.js.map → dist-Brod9LF3.js.map} +1 -1
- package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js → docx-preview-F-jKDMNv.js} +2 -2
- package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js.map → docx-preview-F-jKDMNv.js.map} +1 -1
- package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js → excel-worksheet-utils-DPfAinZn.js} +1 -1
- package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js.map → excel-worksheet-utils-DPfAinZn.js.map} +1 -1
- package/dist/gateway/static/root/assets/extension-debug-page-CDD7ozsC.js +2 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-CRC16AbL.js.map → extension-debug-page-CDD7ozsC.js.map} +1 -1
- package/dist/gateway/static/root/assets/extension-page-UUFMjoWf.js +2 -0
- package/dist/gateway/static/root/assets/{extension-page-BagrJnbm.js.map → extension-page-UUFMjoWf.js.map} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-CP9JNc4m.js +2 -0
- package/dist/gateway/static/root/assets/extension-settings-page-CP9JNc4m.js.map +1 -0
- package/dist/gateway/static/root/assets/index-BZvlG48D.js +150 -0
- package/dist/gateway/static/root/assets/index-BZvlG48D.js.map +1 -0
- package/dist/gateway/static/root/assets/index-DxkgyT8R.css +1 -0
- package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js → jszip.min-CL3dfyxs.js} +1 -1
- package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js.map → jszip.min-CL3dfyxs.js.map} +1 -1
- package/dist/gateway/static/root/assets/logs-page-Cr0eCGb4.js +2 -0
- package/dist/gateway/static/root/assets/{logs-page-Bo9EsE_D.js.map → logs-page-Cr0eCGb4.js.map} +1 -1
- package/dist/gateway/static/root/assets/{pdf--jE7rvON.js → pdf-CX6ji-QC.js} +1 -1
- package/dist/gateway/static/root/assets/{pdf--jE7rvON.js.map → pdf-CX6ji-QC.js.map} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DwLHN5GJ.js +2 -0
- package/dist/gateway/static/root/assets/{sessions-page-CDgXq8qp.js.map → sessions-page-DwLHN5GJ.js.map} +1 -1
- package/dist/gateway/static/root/assets/settings-page-B3O3R0E4.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-B3O3R0E4.js.map +1 -0
- package/dist/gateway/static/root/assets/skills-page-DgBYvH6B.js +3 -0
- package/dist/gateway/static/root/assets/{skills-page-BRS5qYTw.js.map → skills-page-DgBYvH6B.js.map} +1 -1
- package/dist/gateway/static/root/assets/vendor-swr-B5fPo7KK.js +2 -0
- package/dist/gateway/static/root/assets/{vendor-swr-Dp4nzp5h.js.map → vendor-swr-B5fPo7KK.js.map} +1 -1
- package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js → xlsx-CPtvfmVF.js} +1 -1
- package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js.map → xlsx-CPtvfmVF.js.map} +1 -1
- package/dist/gateway/static/root/index.html +5 -4
- package/dist/package.js +1 -1
- package/dist/src/agent/image/tool-model-config.js +2 -2
- package/dist/src/agent/image/tool-model-config.js.map +1 -1
- package/dist/src/agent/tools/execute-code-tool.js +1 -1
- package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
- package/dist/src/cli/commands/extension-dev.d.ts +2 -0
- package/dist/src/cli/commands/extension-dev.js +196 -0
- package/dist/src/cli/commands/extension-dev.js.map +1 -0
- package/dist/src/cli/commands/extension-marketplace.d.ts +4 -0
- package/dist/src/cli/commands/extension-marketplace.js +145 -0
- package/dist/src/cli/commands/extension-marketplace.js.map +1 -0
- package/dist/src/cli/commands/extension-pack.d.ts +2 -0
- package/dist/src/cli/commands/extension-pack.js +242 -0
- package/dist/src/cli/commands/extension-pack.js.map +1 -0
- package/dist/src/cli/commands/extension.js +13 -0
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/index.js +5 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/schema.js +1 -1
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/extensions/api.d.ts +6 -1
- package/dist/src/extensions/api.js +30 -0
- package/dist/src/extensions/api.js.map +1 -1
- package/dist/src/extensions/engine-check.d.ts +14 -0
- package/dist/src/extensions/engine-check.js +148 -0
- package/dist/src/extensions/engine-check.js.map +1 -0
- package/dist/src/extensions/loader.js +23 -0
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/marketplace.d.ts +24 -0
- package/dist/src/extensions/marketplace.js +98 -0
- package/dist/src/extensions/marketplace.js.map +1 -0
- package/dist/src/extensions/normalize-manifest.js +16 -4
- package/dist/src/extensions/normalize-manifest.js.map +1 -1
- package/dist/src/extensions/sdk/index.d.ts +2 -0
- package/dist/src/extensions/sdk/index.js +2 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/sdk/testing.d.ts +49 -3
- package/dist/src/extensions/sdk/testing.js +174 -5
- package/dist/src/extensions/sdk/testing.js.map +1 -1
- package/dist/src/extensions/types/core.d.ts +5 -0
- package/dist/src/extensions/types/manifest.d.ts +17 -0
- package/dist/src/extensions/when-context.d.ts +6 -0
- package/dist/src/extensions/when-context.js +28 -0
- package/dist/src/extensions/when-context.js.map +1 -0
- package/dist/src/extensions/when-expression.d.ts +11 -0
- package/dist/src/extensions/when-expression.js +215 -0
- package/dist/src/extensions/when-expression.js.map +1 -0
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +28 -0
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
- package/dist/src/providers/index.d.ts +6 -3
- package/dist/src/providers/index.js +12 -23
- package/dist/src/providers/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/gateway/static/root/assets/agents-BY_DaknM.js +0 -216
- package/dist/gateway/static/root/assets/agents-BY_DaknM.js.map +0 -1
- package/dist/gateway/static/root/assets/apps-page-BO3nQbJY.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-BO3nQbJY.js.map +0 -1
- package/dist/gateway/static/root/assets/attachment-utils-core-Dt6UxMPV.js.map +0 -1
- package/dist/gateway/static/root/assets/channels-settings-CGzrrBlT.js +0 -9
- package/dist/gateway/static/root/assets/cron-page-BGCdDf2w.js +0 -2
- package/dist/gateway/static/root/assets/cron-utils-Dnks4wWv.js +0 -3
- package/dist/gateway/static/root/assets/dist-BG1ChbY9.js +0 -2
- package/dist/gateway/static/root/assets/extension-debug-page-CRC16AbL.js +0 -2
- package/dist/gateway/static/root/assets/extension-page-BagrJnbm.js +0 -2
- package/dist/gateway/static/root/assets/extension-settings-page-DEpxRKKK.js +0 -2
- package/dist/gateway/static/root/assets/extension-settings-page-DEpxRKKK.js.map +0 -1
- package/dist/gateway/static/root/assets/index-CYVs9x8D.css +0 -1
- package/dist/gateway/static/root/assets/index-KNzRh6gu.js +0 -150
- package/dist/gateway/static/root/assets/index-KNzRh6gu.js.map +0 -1
- package/dist/gateway/static/root/assets/logs-page-Bo9EsE_D.js +0 -2
- package/dist/gateway/static/root/assets/sessions-page-CDgXq8qp.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-BWMTFST6.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-BWMTFST6.js.map +0 -1
- package/dist/gateway/static/root/assets/skills-page-BRS5qYTw.js +0 -3
- package/dist/gateway/static/root/assets/vendor-swr-Dp4nzp5h.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/extensions/sdk/index.ts"],"sourcesContent":["/**\n * xopc Extension SDK\n * \n * Official SDK for developing xopc extensions.\n * Import types and utilities from this module:\n * \n * @example\n * import type { ExtensionApi, ExtensionDefinition } from 'xopc/extension-sdk';\n */\n\n// ============================================================================\n// Core extension types\n// ============================================================================\n\nexport type {\n ExtensionDefinition,\n ExtensionModule,\n ExtensionKind,\n ExtensionApi,\n ExtensionLogger,\n ExtensionManifest,\n ExtensionRecord,\n ExtensionRegistry,\n ResolvedExtensionConfig,\n ExtensionRuntime,\n ExtensionCliRegistration,\n} from '../types/index.js';\n\n// ============================================================================\n// Tools (re-exported from @mariozechner/pi-agent-core)\n// ============================================================================\n\nexport type {\n AgentTool,\n AgentToolResult,\n AgentToolUpdateCallback,\n ToolExecutionStartEvent,\n ToolExecutionUpdateEvent,\n ToolExecutionEndEvent,\n} from '../types/index.js';\n\n// ============================================================================\n// Hooks ( Strongly Typed)\n// ============================================================================\n\nexport type {\n ExtensionHookEvent,\n ExtensionHookHandler,\n HookOptions,\n HookAgentContext,\n AgentMessage,\n ContextEvent,\n ContextResult,\n InputEvent,\n InputResult,\n TurnEvent,\n TurnResult,\n // New hook types\n HookHandlerMap,\n HookExecutionMode,\n // Note: HOOK_EXECUTION_MODES is exported as value below\n // LLM observation hooks\n HookBeforeModelResolveEvent,\n HookBeforeModelResolveResult,\n HookBeforePromptBuildEvent,\n HookBeforePromptBuildResult,\n HookLlmInputEvent,\n HookLlmOutputEvent,\n // Inbound claim\n HookInboundClaimEvent,\n HookInboundClaimResult,\n // Reset hook\n HookBeforeResetEvent,\n HookBeforeResetResult,\n // Message write hook\n HookBeforeMessageWriteEvent,\n HookBeforeMessageWriteResult,\n // Turn hooks\n HookTurnStartEvent,\n HookTurnEndEvent,\n // Tool execution hooks\n HookToolExecutionStartEvent,\n HookToolExecutionUpdateEvent,\n HookToolExecutionEndEvent,\n // Existing context types\n HookContext,\n BeforeAgentStartContext,\n BeforeAgentStartResult,\n AgentEndContext,\n BeforeCompactionContext,\n AfterCompactionContext,\n MessageReceivedContext,\n MessageSendingContext,\n MessageSendingResult,\n MessageSentContext,\n BeforeToolCallContext,\n BeforeToolCallResult,\n AfterToolCallContext,\n SessionStartContext,\n SessionEndContext,\n GatewayStartContext,\n GatewayStopContext,\n} from '../types/index.js';\n\n// ============================================================================\n// Typed Event Bus\n// ============================================================================\n\nexport type {\n EventMap,\n RequestMap,\n EventHandler,\n EventHandlerMeta,\n RequestHandler,\n RequestHandlerMeta,\n WildcardEventHandler,\n WildcardHandlerMeta,\n TypedEventBusOptions,\n RequestOptions,\n} from '../types/index.js';\n\n// ============================================================================\n// Provider Types\n// ============================================================================\n\nexport type {\n ProviderPlugin,\n ProviderModelDefinition,\n ProviderCapabilities,\n ProviderStreamParams,\n ProviderStreamChunk,\n ProviderCompleteParams,\n ProviderResponse,\n ProviderRegistry,\n BUILTIN_PROVIDERS,\n BuiltinProviderId,\n} from '../types/index.js';\n\n// ============================================================================\n// Advanced Features\n// ============================================================================\n\nexport type {\n ProviderConfig,\n ModelConfig,\n OAuthConfig,\n OAuthCallbacks,\n OAuthCredentials,\n FlagConfig,\n FlagValue,\n ShortcutConfig,\n ShortcutHandler,\n} from '../types/index.js';\n\n// ============================================================================\n// Security Types\n// ============================================================================\n\nexport type {\n SafetyCheckResult,\n SecurityConfig,\n ExtensionSourceOrigin,\n ProvenanceInfo,\n} from '../security.js';\n\n// ============================================================================\n// Slot Types\n// ============================================================================\n\nexport type {\n SlotKey,\n SlotClaim,\n SlotConfig,\n} from '../slots.js';\n\n// ============================================================================\n// Diagnostics Types\n// ============================================================================\n\nexport type {\n DiagnosticLevel,\n ExtensionDiagnostic,\n ExtensionLoaderCache,\n} from '../diagnostics.js';\n\n// ============================================================================\n// Channels (ChannelPlugin registry)\n// ============================================================================\n\nexport type {\n ChannelPlugin,\n ChannelPluginInitOptions,\n ChannelPluginStartOptions,\n} from '../../channels/plugin-types.js';\n\n// ============================================================================\n// HTTP\n// ============================================================================\n\nexport type {\n HttpRequestHandler,\n HttpRequest,\n HttpResponse,\n} from '../types/index.js';\n\n// ============================================================================\n// Commands\n// ============================================================================\n\nexport type {\n ExtensionCommand,\n ExtensionCommandHandler,\n ExtensionCommandContext,\n ExtensionCommandResult,\n ExtensionReloadHandler,\n ExtensionReloadResult,\n ExtensionReloadRegistration,\n} from '../types/index.js';\n\n// ============================================================================\n// Services\n// ============================================================================\n\nexport type {\n ExtensionService,\n} from '../types/index.js';\n\n// ============================================================================\n// Gateway\n// ============================================================================\n\nexport type {\n GatewayMethodHandler,\n} from '../types/index.js';\n\n// ============================================================================\n// Export Classes\n// ============================================================================\n\nexport { TypedEventBus } from '../typed-event-bus.js';\nexport { ExtensionRegistryImpl } from '../loader.js';\nexport { ProviderPluginRegistry, getProviderRegistry } from '../../providers/plugin-registry.js';\nexport { SlotRegistry, getSlotRegistry, registerSlotType } from '../slots.js';\nexport { HOOK_EXECUTION_MODES } from '../types/hooks.js';\n\nexport { defineChannelPluginEntry } from './channel-entry.js';\nexport { registerExtensionCliProgram } from './channel-helpers.js';\n\n// ============================================================================\n// Export Hook Runner and Utilities\n// ============================================================================\n\nexport { \n ExtensionHookRunner, \n createHookContext, \n isHookEvent \n} from '../hooks.js';\n\n// ============================================================================\n// Security Utilities\n// ============================================================================\n\nexport {\n checkExtensionPathSafety,\n checkExtensionDirSafety,\n isExtensionAllowed,\n provenanceTracker,\n logSecurityIssue,\n DEFAULT_SECURITY_CONFIG,\n} from '../security.js';\n\n// ============================================================================\n// Diagnostics Utilities\n// ============================================================================\n\nexport {\n getExtensionCache,\n getExtensionDiagnostics,\n ExtensionLoaderCacheImpl,\n ExtensionDiagnostics,\n} from '../diagnostics.js';\n\n// ============================================================================\n// Config\n// ============================================================================\n\nexport type { Config } from '../../config/config-surface.js';\n\n// Subpath barrels (also available as `xopc/extension-sdk/<name>`)\nexport { lazyModule, lazyFunction } from './lazy.js';\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/extensions/sdk/index.ts"],"sourcesContent":["/**\n * xopc Extension SDK\n * \n * Official SDK for developing xopc extensions.\n * Import types and utilities from this module:\n * \n * @example\n * import type { ExtensionApi, ExtensionDefinition } from 'xopc/extension-sdk';\n */\n\n// ============================================================================\n// Core extension types\n// ============================================================================\n\nexport type {\n ExtensionDefinition,\n ExtensionModule,\n ExtensionKind,\n ExtensionApi,\n ExtensionLogger,\n ExtensionManifest,\n ExtensionRecord,\n ExtensionRegistry,\n ResolvedExtensionConfig,\n ExtensionRuntime,\n ExtensionCliRegistration,\n} from '../types/index.js';\n\n// ============================================================================\n// Tools (re-exported from @mariozechner/pi-agent-core)\n// ============================================================================\n\nexport type {\n AgentTool,\n AgentToolResult,\n AgentToolUpdateCallback,\n ToolExecutionStartEvent,\n ToolExecutionUpdateEvent,\n ToolExecutionEndEvent,\n} from '../types/index.js';\n\n// ============================================================================\n// Hooks ( Strongly Typed)\n// ============================================================================\n\nexport type {\n ExtensionHookEvent,\n ExtensionHookHandler,\n HookOptions,\n HookAgentContext,\n AgentMessage,\n ContextEvent,\n ContextResult,\n InputEvent,\n InputResult,\n TurnEvent,\n TurnResult,\n // New hook types\n HookHandlerMap,\n HookExecutionMode,\n // Note: HOOK_EXECUTION_MODES is exported as value below\n // LLM observation hooks\n HookBeforeModelResolveEvent,\n HookBeforeModelResolveResult,\n HookBeforePromptBuildEvent,\n HookBeforePromptBuildResult,\n HookLlmInputEvent,\n HookLlmOutputEvent,\n // Inbound claim\n HookInboundClaimEvent,\n HookInboundClaimResult,\n // Reset hook\n HookBeforeResetEvent,\n HookBeforeResetResult,\n // Message write hook\n HookBeforeMessageWriteEvent,\n HookBeforeMessageWriteResult,\n // Turn hooks\n HookTurnStartEvent,\n HookTurnEndEvent,\n // Tool execution hooks\n HookToolExecutionStartEvent,\n HookToolExecutionUpdateEvent,\n HookToolExecutionEndEvent,\n // Existing context types\n HookContext,\n BeforeAgentStartContext,\n BeforeAgentStartResult,\n AgentEndContext,\n BeforeCompactionContext,\n AfterCompactionContext,\n MessageReceivedContext,\n MessageSendingContext,\n MessageSendingResult,\n MessageSentContext,\n BeforeToolCallContext,\n BeforeToolCallResult,\n AfterToolCallContext,\n SessionStartContext,\n SessionEndContext,\n GatewayStartContext,\n GatewayStopContext,\n} from '../types/index.js';\n\n// ============================================================================\n// Typed Event Bus\n// ============================================================================\n\nexport type {\n EventMap,\n RequestMap,\n EventHandler,\n EventHandlerMeta,\n RequestHandler,\n RequestHandlerMeta,\n WildcardEventHandler,\n WildcardHandlerMeta,\n TypedEventBusOptions,\n RequestOptions,\n} from '../types/index.js';\n\n// ============================================================================\n// Provider Types\n// ============================================================================\n\nexport type {\n ProviderPlugin,\n ProviderModelDefinition,\n ProviderCapabilities,\n ProviderStreamParams,\n ProviderStreamChunk,\n ProviderCompleteParams,\n ProviderResponse,\n ProviderRegistry,\n BUILTIN_PROVIDERS,\n BuiltinProviderId,\n} from '../types/index.js';\n\n// ============================================================================\n// Advanced Features\n// ============================================================================\n\nexport type {\n ProviderConfig,\n ModelConfig,\n OAuthConfig,\n OAuthCallbacks,\n OAuthCredentials,\n FlagConfig,\n FlagValue,\n ShortcutConfig,\n ShortcutHandler,\n} from '../types/index.js';\n\n// ============================================================================\n// Security Types\n// ============================================================================\n\nexport type {\n SafetyCheckResult,\n SecurityConfig,\n ExtensionSourceOrigin,\n ProvenanceInfo,\n} from '../security.js';\n\n// ============================================================================\n// Slot Types\n// ============================================================================\n\nexport type {\n SlotKey,\n SlotClaim,\n SlotConfig,\n} from '../slots.js';\n\n// ============================================================================\n// Diagnostics Types\n// ============================================================================\n\nexport type {\n DiagnosticLevel,\n ExtensionDiagnostic,\n ExtensionLoaderCache,\n} from '../diagnostics.js';\n\n// ============================================================================\n// Channels (ChannelPlugin registry)\n// ============================================================================\n\nexport type {\n ChannelPlugin,\n ChannelPluginInitOptions,\n ChannelPluginStartOptions,\n} from '../../channels/plugin-types.js';\n\n// ============================================================================\n// HTTP\n// ============================================================================\n\nexport type {\n HttpRequestHandler,\n HttpRequest,\n HttpResponse,\n} from '../types/index.js';\n\n// ============================================================================\n// Commands\n// ============================================================================\n\nexport type {\n ExtensionCommand,\n ExtensionCommandHandler,\n ExtensionCommandContext,\n ExtensionCommandResult,\n ExtensionReloadHandler,\n ExtensionReloadResult,\n ExtensionReloadRegistration,\n} from '../types/index.js';\n\n// ============================================================================\n// Services\n// ============================================================================\n\nexport type {\n ExtensionService,\n} from '../types/index.js';\n\n// ============================================================================\n// Gateway\n// ============================================================================\n\nexport type {\n GatewayMethodHandler,\n} from '../types/index.js';\n\n// ============================================================================\n// Export Classes\n// ============================================================================\n\nexport { TypedEventBus } from '../typed-event-bus.js';\nexport { ExtensionRegistryImpl } from '../loader.js';\nexport { ProviderPluginRegistry, getProviderRegistry } from '../../providers/plugin-registry.js';\nexport { SlotRegistry, getSlotRegistry, registerSlotType } from '../slots.js';\nexport { HOOK_EXECUTION_MODES } from '../types/hooks.js';\n\nexport { defineChannelPluginEntry } from './channel-entry.js';\nexport { registerExtensionCliProgram } from './channel-helpers.js';\n\n// ============================================================================\n// Export Hook Runner and Utilities\n// ============================================================================\n\nexport { \n ExtensionHookRunner, \n createHookContext, \n isHookEvent \n} from '../hooks.js';\n\n// ============================================================================\n// Security Utilities\n// ============================================================================\n\nexport {\n checkExtensionPathSafety,\n checkExtensionDirSafety,\n isExtensionAllowed,\n provenanceTracker,\n logSecurityIssue,\n DEFAULT_SECURITY_CONFIG,\n} from '../security.js';\n\n// ============================================================================\n// Diagnostics Utilities\n// ============================================================================\n\nexport {\n getExtensionCache,\n getExtensionDiagnostics,\n ExtensionLoaderCacheImpl,\n ExtensionDiagnostics,\n} from '../diagnostics.js';\n\n// ============================================================================\n// Config\n// ============================================================================\n\nexport type { Config } from '../../config/config-surface.js';\n\n// Subpath barrels (also available as `xopc/extension-sdk/<name>`)\nexport { lazyModule, lazyFunction } from './lazy.js';\n\nexport { createMockExtensionApi, createTestGateway } from './testing.js';\nexport type {\n MockExtensionApi,\n MockExtensionApiOptions,\n TestGateway,\n} from './testing.js';\n"],"mappings":";;;;;;;;;;;;;sBAiPiG"}
|
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Extension testing helpers (Phase 2): mock API + minimal HTTP gateway fixture.
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
5
|
-
|
|
4
|
+
import type { AgentTool } from '@mariozechner/pi-agent-core';
|
|
5
|
+
import type { ChannelPlugin } from '../../channels/plugin-types.js';
|
|
6
|
+
import type { Config } from '../../config/config-surface.js';
|
|
7
|
+
import type { ExtensionApi, ExtensionCommand, ExtensionCommandContext, ExtensionLogger, ExtensionReloadHandler, ExtensionService, GatewayMethodHandler, HttpRequestHandler } from '../types/core.js';
|
|
8
|
+
import type { ExtensionHookEvent } from '../types/hooks.js';
|
|
9
|
+
import type { CommandContribution } from '../types/manifest.js';
|
|
10
|
+
export interface MockExtensionApiOptions {
|
|
11
|
+
id?: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
version?: string;
|
|
14
|
+
source?: string;
|
|
15
|
+
extensionConfig?: Record<string, unknown>;
|
|
16
|
+
config?: Partial<Config>;
|
|
17
|
+
logger?: ExtensionLogger;
|
|
18
|
+
/** Pretend manifest `ui.contributions.commands` for `onCommand` tests. */
|
|
19
|
+
manifestCommands?: CommandContribution[];
|
|
20
|
+
}
|
|
21
|
+
export interface MockExtensionApi extends ExtensionApi {
|
|
22
|
+
getRegisteredTools(): AgentTool[];
|
|
23
|
+
getRegisteredHooks(): Array<{
|
|
24
|
+
event: ExtensionHookEvent;
|
|
25
|
+
handler: unknown;
|
|
26
|
+
}>;
|
|
27
|
+
getRegisteredCommands(): ExtensionCommand[];
|
|
28
|
+
getRegisteredHttpRoutes(): Array<{
|
|
29
|
+
path: string;
|
|
30
|
+
handler: HttpRequestHandler;
|
|
31
|
+
}>;
|
|
32
|
+
getRegisteredServices(): ExtensionService[];
|
|
33
|
+
getRegisteredGatewayMethods(): Array<{
|
|
34
|
+
method: string;
|
|
35
|
+
handler: GatewayMethodHandler;
|
|
36
|
+
}>;
|
|
37
|
+
getRegisteredChannelPlugins(): ChannelPlugin[];
|
|
38
|
+
getRegisteredReloadHandlers(): ExtensionReloadHandler[];
|
|
39
|
+
getEmittedEvents(): Array<{
|
|
40
|
+
event: string;
|
|
41
|
+
data: unknown;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
export declare function createMockExtensionApi(options?: MockExtensionApiOptions): MockExtensionApi;
|
|
45
|
+
export interface TestGateway {
|
|
46
|
+
baseUrl: string;
|
|
47
|
+
fetch: (path: string, init?: RequestInit) => Promise<Response>;
|
|
48
|
+
close: () => Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
export declare function createTestGateway(): Promise<TestGateway>;
|
|
51
|
+
export type { ExtensionCommandContext };
|
|
@@ -1,12 +1,181 @@
|
|
|
1
|
+
import { TypedEventBus } from "../typed-event-bus.js";
|
|
2
|
+
import { serve } from "@hono/node-server";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
import { EventEmitter, once } from "node:events";
|
|
1
5
|
//#region src/extensions/sdk/testing.ts
|
|
2
|
-
|
|
6
|
+
const noopLogger = {
|
|
7
|
+
debug: () => {},
|
|
8
|
+
info: () => {},
|
|
9
|
+
warn: () => {},
|
|
10
|
+
error: () => {}
|
|
11
|
+
};
|
|
12
|
+
function createMockExtensionApi(options = {}) {
|
|
13
|
+
return new MockExtensionApiImpl(options);
|
|
14
|
+
}
|
|
15
|
+
var MockExtensionApiImpl = class {
|
|
16
|
+
id;
|
|
17
|
+
name;
|
|
18
|
+
version;
|
|
19
|
+
source;
|
|
20
|
+
config;
|
|
21
|
+
extensionConfig;
|
|
22
|
+
logger;
|
|
23
|
+
runtime;
|
|
24
|
+
events;
|
|
25
|
+
_tools = [];
|
|
26
|
+
_hooks = [];
|
|
27
|
+
_commands = [];
|
|
28
|
+
_http = [];
|
|
29
|
+
_services = [];
|
|
30
|
+
_gw = [];
|
|
31
|
+
_channels = [];
|
|
32
|
+
_reload = [];
|
|
33
|
+
_emitted = [];
|
|
34
|
+
_bus = new EventEmitter();
|
|
35
|
+
_manifestCommands = /* @__PURE__ */ new Map();
|
|
36
|
+
constructor(opts) {
|
|
37
|
+
this.id = opts.id ?? "test-extension";
|
|
38
|
+
this.name = opts.name ?? "Test Extension";
|
|
39
|
+
this.version = opts.version;
|
|
40
|
+
this.source = opts.source ?? "/test";
|
|
41
|
+
this.extensionConfig = opts.extensionConfig ?? {};
|
|
42
|
+
this.config = opts.config ?? {};
|
|
43
|
+
this.logger = opts.logger ?? noopLogger;
|
|
44
|
+
this.runtime = {
|
|
45
|
+
config: this.config,
|
|
46
|
+
log: this.logger
|
|
47
|
+
};
|
|
48
|
+
this.events = new TypedEventBus({ logger: this.logger });
|
|
49
|
+
if (opts.manifestCommands?.length) for (const c of opts.manifestCommands) this._manifestCommands.set(c.id, c);
|
|
50
|
+
}
|
|
51
|
+
_setManifestCommands(commands) {
|
|
52
|
+
this._manifestCommands.clear();
|
|
53
|
+
for (const c of commands) this._manifestCommands.set(c.id, c);
|
|
54
|
+
}
|
|
55
|
+
getRegisteredTools() {
|
|
56
|
+
return [...this._tools];
|
|
57
|
+
}
|
|
58
|
+
getRegisteredHooks() {
|
|
59
|
+
return [...this._hooks];
|
|
60
|
+
}
|
|
61
|
+
getRegisteredCommands() {
|
|
62
|
+
return [...this._commands];
|
|
63
|
+
}
|
|
64
|
+
getRegisteredHttpRoutes() {
|
|
65
|
+
return [...this._http];
|
|
66
|
+
}
|
|
67
|
+
getRegisteredServices() {
|
|
68
|
+
return [...this._services];
|
|
69
|
+
}
|
|
70
|
+
getRegisteredGatewayMethods() {
|
|
71
|
+
return [...this._gw];
|
|
72
|
+
}
|
|
73
|
+
getRegisteredChannelPlugins() {
|
|
74
|
+
return [...this._channels];
|
|
75
|
+
}
|
|
76
|
+
getRegisteredReloadHandlers() {
|
|
77
|
+
return [...this._reload];
|
|
78
|
+
}
|
|
79
|
+
getEmittedEvents() {
|
|
80
|
+
return [...this._emitted];
|
|
81
|
+
}
|
|
82
|
+
registerTool(tool) {
|
|
83
|
+
this._tools.push(tool);
|
|
84
|
+
}
|
|
85
|
+
registerHook(event, handler, _opts) {
|
|
86
|
+
this._hooks.push({
|
|
87
|
+
event,
|
|
88
|
+
handler
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
onHook(_hookName, _handler) {}
|
|
92
|
+
registerChannel(registration) {
|
|
93
|
+
this._channels.push(registration.plugin);
|
|
94
|
+
}
|
|
95
|
+
registerHttpRoute(path, handler) {
|
|
96
|
+
this._http.push({
|
|
97
|
+
path,
|
|
98
|
+
handler
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
registerCommand(command) {
|
|
102
|
+
this._commands.push(command);
|
|
103
|
+
}
|
|
104
|
+
onCommand(commandId, handler) {
|
|
105
|
+
const meta = this._manifestCommands.get(commandId);
|
|
106
|
+
if (!meta) {
|
|
107
|
+
const name = commandId.includes(".") ? (commandId.split(".").pop() ?? commandId).trim() : commandId;
|
|
108
|
+
this.registerCommand({
|
|
109
|
+
name: name || commandId,
|
|
110
|
+
description: commandId,
|
|
111
|
+
handler
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
let name;
|
|
116
|
+
if (meta.chatAlias?.trim()) name = meta.chatAlias.trim().replace(/^\//, "");
|
|
117
|
+
else name = meta.id.includes(".") ? meta.id.split(".").pop() ?? meta.id : meta.id;
|
|
118
|
+
if (!name) name = meta.id;
|
|
119
|
+
this.registerCommand({
|
|
120
|
+
name,
|
|
121
|
+
description: meta.title,
|
|
122
|
+
handler
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
registerReload(handler) {
|
|
126
|
+
this._reload.push(handler);
|
|
127
|
+
}
|
|
128
|
+
registerService(service) {
|
|
129
|
+
this._services.push(service);
|
|
130
|
+
}
|
|
131
|
+
registerGatewayMethod(method, handler) {
|
|
132
|
+
this._gw.push({
|
|
133
|
+
method,
|
|
134
|
+
handler
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
registerCli(_factory, _opts) {}
|
|
138
|
+
resolvePath(input) {
|
|
139
|
+
return `${this.source.replace(/\/$/, "")}/${input.replace(/^\//, "")}`;
|
|
140
|
+
}
|
|
141
|
+
emit(event, data) {
|
|
142
|
+
this._emitted.push({
|
|
143
|
+
event,
|
|
144
|
+
data
|
|
145
|
+
});
|
|
146
|
+
this._bus.emit(event, data);
|
|
147
|
+
}
|
|
148
|
+
on(event, handler) {
|
|
149
|
+
this._bus.on(event, handler);
|
|
150
|
+
}
|
|
151
|
+
off(event, handler) {
|
|
152
|
+
this._bus.off(event, handler);
|
|
153
|
+
}
|
|
154
|
+
registerProvider(_plugin) {}
|
|
155
|
+
registerProviderPlugin(_plugin) {}
|
|
156
|
+
registerFlag(_name, _config) {}
|
|
157
|
+
getFlag(_name) {}
|
|
158
|
+
registerShortcut(_key, _config) {}
|
|
159
|
+
};
|
|
160
|
+
async function createTestGateway() {
|
|
161
|
+
const app = new Hono();
|
|
162
|
+
app.get("/health", (c) => c.json({ ok: true }));
|
|
163
|
+
const server = serve({
|
|
164
|
+
fetch: app.fetch,
|
|
165
|
+
port: 0,
|
|
166
|
+
hostname: "127.0.0.1"
|
|
167
|
+
});
|
|
168
|
+
if (!server.listening) await once(server, "listening");
|
|
169
|
+
const baseUrl = `http://127.0.0.1:${server.address().port}`;
|
|
3
170
|
return {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
171
|
+
baseUrl,
|
|
172
|
+
fetch: (path, init) => fetch(`${baseUrl}${path.startsWith("/") ? path : `/${path}`}`, init),
|
|
173
|
+
close: () => new Promise((resolve, reject) => {
|
|
174
|
+
server.close((err) => err ? reject(err) : resolve());
|
|
175
|
+
})
|
|
7
176
|
};
|
|
8
177
|
}
|
|
9
178
|
//#endregion
|
|
10
|
-
export { createMockExtensionApi };
|
|
179
|
+
export { createMockExtensionApi, createTestGateway };
|
|
11
180
|
|
|
12
181
|
//# sourceMappingURL=testing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","names":[],"sources":["../../../../src/extensions/sdk/testing.ts"],"sourcesContent":["/**\n * Lightweight test doubles — no Vitest dependency.\n */\n\nimport type { ExtensionApi } from '../types/core.js';\n\nexport function createMockExtensionApi(overrides?: Partial<ExtensionApi>): ExtensionApi {\n return {\n id: 'mock',\n name: 'mock',\n ...overrides,\n } as unknown as ExtensionApi;\n}\n"],"mappings":";AAMA,SAAgB,uBAAuB,WAAiD;AACtF,QAAO;EACL,IAAI;EACJ,MAAM;EACN,GAAG;EACJ"}
|
|
1
|
+
{"version":3,"file":"testing.js","names":[],"sources":["../../../../src/extensions/sdk/testing.ts"],"sourcesContent":["/**\n * Extension testing helpers (Phase 2): mock API + minimal HTTP gateway fixture.\n */\n\nimport type { Command } from 'commander';\nimport { EventEmitter } from 'node:events';\nimport { once } from 'node:events';\nimport type { AddressInfo } from 'node:net';\n\nimport type { AgentTool } from '@mariozechner/pi-agent-core';\n\nimport { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport type { ServerType } from '@hono/node-server';\n\nimport type { ChannelPlugin } from '../../channels/plugin-types.js';\nimport type { Config } from '../../config/config-surface.js';\nimport type {\n ExtensionApi,\n ExtensionCommand,\n ExtensionCommandContext,\n ExtensionCommandHandler,\n ExtensionLogger,\n ExtensionReloadHandler,\n ExtensionRuntime,\n ExtensionService,\n GatewayMethodHandler,\n HttpRequestHandler,\n} from '../types/core.js';\nimport type { FlagConfig, FlagValue, ShortcutConfig } from '../types/phase4.js';\nimport type {\n ExtensionHookEvent,\n ExtensionHookHandler,\n HookHandlerMap,\n HookOptions,\n} from '../types/hooks.js';\nimport type { CommandContribution } from '../types/manifest.js';\nimport { TypedEventBus } from '../typed-event-bus.js';\n\nexport interface MockExtensionApiOptions {\n id?: string;\n name?: string;\n version?: string;\n source?: string;\n extensionConfig?: Record<string, unknown>;\n config?: Partial<Config>;\n logger?: ExtensionLogger;\n /** Pretend manifest `ui.contributions.commands` for `onCommand` tests. */\n manifestCommands?: CommandContribution[];\n}\n\nconst noopLogger: ExtensionLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nexport interface MockExtensionApi extends ExtensionApi {\n getRegisteredTools(): AgentTool[];\n getRegisteredHooks(): Array<{ event: ExtensionHookEvent; handler: unknown }>;\n getRegisteredCommands(): ExtensionCommand[];\n getRegisteredHttpRoutes(): Array<{ path: string; handler: HttpRequestHandler }>;\n getRegisteredServices(): ExtensionService[];\n getRegisteredGatewayMethods(): Array<{ method: string; handler: GatewayMethodHandler }>;\n getRegisteredChannelPlugins(): ChannelPlugin[];\n getRegisteredReloadHandlers(): ExtensionReloadHandler[];\n getEmittedEvents(): Array<{ event: string; data: unknown }>;\n}\n\nexport function createMockExtensionApi(options: MockExtensionApiOptions = {}): MockExtensionApi {\n return new MockExtensionApiImpl(options);\n}\n\nclass MockExtensionApiImpl implements MockExtensionApi {\n readonly id: string;\n readonly name: string;\n readonly version: string | undefined;\n readonly source: string;\n readonly config: Config;\n readonly extensionConfig: Record<string, unknown>;\n readonly logger: ExtensionLogger;\n readonly runtime: ExtensionRuntime;\n readonly events: TypedEventBus;\n\n private readonly _tools: AgentTool[] = [];\n private readonly _hooks: Array<{ event: ExtensionHookEvent; handler: unknown }> = [];\n private readonly _commands: ExtensionCommand[] = [];\n private readonly _http: Array<{ path: string; handler: HttpRequestHandler }> = [];\n private readonly _services: ExtensionService[] = [];\n private readonly _gw: Array<{ method: string; handler: GatewayMethodHandler }> = [];\n private readonly _channels: ChannelPlugin[] = [];\n private readonly _reload: ExtensionReloadHandler[] = [];\n private readonly _emitted: Array<{ event: string; data: unknown }> = [];\n private readonly _bus = new EventEmitter();\n private readonly _manifestCommands = new Map<string, CommandContribution>();\n\n constructor(opts: MockExtensionApiOptions) {\n this.id = opts.id ?? 'test-extension';\n this.name = opts.name ?? 'Test Extension';\n this.version = opts.version;\n this.source = opts.source ?? '/test';\n this.extensionConfig = opts.extensionConfig ?? {};\n this.config = (opts.config ?? {}) as Config;\n this.logger = opts.logger ?? noopLogger;\n this.runtime = { config: this.config, log: this.logger };\n this.events = new TypedEventBus({ logger: this.logger });\n if (opts.manifestCommands?.length) {\n for (const c of opts.manifestCommands) {\n this._manifestCommands.set(c.id, c);\n }\n }\n }\n\n _setManifestCommands(commands: CommandContribution[]): void {\n this._manifestCommands.clear();\n for (const c of commands) {\n this._manifestCommands.set(c.id, c);\n }\n }\n\n getRegisteredTools(): AgentTool[] {\n return [...this._tools];\n }\n getRegisteredHooks(): Array<{ event: ExtensionHookEvent; handler: unknown }> {\n return [...this._hooks];\n }\n getRegisteredCommands(): ExtensionCommand[] {\n return [...this._commands];\n }\n getRegisteredHttpRoutes(): Array<{ path: string; handler: HttpRequestHandler }> {\n return [...this._http];\n }\n getRegisteredServices(): ExtensionService[] {\n return [...this._services];\n }\n getRegisteredGatewayMethods(): Array<{ method: string; handler: GatewayMethodHandler }> {\n return [...this._gw];\n }\n getRegisteredChannelPlugins(): ChannelPlugin[] {\n return [...this._channels];\n }\n getRegisteredReloadHandlers(): ExtensionReloadHandler[] {\n return [...this._reload];\n }\n getEmittedEvents(): Array<{ event: string; data: unknown }> {\n return [...this._emitted];\n }\n\n registerTool(tool: AgentTool): void {\n this._tools.push(tool);\n }\n\n registerHook(\n event: ExtensionHookEvent,\n handler: ExtensionHookHandler,\n _opts?: HookOptions,\n ): void {\n this._hooks.push({ event, handler });\n }\n\n onHook<K extends ExtensionHookEvent>(_hookName: K, _handler: HookHandlerMap[K]): void {\n /* optional for tests */\n }\n\n registerChannel(registration: { plugin: ChannelPlugin }): void {\n this._channels.push(registration.plugin);\n }\n\n registerHttpRoute(path: string, handler: HttpRequestHandler): void {\n this._http.push({ path, handler });\n }\n\n registerCommand(command: ExtensionCommand): void {\n this._commands.push(command);\n }\n\n onCommand(commandId: string, handler: ExtensionCommandHandler): void {\n const meta = this._manifestCommands.get(commandId);\n if (!meta) {\n const name = commandId.includes('.')\n ? (commandId.split('.').pop() ?? commandId).trim()\n : commandId;\n this.registerCommand({\n name: name || commandId,\n description: commandId,\n handler,\n });\n return;\n }\n let name: string;\n if (meta.chatAlias?.trim()) {\n name = meta.chatAlias.trim().replace(/^\\//, '');\n } else {\n name = meta.id.includes('.') ? (meta.id.split('.').pop() ?? meta.id) : meta.id;\n }\n if (!name) name = meta.id;\n this.registerCommand({ name, description: meta.title, handler });\n }\n\n registerReload(handler: ExtensionReloadHandler): void {\n this._reload.push(handler);\n }\n\n registerService(service: ExtensionService): void {\n this._services.push(service);\n }\n\n registerGatewayMethod(method: string, handler: GatewayMethodHandler): void {\n this._gw.push({ method, handler });\n }\n\n registerCli(_factory: (ctx: { program: Command }) => void, _opts?: { commands: string[] }): void {\n /* noop */\n }\n\n resolvePath(input: string): string {\n const base = this.source.replace(/\\/$/, '');\n const p = input.replace(/^\\//, '');\n return `${base}/${p}`;\n }\n\n emit(event: string, data: unknown): void {\n this._emitted.push({ event, data });\n this._bus.emit(event, data);\n }\n\n on(event: string, handler: (data: unknown) => void): void {\n this._bus.on(event, handler);\n }\n\n off(event: string, handler: (data: unknown) => void): void {\n this._bus.off(event, handler);\n }\n\n registerProvider(_plugin: unknown): void {\n /* noop */\n }\n\n registerProviderPlugin(_plugin: unknown): void {\n /* noop */\n }\n\n registerFlag(_name: string, _config: FlagConfig): void {\n /* noop */\n }\n\n getFlag(_name: string): FlagValue {\n return undefined;\n }\n\n registerShortcut(_key: string, _config: ShortcutConfig): void {\n /* noop */\n }\n}\n\nexport interface TestGateway {\n baseUrl: string;\n fetch: (path: string, init?: RequestInit) => Promise<Response>;\n close: () => Promise<void>;\n}\n\nexport async function createTestGateway(): Promise<TestGateway> {\n const app = new Hono();\n app.get('/health', (c) => c.json({ ok: true }));\n\n const server: ServerType = serve({\n fetch: app.fetch,\n port: 0,\n hostname: '127.0.0.1',\n });\n\n if (!server.listening) {\n await once(server, 'listening');\n }\n const addr = server.address() as AddressInfo;\n const port = addr.port;\n const baseUrl = `http://127.0.0.1:${port}`;\n\n return {\n baseUrl,\n fetch: (path, init) =>\n fetch(`${baseUrl}${path.startsWith('/') ? path : `/${path}`}`, init),\n close: () =>\n new Promise((resolve, reject) => {\n server.close((err) => (err ? reject(err) : resolve()));\n }),\n };\n}\n\nexport type { ExtensionCommandContext };\n"],"mappings":"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"}
|
|
@@ -79,6 +79,11 @@ export interface ExtensionApi {
|
|
|
79
79
|
}): void;
|
|
80
80
|
registerHttpRoute(path: string, handler: HttpRequestHandler): void;
|
|
81
81
|
registerCommand(command: ExtensionCommand): void;
|
|
82
|
+
/**
|
|
83
|
+
* Bind a handler to a manifest-declared command id (`ui.contributions.commands`).
|
|
84
|
+
* Metadata comes from the manifest; optional `chatAlias` becomes the slash command name.
|
|
85
|
+
*/
|
|
86
|
+
onCommand(commandId: string, handler: ExtensionCommandHandler): void;
|
|
82
87
|
/**
|
|
83
88
|
* Register a reload handler when config paths matching this extension change during hot reload.
|
|
84
89
|
*/
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* New fields are optional for backward compatibility.
|
|
4
4
|
*/
|
|
5
5
|
import type { ExtensionKind } from './core.js';
|
|
6
|
+
/** Declared in `engines` in the extension manifest (e.g. VSCode-style). */
|
|
7
|
+
export interface EnginesDeclaration {
|
|
8
|
+
/** Semver range string for the running xopc CLI / gateway, e.g. `">=0.0.20"`. */
|
|
9
|
+
xopc?: string;
|
|
10
|
+
}
|
|
6
11
|
export interface ExtensionManifest {
|
|
7
12
|
id: string;
|
|
8
13
|
name: string;
|
|
@@ -29,6 +34,11 @@ export interface ExtensionManifest {
|
|
|
29
34
|
configPrefixes?: string[];
|
|
30
35
|
supportsHotReload?: boolean;
|
|
31
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Host / runtime requirements (e.g. compatible xopc semver range).
|
|
39
|
+
* Normalized from `xopc.extension.json` `engines`.
|
|
40
|
+
*/
|
|
41
|
+
engines?: EnginesDeclaration;
|
|
32
42
|
/** Chat commands metadata (runtime registration still required in `register()`). */
|
|
33
43
|
commands?: ExtensionManifestCommand[];
|
|
34
44
|
/** Frontend UI declaration — enables extensions to render custom UI in the Gateway Console. */
|
|
@@ -70,6 +80,8 @@ export interface SidebarPanelContribution {
|
|
|
70
80
|
icon?: string;
|
|
71
81
|
entrypoint: string;
|
|
72
82
|
defaultVisible?: boolean;
|
|
83
|
+
/** When-expression for visibility (see Phase 2). */
|
|
84
|
+
when?: string;
|
|
73
85
|
}
|
|
74
86
|
/** A panel rendered inside the settings page. */
|
|
75
87
|
export interface SettingsPanelContribution {
|
|
@@ -102,6 +114,7 @@ export interface PageContribution {
|
|
|
102
114
|
showInNav?: boolean;
|
|
103
115
|
/** Lucide icon name */
|
|
104
116
|
navIcon?: string;
|
|
117
|
+
when?: string;
|
|
105
118
|
}
|
|
106
119
|
/** A command registered in the command palette. */
|
|
107
120
|
export interface CommandContribution {
|
|
@@ -109,6 +122,9 @@ export interface CommandContribution {
|
|
|
109
122
|
title: string;
|
|
110
123
|
shortcut?: string;
|
|
111
124
|
opensPanel?: string;
|
|
125
|
+
/** Optional chat slash name (e.g. `/hello`); bound via `api.onCommand`. */
|
|
126
|
+
chatAlias?: string;
|
|
127
|
+
when?: string;
|
|
112
128
|
}
|
|
113
129
|
/** A small widget rendered in the status bar. */
|
|
114
130
|
export interface StatusBarItemContribution {
|
|
@@ -116,6 +132,7 @@ export interface StatusBarItemContribution {
|
|
|
116
132
|
entrypoint: string;
|
|
117
133
|
position?: 'left' | 'right';
|
|
118
134
|
width?: number;
|
|
135
|
+
when?: string;
|
|
119
136
|
}
|
|
120
137
|
export interface ProviderAuthChoice {
|
|
121
138
|
provider: string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Config as SurfaceConfig } from '../config/config-surface.js';
|
|
2
|
+
import type { ExtensionLoader } from './loader.js';
|
|
3
|
+
/**
|
|
4
|
+
* Flat payload for `GET /api/context` and client-side `when` evaluation.
|
|
5
|
+
*/
|
|
6
|
+
export declare function buildWhenContextSnapshot(config: SurfaceConfig, loader: ExtensionLoader | null): Record<string, unknown>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getAllProviders, init_providers, isProviderConfiguredSync } from "../providers/index.js";
|
|
2
|
+
//#region src/extensions/when-context.ts
|
|
3
|
+
init_providers();
|
|
4
|
+
/**
|
|
5
|
+
* Flat payload for `GET /api/context` and client-side `when` evaluation.
|
|
6
|
+
*/
|
|
7
|
+
function buildWhenContextSnapshot(config, loader) {
|
|
8
|
+
const out = {
|
|
9
|
+
platform: process.platform,
|
|
10
|
+
isElectron: Boolean(process.versions.electron),
|
|
11
|
+
gatewayAuthenticated: true,
|
|
12
|
+
sessionActive: false,
|
|
13
|
+
agentRunning: false
|
|
14
|
+
};
|
|
15
|
+
for (const p of getAllProviders()) out[`hasProvider.${p}`] = isProviderConfiguredSync(p);
|
|
16
|
+
const channels = config.channels;
|
|
17
|
+
if (channels && typeof channels === "object") for (const [id, raw] of Object.entries(channels)) if (raw && typeof raw === "object" && raw !== null && "enabled" in raw) out[`hasChannel.${id}`] = raw.enabled === true;
|
|
18
|
+
else out[`hasChannel.${id}`] = Boolean(raw);
|
|
19
|
+
if (loader) try {
|
|
20
|
+
loader.setConfig(config);
|
|
21
|
+
for (const ext of loader.discoverExtensions()) out[`extensionEnabled.${ext.id}`] = true;
|
|
22
|
+
} catch {}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { buildWhenContextSnapshot };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=when-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"when-context.js","names":[],"sources":["../../../src/extensions/when-context.ts"],"sourcesContent":["import type { Config as SurfaceConfig } from '../config/config-surface.js';\nimport { getAllProviders, isProviderConfiguredSync } from '../providers/index.js';\nimport type { ExtensionLoader } from './loader.js';\n\n/**\n * Flat payload for `GET /api/context` and client-side `when` evaluation.\n */\nexport function buildWhenContextSnapshot(\n config: SurfaceConfig,\n loader: ExtensionLoader | null,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {\n platform: process.platform,\n isElectron: Boolean(process.versions.electron),\n gatewayAuthenticated: true,\n sessionActive: false,\n agentRunning: false,\n };\n for (const p of getAllProviders()) {\n out[`hasProvider.${p}`] = isProviderConfiguredSync(p);\n }\n const channels = config.channels;\n if (channels && typeof channels === 'object') {\n for (const [id, raw] of Object.entries(channels)) {\n if (raw && typeof raw === 'object' && raw !== null && 'enabled' in raw) {\n out[`hasChannel.${id}`] = (raw as { enabled?: boolean }).enabled === true;\n } else {\n out[`hasChannel.${id}`] = Boolean(raw);\n }\n }\n }\n if (loader) {\n try {\n loader.setConfig(config);\n for (const ext of loader.discoverExtensions()) {\n out[`extensionEnabled.${ext.id}`] = true;\n }\n } catch {\n /* non-fatal for context snapshot */\n }\n }\n return out;\n}\n"],"mappings":";;gBACkF;;;;AAMlF,SAAgB,yBACd,QACA,QACyB;CACzB,MAAM,MAA+B;EACnC,UAAU,QAAQ;EAClB,YAAY,QAAQ,QAAQ,SAAS,SAAS;EAC9C,sBAAsB;EACtB,eAAe;EACf,cAAc;EACf;AACD,MAAK,MAAM,KAAK,iBAAiB,CAC/B,KAAI,eAAe,OAAO,yBAAyB,EAAE;CAEvD,MAAM,WAAW,OAAO;AACxB,KAAI,YAAY,OAAO,aAAa,SAClC,MAAK,MAAM,CAAC,IAAI,QAAQ,OAAO,QAAQ,SAAS,CAC9C,KAAI,OAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,aAAa,IACjE,KAAI,cAAc,QAAS,IAA8B,YAAY;KAErE,KAAI,cAAc,QAAQ,QAAQ,IAAI;AAI5C,KAAI,OACF,KAAI;AACF,SAAO,UAAU,OAAO;AACxB,OAAK,MAAM,OAAO,OAAO,oBAAoB,CAC3C,KAAI,oBAAoB,IAAI,QAAQ;SAEhC;AAIV,QAAO"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boolean when-expressions for extension UI contributions (Phase 2).
|
|
3
|
+
* Grammar: expression := term (('&&' | '||') term)*; term := '!' term | atom;
|
|
4
|
+
* atom := variable (('==' | '!=') literal)? | '(' expression ')'
|
|
5
|
+
*/
|
|
6
|
+
export type WhenContext = Record<string, unknown>;
|
|
7
|
+
/**
|
|
8
|
+
* Evaluate a when-expression string against a flat context object.
|
|
9
|
+
* Unknown variables are treated as falsey for boolean tests; missing keys are undefined.
|
|
10
|
+
*/
|
|
11
|
+
export declare function evaluateWhenExpression(expr: string, ctx: WhenContext): boolean;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
//#region src/extensions/when-expression.ts
|
|
2
|
+
function tokenize(input) {
|
|
3
|
+
const out = [];
|
|
4
|
+
let i = 0;
|
|
5
|
+
const s = input.trim();
|
|
6
|
+
while (i < s.length) {
|
|
7
|
+
const c = s[i];
|
|
8
|
+
if (/\s/.test(c)) {
|
|
9
|
+
i++;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (s.slice(i, i + 2) === "&&") {
|
|
13
|
+
out.push({ k: "AND" });
|
|
14
|
+
i += 2;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (s.slice(i, i + 2) === "||") {
|
|
18
|
+
out.push({ k: "OR" });
|
|
19
|
+
i += 2;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (s.slice(i, i + 2) === "==") {
|
|
23
|
+
out.push({ k: "EQ" });
|
|
24
|
+
i += 2;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (s.slice(i, i + 2) === "!=") {
|
|
28
|
+
out.push({ k: "NE" });
|
|
29
|
+
i += 2;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (c === "(") {
|
|
33
|
+
out.push({ k: "LP" });
|
|
34
|
+
i++;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (c === ")") {
|
|
38
|
+
out.push({ k: "RP" });
|
|
39
|
+
i++;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (c === "!") {
|
|
43
|
+
out.push({ k: "NOT" });
|
|
44
|
+
i++;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (c === "'" || c === "\"") {
|
|
48
|
+
const q = c;
|
|
49
|
+
i++;
|
|
50
|
+
let buf = "";
|
|
51
|
+
while (i < s.length) {
|
|
52
|
+
const ch = s[i];
|
|
53
|
+
if (ch === "\\" && i + 1 < s.length) {
|
|
54
|
+
buf += s[i + 1];
|
|
55
|
+
i += 2;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (ch === q) {
|
|
59
|
+
i++;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
buf += ch;
|
|
63
|
+
i++;
|
|
64
|
+
}
|
|
65
|
+
out.push({
|
|
66
|
+
k: "STR",
|
|
67
|
+
v: buf
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (/[0-9]/.test(c) || c === "-" && i + 1 < s.length && /[0-9]/.test(s[i + 1])) {
|
|
72
|
+
let j = i + 1;
|
|
73
|
+
while (j < s.length && /[0-9.]/.test(s[j])) j++;
|
|
74
|
+
const n = Number(s.slice(i, j));
|
|
75
|
+
out.push({
|
|
76
|
+
k: "NUM",
|
|
77
|
+
v: Number.isFinite(n) ? n : 0
|
|
78
|
+
});
|
|
79
|
+
i = j;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (/[a-zA-Z_]/.test(c)) {
|
|
83
|
+
let j = i + 1;
|
|
84
|
+
while (j < s.length && /[a-zA-Z0-9_.]/.test(s[j])) j++;
|
|
85
|
+
const word = s.slice(i, j);
|
|
86
|
+
i = j;
|
|
87
|
+
if (word === "true") out.push({
|
|
88
|
+
k: "BOOL",
|
|
89
|
+
v: true
|
|
90
|
+
});
|
|
91
|
+
else if (word === "false") out.push({
|
|
92
|
+
k: "BOOL",
|
|
93
|
+
v: false
|
|
94
|
+
});
|
|
95
|
+
else out.push({
|
|
96
|
+
k: "IDENT",
|
|
97
|
+
v: word
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
throw new Error(`Unexpected character in when-expression at ${i}: ${c}`);
|
|
102
|
+
}
|
|
103
|
+
out.push({ k: "EOF" });
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
function ctxLookup(ctx, key) {
|
|
107
|
+
if (key in ctx) return ctx[key];
|
|
108
|
+
}
|
|
109
|
+
var Parser = class {
|
|
110
|
+
i = 0;
|
|
111
|
+
constructor(toks, ctx) {
|
|
112
|
+
this.toks = toks;
|
|
113
|
+
this.ctx = ctx;
|
|
114
|
+
}
|
|
115
|
+
parse() {
|
|
116
|
+
const v = this.parseOr();
|
|
117
|
+
const tail = this.cur();
|
|
118
|
+
if (tail.k !== "EOF") throw new Error(`Unexpected tokens after expression (at ${this.i}: ${tail.k}${"v" in tail ? ` ${tail.v}` : ""})`);
|
|
119
|
+
return v;
|
|
120
|
+
}
|
|
121
|
+
cur() {
|
|
122
|
+
return this.toks[this.i] ?? { k: "EOF" };
|
|
123
|
+
}
|
|
124
|
+
parseOr() {
|
|
125
|
+
let v = this.parseAnd();
|
|
126
|
+
while (this.cur().k === "OR") {
|
|
127
|
+
this.i++;
|
|
128
|
+
const rhs = this.parseAnd();
|
|
129
|
+
v = v || rhs;
|
|
130
|
+
}
|
|
131
|
+
return v;
|
|
132
|
+
}
|
|
133
|
+
parseAnd() {
|
|
134
|
+
let v = this.parseUnary();
|
|
135
|
+
while (this.cur().k === "AND") {
|
|
136
|
+
this.i++;
|
|
137
|
+
const rhs = this.parseUnary();
|
|
138
|
+
v = v && rhs;
|
|
139
|
+
}
|
|
140
|
+
return v;
|
|
141
|
+
}
|
|
142
|
+
parseUnary() {
|
|
143
|
+
if (this.cur().k === "NOT") {
|
|
144
|
+
this.i++;
|
|
145
|
+
return !this.parseUnary();
|
|
146
|
+
}
|
|
147
|
+
return this.parsePrimary();
|
|
148
|
+
}
|
|
149
|
+
parsePrimary() {
|
|
150
|
+
if (this.cur().k === "LP") {
|
|
151
|
+
this.i++;
|
|
152
|
+
const inner = this.parseOr();
|
|
153
|
+
if (this.cur().k !== "RP") throw new Error("Expected )");
|
|
154
|
+
this.i++;
|
|
155
|
+
return inner;
|
|
156
|
+
}
|
|
157
|
+
return this.parseAtom();
|
|
158
|
+
}
|
|
159
|
+
parseAtom() {
|
|
160
|
+
const left = this.readAtomValue();
|
|
161
|
+
const op = this.cur();
|
|
162
|
+
if (op.k === "EQ" || op.k === "NE") {
|
|
163
|
+
this.i++;
|
|
164
|
+
const eq = valuesLooselyEqual(left, this.readAtomValue());
|
|
165
|
+
return op.k === "EQ" ? eq : !eq;
|
|
166
|
+
}
|
|
167
|
+
return isTruthyWhen(left);
|
|
168
|
+
}
|
|
169
|
+
readAtomValue() {
|
|
170
|
+
const t = this.cur();
|
|
171
|
+
if (t.k === "IDENT") {
|
|
172
|
+
this.i++;
|
|
173
|
+
return ctxLookup(this.ctx, t.v);
|
|
174
|
+
}
|
|
175
|
+
if (t.k === "STR") {
|
|
176
|
+
this.i++;
|
|
177
|
+
return t.v;
|
|
178
|
+
}
|
|
179
|
+
if (t.k === "NUM") {
|
|
180
|
+
this.i++;
|
|
181
|
+
return t.v;
|
|
182
|
+
}
|
|
183
|
+
if (t.k === "BOOL") {
|
|
184
|
+
this.i++;
|
|
185
|
+
return t.v;
|
|
186
|
+
}
|
|
187
|
+
throw new Error("Expected value in when-expression");
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
function valuesLooselyEqual(a, b) {
|
|
191
|
+
if (a === b) return true;
|
|
192
|
+
if (a == null || b == null) return a === b;
|
|
193
|
+
if (typeof a === "boolean" || typeof b === "boolean") return Boolean(a) === Boolean(b);
|
|
194
|
+
if (typeof a === "number" && typeof b === "number") return a === b;
|
|
195
|
+
return String(a) === String(b);
|
|
196
|
+
}
|
|
197
|
+
function isTruthyWhen(v) {
|
|
198
|
+
if (typeof v === "boolean") return v;
|
|
199
|
+
if (typeof v === "number") return v !== 0;
|
|
200
|
+
if (typeof v === "string") return v.length > 0;
|
|
201
|
+
return v != null;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Evaluate a when-expression string against a flat context object.
|
|
205
|
+
* Unknown variables are treated as falsey for boolean tests; missing keys are undefined.
|
|
206
|
+
*/
|
|
207
|
+
function evaluateWhenExpression(expr, ctx) {
|
|
208
|
+
const s = expr.trim();
|
|
209
|
+
if (!s) return true;
|
|
210
|
+
return new Parser(tokenize(s), ctx).parse();
|
|
211
|
+
}
|
|
212
|
+
//#endregion
|
|
213
|
+
export { evaluateWhenExpression };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=when-expression.js.map
|