@xopcai/xopc 0.0.106 → 0.0.108
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/dashscope/xopc.extension.json +7 -2
- package/dist/extensions/fal/xopc.extension.json +7 -2
- package/dist/extensions/feishu/src/adapters/gateway-setup.js +22 -26
- package/dist/extensions/feishu/src/adapters/gateway-setup.js.map +1 -1
- package/dist/extensions/feishu/xopc.extension.json +458 -26
- package/dist/extensions/google/xopc.extension.json +7 -2
- package/dist/extensions/minimax/xopc.extension.json +8 -2
- package/dist/extensions/openai/xopc.extension.json +7 -2
- package/dist/extensions/telegram/xopc.extension.json +10 -2
- package/dist/extensions/weixin/xopc.extension.json +57 -14
- package/dist/gateway/static/root/apple-touch-icon.png +0 -0
- package/dist/gateway/static/root/assets/agents-56kCGHdV.js +78 -0
- package/dist/gateway/static/root/assets/apps-page-Dd88J47r.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-Ckyvnvml.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-DKo2ZdRA.js +1 -0
- package/dist/gateway/static/root/assets/{dist-OM9qGfm5.js → dist-dmxWdtPG.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-D-WHrr_w.js → extension-debug-page-iGpG-vpp.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CCgjSoKn.js → extension-page-I7-3uHiD.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BJH5a-Gn.js → extension-settings-page-BisnHG7r.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-CkvRO30i.js → fetch-CFdKBnE6.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-BkKyRDrF.js → gateway-startup-retry-DdngdObI.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-CVyP6pFj.js → heartbeat-config-api-tbWAFPN8.js} +1 -1
- package/dist/gateway/static/root/assets/index-DI5_GsZt.css +1 -0
- package/dist/gateway/static/root/assets/index-slwUkhxH.js +4845 -0
- package/dist/gateway/static/root/assets/{logs-page-R6Vv_Ja6.js → logs-page-CCZiXrWu.js} +2 -2
- package/dist/gateway/static/root/assets/{note-detail-page-BUDarLhh.js → note-detail-page-BYbw2GLl.js} +4 -4
- package/dist/gateway/static/root/assets/{note-time-Cc2-T21g.js → note-time-Cya72Lte.js} +1 -1
- package/dist/gateway/static/root/assets/notes-page-DrAO7VRK.js +1 -0
- package/dist/gateway/static/root/assets/{schema-form-CrlOmEZt.js → schema-form-a6UJCK55.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DNU3Ymvz.js +1 -0
- package/dist/gateway/static/root/assets/{settings-advanced-gate-BBAQxxew.js → settings-advanced-gate-CyEJYTbw.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DjPD-ErC.js → settings-form-section-DpHo2ecA.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-YRwe4q56.js +3 -0
- package/dist/gateway/static/root/assets/{share-preview-page-BjvPht3V.js → share-preview-page-B7rf4QB4.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page-we1NAt7u.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-DYgVdRB4.js → theme-store-BZozwAeK.js} +1 -1
- package/dist/gateway/static/root/assets/url-DQRqHuL-.js +3 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-Btagks_B.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-page.utils-D4sqR0yX.js → workflow-page.utils-DvzJKs6W.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BgOAgCOG.js +18 -0
- package/dist/gateway/static/root/favicon-16.png +0 -0
- package/dist/gateway/static/root/favicon-32.png +0 -0
- package/dist/gateway/static/root/favicon.ico +0 -0
- package/dist/gateway/static/root/favicon.svg +18 -0
- package/dist/gateway/static/root/index.html +11 -10
- package/dist/package.js +1 -1
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.d.ts +4 -0
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js +4 -2
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js.map +1 -1
- package/dist/src/agent/starter-agents.d.ts +19 -0
- package/dist/src/agent/starter-agents.js +190 -0
- package/dist/src/agent/starter-agents.js.map +1 -0
- package/dist/src/agent/workflow/runtime.js +49 -15
- package/dist/src/agent/workflow/runtime.js.map +1 -1
- package/dist/src/agent/workflow/types.d.ts +11 -0
- package/dist/src/channels/catalog/channel-catalog-service.js +13 -0
- package/dist/src/channels/catalog/channel-catalog-service.js.map +1 -1
- package/dist/src/channels/plugins/types.adapters.d.ts +2 -0
- package/dist/src/cli/commands/extension-create.d.ts +2 -0
- package/dist/src/cli/commands/extension-create.js +165 -0
- package/dist/src/cli/commands/extension-create.js.map +1 -0
- package/dist/src/cli/commands/extension-dev.js +0 -2
- package/dist/src/cli/commands/extension-dev.js.map +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +104 -80
- package/dist/src/cli/commands/extension-marketplace.js.map +1 -1
- package/dist/src/cli/commands/extension-pack.d.ts +13 -0
- package/dist/src/cli/commands/extension-pack.js +159 -91
- package/dist/src/cli/commands/extension-pack.js.map +1 -1
- package/dist/src/cli/commands/extension.d.ts +2 -0
- package/dist/src/cli/commands/extension.js +375 -3
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/commands/gateway/run-foreground.js +1 -2
- package/dist/src/cli/commands/gateway/run-foreground.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +0 -2
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/setup.js +0 -2
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +9 -6
- package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
- package/dist/src/config/paths.d.ts +2 -2
- package/dist/src/config/paths.js +10 -6
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/schema.d.ts +2 -0
- package/dist/src/config/schema.js +3 -1
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/extensions/api.d.ts +4 -2
- package/dist/src/extensions/api.js +31 -12
- package/dist/src/extensions/api.js.map +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -0
- package/dist/src/extensions/discover-extensions.js.map +1 -1
- package/dist/src/extensions/install.d.ts +1 -0
- package/dist/src/extensions/install.js +51 -8
- package/dist/src/extensions/install.js.map +1 -1
- package/dist/src/extensions/loader.d.ts +0 -4
- package/dist/src/extensions/loader.js +54 -87
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.d.ts +9 -1
- package/dist/src/extensions/lockfile.js.map +1 -1
- package/dist/src/extensions/normalize-manifest.js +20 -4
- package/dist/src/extensions/normalize-manifest.js.map +1 -1
- package/dist/src/extensions/package-contract.d.ts +12 -0
- package/dist/src/extensions/package-contract.js +59 -0
- package/dist/src/extensions/package-contract.js.map +1 -0
- package/dist/src/extensions/sdk/core.d.ts +1 -1
- package/dist/src/extensions/sdk/index.d.ts +1 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/types/manifest.d.ts +15 -0
- package/dist/src/gateway/agents-admin.d.ts +0 -8
- package/dist/src/gateway/agents-admin.js +1 -26
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +1 -55
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/channels.js +10 -1
- package/dist/src/gateway/hono/routes/channels.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +130 -0
- package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +44 -0
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/notes/agent-context.d.ts +12 -4
- package/dist/src/notes/agent-context.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.js +0 -7
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.js +0 -7
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/tui-backend.d.ts +0 -1
- package/dist/src/tui/tui-commands.js +1 -1
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-picker-overlay.js +1 -1
- package/dist/src/tui/tui-picker-overlay.js.map +1 -1
- package/dist/src/workflows/domain/definition-utils.d.ts +1 -0
- package/dist/src/workflows/domain/definition-utils.js +32 -2
- package/dist/src/workflows/domain/definition-utils.js.map +1 -1
- package/dist/src/workflows/domain/definition.d.ts +2 -0
- package/dist/src/workflows/domain/event.d.ts +2 -1
- package/dist/src/workflows/domain/index.d.ts +1 -0
- package/dist/src/workflows/domain/index.js +3 -2
- package/dist/src/workflows/domain/run.d.ts +27 -1
- package/dist/src/workflows/domain/run.js.map +1 -1
- package/dist/src/workflows/domain/schema-validation.d.ts +8 -0
- package/dist/src/workflows/domain/schema-validation.js +28 -0
- package/dist/src/workflows/domain/schema-validation.js.map +1 -0
- package/dist/src/workflows/draft/index.d.ts +3 -0
- package/dist/src/workflows/draft/index.js +3 -0
- package/dist/src/workflows/draft/workflow-draft-prompt.d.ts +23 -0
- package/dist/src/workflows/draft/workflow-draft-prompt.js +98 -0
- package/dist/src/workflows/draft/workflow-draft-prompt.js.map +1 -0
- package/dist/src/workflows/draft/workflow-draft-service.d.ts +12 -0
- package/dist/src/workflows/draft/workflow-draft-service.js +131 -0
- package/dist/src/workflows/draft/workflow-draft-service.js.map +1 -0
- package/dist/src/workflows/draft/workflow-draft-validator.d.ts +4 -0
- package/dist/src/workflows/draft/workflow-draft-validator.js +133 -0
- package/dist/src/workflows/draft/workflow-draft-validator.js.map +1 -0
- package/dist/src/workflows/draft/workflow-draft.types.d.ts +44 -0
- package/dist/src/workflows/draft/workflow-draft.types.js +1 -0
- package/dist/src/workflows/engine/projector.js +1 -0
- package/dist/src/workflows/engine/projector.js.map +1 -1
- package/dist/src/workflows/engine/workflow-engine.d.ts +18 -0
- package/dist/src/workflows/engine/workflow-engine.js +320 -11
- package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
- package/dist/src/workflows/index.js +4 -3
- package/dist/src/workflows/service/workflow-run-service.d.ts +14 -3
- package/dist/src/workflows/service/workflow-run-service.js +227 -6
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.types.d.ts +7 -2
- package/package.json +4 -2
- package/dist/gateway/static/root/assets/agents-3eP0vqFO.js +0 -396
- package/dist/gateway/static/root/assets/apps-page-CZ2ZKrko.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BbETa93K.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-DCtUkHMu.js +0 -1
- package/dist/gateway/static/root/assets/index-BPYKW7FB.css +0 -1
- package/dist/gateway/static/root/assets/index-CEZxETzw.js +0 -4845
- package/dist/gateway/static/root/assets/notes-page-EAHl16bq.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Ce_xuuSB.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-KhQ9QgH0.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-C0laq7l1.js +0 -2
- package/dist/gateway/static/root/assets/url-DPEStU4M.js +0 -3
- package/dist/gateway/static/root/assets/voice-api-key-field-DeGckWkl.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-DFjM95g5.js +0 -27
- package/dist/src/agent/builtin-agent-ids.d.ts +0 -2
- package/dist/src/agent/builtin-agent-ids.js +0 -17
- package/dist/src/agent/builtin-agent-ids.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","names":[],"sources":["../../../src/extensions/api.ts"],"sourcesContent":["/**\n * Extension API Implementation\n * \n * Complete extension API with strong typing, security, and provider support.\n */\n\nimport type { AgentTool } from '@earendil-works/pi-agent-core';\nimport type { Command } from 'commander';\nimport type {\n ExtensionApi,\n ExtensionLogger,\n ExtensionRuntime,\n ExtensionCliRegistration,\n GatewayMethodHandler,\n HttpRequestHandler,\n ExtensionCommand,\n ExtensionCommandContext,\n ExtensionCommandHandler,\n ExtensionReloadHandler,\n ExtensionService,\n FlagConfig,\n FlagValue,\n ShortcutConfig,\n HookHandlerMap,\n ExtensionHookEvent,\n HookExecutionMode,\n TuiExtensionRegistrar,\n ExtensionSendUserMessageOptions,\n ExtensionUserMessageContent,\n ExtensionCustomMessage,\n ExtensionSendMessageOptions,\n} from './types/index.js';\nimport type { CommandContribution } from './types/manifest.js';\nimport type { CommandDefinition } from '../chat-commands/types.js';\nimport { commandRegistry } from '../chat-commands/registry.js';\nimport type { ChannelPlugin } from '../channels/plugin-types.js';\nimport {\n HOOK_EXECUTION_MODES,\n} from './types/hooks.js';\nimport type { Config } from '../config/config-surface.js';\nimport type { Config as SchemaConfig } from '../config/schema.js';\nimport { resolve, isAbsolute } from 'path';\nimport { EventEmitter } from 'events';\nimport { createLogger } from '../utils/logger.js';\nimport { TypedEventBus } from './typed-event-bus.js';\nimport { ExtensionRegistryImpl } from './extension-registry-impl.js';\nimport { registerMarketplaceAdapter as _registerMarketplaceAdapter, unregisterMarketplaceAdapter as _unregisterMarketplaceAdapter } from '../agent/skills/marketplace/registry.js';\nimport { registerSpeechProvider as registerSpeechProviderInRegistry } from '../voice/tts/speech-registry.js';\nimport type { SpeechProviderPlugin } from '../voice/tts/speech-provider-types.js';\nimport { registerMediaUnderstandingProvider as registerMediaUnderstandingProviderInRegistry } from '../media-understanding/registry.js';\nimport type { MediaUnderstandingProvider } from '../media-understanding/types.js';\n\nexport class ExtensionApiImpl implements ExtensionApi {\n private _tools: Map<string, AgentTool> = new Map();\n private _hooks: Map<string, Set<Function>> = new Map();\n // Strongly typed hooks (for on() method)\n private _typedHooks: Map<ExtensionHookEvent, Set<Function>> = new Map();\n private _eventBus = new EventEmitter();\n private _typedEventBus: TypedEventBus;\n \n // Unified Registry\n private _registry: ExtensionRegistryImpl;\n\n private readonly _runtime: ExtensionRuntime;\n\n private _reloadConfigPrefixes: string[] = [];\n private _registeredCommandIds: string[] = [];\n private _registeredSpeechProviderIds: string[] = [];\n private _registeredMediaUnderstandingProviderIds: string[] = [];\n private _registeredMarketplaceAdapterIds: string[] = [];\n private readonly _manifestCommands = new Map<string, CommandContribution>();\n\n constructor(\n public readonly id: string,\n public readonly name: string,\n public readonly version: string | undefined,\n public readonly source: string,\n public readonly config: Config,\n public readonly extensionConfig: Record<string, unknown>,\n private readonly _logger: ExtensionLogger,\n private readonly _resolvePath: (input: string) => string,\n private readonly _coreRegistry?: ExtensionRegistryImpl,\n runtime?: ExtensionRuntime,\n ) {\n // Initialize typed event bus\n this._typedEventBus = new TypedEventBus({\n logger: _logger,\n });\n \n this._registry = _coreRegistry ?? new ExtensionRegistryImpl();\n this._runtime =\n runtime ??\n ({\n config: this.config,\n log: this._logger,\n } as ExtensionRuntime);\n }\n\n get logger(): ExtensionLogger {\n return this._logger;\n }\n\n get runtime(): ExtensionRuntime {\n return this._runtime;\n }\n\n registerTool(tool: AgentTool): void {\n if (this._tools.has(tool.name)) {\n this._logger.warn(`Tool ${tool.name} already registered, overwriting`);\n }\n this._tools.set(tool.name, tool);\n this._logger.info(`Registered tool: ${tool.name}`);\n }\n\n registerHook(event: string, handler: Function, opts?: { priority?: number; once?: boolean }): void {\n if (!this._hooks.has(event)) {\n this._hooks.set(event, new Set());\n }\n this._hooks.get(event)!.add(handler);\n\n if (opts?.once) {\n const wrapper = async (...args: unknown[]) => {\n await handler(...args);\n this._hooks.get(event)?.delete(wrapper);\n };\n this._hooks.get(event)!.add(wrapper);\n }\n\n this._logger.info(`Registered hook: ${event}`);\n }\n\n /**\n * Strongly typed hook registration\n * Provides compile-time type checking and IDE autocomplete\n * \n * @example\n * api.on('before_agent_start', async (event, ctx) => {\n * console.log('Agent starting with prompt:', event.prompt);\n * return { systemPrompt: event.systemPrompt };\n * });\n */\n onHook<K extends ExtensionHookEvent>(\n hookName: K,\n handler: HookHandlerMap[K],\n _opts?: { priority?: number },\n ): void {\n // Get execution mode for this hook\n const mode = (HOOK_EXECUTION_MODES as Record<string, HookExecutionMode>)[hookName];\n \n if (!this._typedHooks.has(hookName)) {\n this._typedHooks.set(hookName, new Set());\n }\n \n this._typedHooks.get(hookName)!.add(handler);\n\n this._logger.debug(`Registered typed hook: ${hookName} (mode: ${mode})`);\n }\n\n /**\n * Get typed hooks for a specific event\n */\n _getTypedHooks<K extends ExtensionHookEvent>(hookName: K): HookHandlerMap[K][] {\n const hooks = this._typedHooks.get(hookName);\n return hooks ? Array.from(hooks) as HookHandlerMap[K][] : [];\n }\n\n /** Adds a ChannelPlugin to the extension registry; emits `channel:register` for observability. */\n registerChannel(registration: { plugin: ChannelPlugin }): void {\n const plugin = registration.plugin;\n this._registry.addChannelPlugin(plugin);\n this._eventBus.emit('channel:register', plugin);\n this._logger.info(`Registered channel plugin: ${plugin.id}`);\n }\n\n registerHttpRoute(path: string, handler: HttpRequestHandler): void {\n this._eventBus.emit('http:route', { path, handler });\n this._logger.info(`Registered HTTP route: ${path}`);\n }\n\n registerCommand(command: ExtensionCommand): void {\n const commandId = `ext.${this.id}.${command.name}`;\n\n const definition: CommandDefinition = {\n id: commandId,\n name: command.name,\n aliases: command.aliases,\n description: command.description,\n category: 'extension',\n scope: command.scope ?? ['global'],\n acceptsArgs: command.acceptsArgs,\n examples: command.examples,\n handler: async (ctx, args) => {\n const extensionCtx: ExtensionCommandContext = {\n sessionKey: ctx.sessionKey,\n source: ctx.source,\n isGroup: ctx.isGroup,\n config: ctx.config as SchemaConfig as ExtensionCommandContext['config'],\n reply: (text: string) => ctx.reply(text),\n };\n\n const result = await command.handler(args, extensionCtx);\n\n if (!result) {\n return { content: '', success: true };\n }\n return {\n content: result.content,\n success: result.success ?? true,\n };\n },\n };\n\n commandRegistry.register(definition);\n this._registeredCommandIds.push(commandId);\n this._registry.addCommand(command);\n\n this._eventBus.emit('command:register', command);\n this._logger.info(`Registered chat command: /${command.name}`);\n }\n\n /** Injected from manifest `ui.contributions.commands` by the loader. */\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 onCommand(commandId: string, handler: ExtensionCommandHandler): void {\n const meta = this._manifestCommands.get(commandId);\n if (!meta) {\n this._logger.warn(\n `onCommand: unknown command id \"${commandId}\" — registering runtime-only chat command`,\n );\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) {\n name = meta.id;\n }\n this.registerCommand({\n name,\n description: meta.title,\n handler,\n });\n this._logger.info(`Bound manifest command \"${commandId}\" as /${name}`);\n }\n\n registerReload(handler: ExtensionReloadHandler): void {\n const prefixes =\n this._reloadConfigPrefixes.length > 0\n ? this._reloadConfigPrefixes\n : [`extensions.${this.id}`];\n\n this._registry.addReloadRegistration({\n extensionId: this.id,\n handler,\n configPrefixes: prefixes,\n });\n this._logger.info('Registered reload handler');\n }\n\n /** Called from loader when manifest declares `reload.configPrefixes`. */\n _setReloadConfigPrefixes(prefixes: string[]): void {\n this._reloadConfigPrefixes = [...prefixes];\n }\n\n registerService(service: ExtensionService): void {\n this._eventBus.emit('service:register', service);\n this._logger.info(`Registered service: ${service.id}`);\n }\n\n registerGatewayMethod(method: string, handler: GatewayMethodHandler): void {\n this._eventBus.emit('gateway:method', { method, handler });\n this._logger.info(`Registered gateway method: ${method}`);\n }\n\n registerCli(\n factory: (ctx: { program: Command }) => void,\n opts?: { commands: string[] },\n ): void {\n const reg: ExtensionCliRegistration = {\n extensionId: this.id,\n commands: opts?.commands ?? [],\n factory,\n };\n this._registry.addCliRegistration(reg);\n this._eventBus.emit('cli:register', reg);\n this._logger.info(\n `Registered CLI factory${opts?.commands?.length ? ` (${opts.commands.join(', ')})` : ''}`,\n );\n }\n\n resolvePath(input: string): string {\n return this._resolvePath(input);\n }\n\n emit(event: string, data: unknown): void {\n this._eventBus.emit(event, data);\n }\n\n on(event: string, handler: (data: unknown) => void): void {\n this._eventBus.on(event, handler);\n }\n\n off(event: string, handler: (data: unknown) => void): void {\n this._eventBus.off(event, handler);\n }\n\n // Typed Event Bus\n get events(): TypedEventBus {\n return this._typedEventBus;\n }\n\n // Unified Registry Methods\n \n /**\n * Register a full ProviderPlugin\n */\n registerProvider(plugin: import('./types/providers.js').ProviderPlugin): void {\n import('../providers/plugin-registry.js').then(({ getProviderRegistry }) => {\n const registry = getProviderRegistry();\n registry.register(plugin);\n this._logger.info(`Extension registered provider: ${plugin.id}`);\n }).catch((err: unknown) => {\n this._logger.error(`Failed to register provider: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n /**\n * Register a full ProviderPlugin (alias for registerProvider)\n */\n registerProviderPlugin(plugin: import('./types/providers.js').ProviderPlugin): void {\n this.registerProvider(plugin);\n }\n\n registerSpeechProvider(plugin: SpeechProviderPlugin): void {\n registerSpeechProviderInRegistry(plugin);\n if (!this._registeredSpeechProviderIds.includes(plugin.id)) {\n this._registeredSpeechProviderIds.push(plugin.id);\n }\n this._logger.info(`Registered speech provider: ${plugin.id}`);\n }\n\n /** Speech provider ids registered through this extension API instance. */\n getRegisteredSpeechProviderIds(): readonly string[] {\n return [...this._registeredSpeechProviderIds];\n }\n\n registerMediaUnderstandingProvider(plugin: MediaUnderstandingProvider): void {\n registerMediaUnderstandingProviderInRegistry(plugin);\n if (!this._registeredMediaUnderstandingProviderIds.includes(plugin.id)) {\n this._registeredMediaUnderstandingProviderIds.push(plugin.id);\n }\n this._logger.info(`Registered media understanding provider: ${plugin.id}`);\n }\n\n /** Media understanding provider ids registered through this extension API instance. */\n getRegisteredMediaUnderstandingProviderIds(): readonly string[] {\n return [...this._registeredMediaUnderstandingProviderIds];\n }\n\n /**\n * Register a skills marketplace adapter so it appears in the UI provider picker.\n */\n registerMarketplaceAdapter(registration: {\n adapter: import('../agent/skills/marketplace/adapter.types.js').SkillsMarketplaceAdapter;\n displayName?: string;\n }): void {\n _registerMarketplaceAdapter(registration);\n const adapterId = registration.adapter.id;\n if (!this._registeredMarketplaceAdapterIds.includes(adapterId)) {\n this._registeredMarketplaceAdapterIds.push(adapterId);\n }\n this._logger.info(`Extension registered marketplace adapter: ${adapterId}`);\n }\n\n registerFlag(_name: string, _config: FlagConfig): void {\n // this._registry.registerFlag(name, config, this.id);\n }\n\n getFlag(_name: string): FlagValue {\n return undefined; // this._registry.getFlag(name);\n }\n\n registerShortcut(_key: string, _config: ShortcutConfig): void {\n // this._registry.registerShortcut(key, config, { extensionId: this.id });\n }\n\n registerTui(register: TuiExtensionRegistrar): void {\n this._registry.addTuiRegistration(this.id, register);\n this._logger.info('Registered TUI contributions (deferred until xopc tui starts)');\n }\n\n setLabel(entryId: string, label: string | undefined): void {\n this._runtime.setLabel?.(entryId, label);\n }\n\n sendUserMessage(\n content: ExtensionUserMessageContent,\n options?: ExtensionSendUserMessageOptions,\n ): void {\n this._runtime.sendUserMessage?.(content, options);\n }\n\n appendEntry<T = unknown>(customType: string, data?: T): void {\n this._runtime.appendEntry?.(customType, data);\n }\n\n sendMessage<T = unknown>(\n message: ExtensionCustomMessage<T>,\n options?: ExtensionSendMessageOptions,\n ): void {\n this._runtime.sendMessage?.(message, options);\n }\n\n // Internal methods for extension manager\n _getTools(): Map<string, AgentTool> {\n return this._tools;\n }\n\n _getHooks(): Map<string, Set<Function>> {\n return this._hooks;\n }\n\n _getEventBus(): EventEmitter {\n return this._eventBus;\n }\n\n _cleanup(): void {\n for (const commandId of this._registeredCommandIds) {\n commandRegistry.unregister(commandId);\n }\n this._registeredCommandIds = [];\n this._manifestCommands.clear();\n\n this._registry.removeReloadRegistration(this.id);\n\n for (const adapterId of this._registeredMarketplaceAdapterIds) {\n _unregisterMarketplaceAdapter(adapterId);\n }\n this._registeredMarketplaceAdapterIds = [];\n\n this._typedEventBus.cleanupAll();\n this._eventBus.removeAllListeners();\n this._hooks.clear();\n this._typedHooks.clear();\n }\n}\n\n// ============================================================================\n// Default Logger\n// ============================================================================\n\nexport function createExtensionLogger(prefix: string): ExtensionLogger {\n const childLogger = createLogger(`Extension:${prefix}`);\n\n return {\n debug: (msg: string) => childLogger.debug(msg),\n info: (msg: string) => childLogger.info(msg),\n warn: (msg: string) => childLogger.warn(msg),\n error: (msg: string) => childLogger.error(msg),\n };\n}\n\n// ============================================================================\n// Path Resolver\n// ============================================================================\n\nexport function createPathResolver(extensionDir: string, workspaceDir: string) {\n return (input: string): string => {\n if (input.startsWith('~')) {\n return input.replace('~', process.env.HOME || '');\n }\n if (input.startsWith('.')) {\n return resolve(extensionDir, input);\n }\n if (!isAbsolute(input)) {\n return resolve(workspaceDir, input);\n }\n return input;\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api.js","names":[],"sources":["../../../src/extensions/api.ts"],"sourcesContent":["/**\n * Extension API Implementation\n * \n * Complete extension API with strong typing, security, and provider support.\n */\n\nimport type { AgentTool } from '@earendil-works/pi-agent-core';\nimport type { Command } from 'commander';\nimport type {\n ExtensionApi,\n ExtensionLogger,\n ExtensionRuntime,\n ExtensionCliRegistration,\n GatewayMethodHandler,\n HttpRequestHandler,\n ExtensionCommand,\n ExtensionCommandContext,\n ExtensionCommandHandler,\n ExtensionReloadHandler,\n ExtensionService,\n FlagConfig,\n FlagValue,\n ShortcutConfig,\n HookHandlerMap,\n ExtensionHookEvent,\n ExtensionHookHandler,\n HookExecutionMode,\n TuiExtensionRegistrar,\n ExtensionSendUserMessageOptions,\n ExtensionUserMessageContent,\n ExtensionCustomMessage,\n ExtensionSendMessageOptions,\n} from './types/index.js';\nimport type { CommandContribution, ContractDeclaration } from './types/manifest.js';\nimport type { CommandDefinition } from '../chat-commands/types.js';\nimport { commandRegistry } from '../chat-commands/registry.js';\nimport type { ChannelPlugin } from '../channels/plugin-types.js';\nimport {\n HOOK_EXECUTION_MODES,\n} from './types/hooks.js';\nimport type { Config } from '../config/config-surface.js';\nimport type { Config as SchemaConfig } from '../config/schema.js';\nimport { resolve, isAbsolute } from 'path';\nimport { EventEmitter } from 'events';\nimport { createLogger } from '../utils/logger.js';\nimport { TypedEventBus } from './typed-event-bus.js';\nimport { ExtensionRegistryImpl } from './extension-registry-impl.js';\nimport { registerMarketplaceAdapter as _registerMarketplaceAdapter, unregisterMarketplaceAdapter as _unregisterMarketplaceAdapter } from '../agent/skills/marketplace/registry.js';\nimport { registerSpeechProvider as registerSpeechProviderInRegistry } from '../voice/tts/speech-registry.js';\nimport type { SpeechProviderPlugin } from '../voice/tts/speech-provider-types.js';\nimport { registerMediaUnderstandingProvider as registerMediaUnderstandingProviderInRegistry } from '../media-understanding/registry.js';\nimport type { MediaUnderstandingProvider } from '../media-understanding/types.js';\n\nexport class ExtensionApiImpl implements ExtensionApi {\n private _tools: Map<string, AgentTool> = new Map();\n private _hooks: Map<string, Set<Function>> = new Map();\n // Strongly typed hooks (for on() method)\n private _typedHooks: Map<ExtensionHookEvent, Set<Function>> = new Map();\n private _eventBus = new EventEmitter();\n private _typedEventBus: TypedEventBus;\n \n // Unified Registry\n private _registry: ExtensionRegistryImpl;\n\n private readonly _runtime: ExtensionRuntime;\n\n private _reloadConfigPrefixes: string[] = [];\n private _registeredCommandIds: string[] = [];\n private _registeredSpeechProviderIds: string[] = [];\n private _registeredMediaUnderstandingProviderIds: string[] = [];\n private _registeredMarketplaceAdapterIds: string[] = [];\n private readonly _manifestCommands = new Map<string, CommandContribution>();\n\n constructor(\n public readonly id: string,\n public readonly name: string,\n public readonly version: string | undefined,\n public readonly source: string,\n public readonly config: Config,\n public readonly extensionConfig: Record<string, unknown>,\n private readonly _logger: ExtensionLogger,\n private readonly _resolvePath: (input: string) => string,\n private readonly _coreRegistry?: ExtensionRegistryImpl,\n private readonly _contracts: ContractDeclaration = {},\n runtime?: ExtensionRuntime,\n ) {\n // Initialize typed event bus\n this._typedEventBus = new TypedEventBus({\n logger: _logger,\n });\n \n this._registry = _coreRegistry ?? new ExtensionRegistryImpl();\n this._runtime =\n runtime ??\n ({\n config: this.config,\n log: this._logger,\n } as ExtensionRuntime);\n }\n\n get logger(): ExtensionLogger {\n return this._logger;\n }\n\n get runtime(): ExtensionRuntime {\n return this._runtime;\n }\n\n private _assertContract(kind: keyof ContractDeclaration, value: string): void {\n const declared = this._contracts[kind];\n if (!declared?.length) {\n throw new Error(\n `Extension ${this.id} attempted to register ${kind}:${value}, but manifest contracts.${kind} is missing`,\n );\n }\n const allowed = declared.some((entry) => {\n if (entry === value) return true;\n if (entry.endsWith('*')) return value.startsWith(entry.slice(0, -1));\n return false;\n });\n if (!allowed) {\n throw new Error(\n `Extension ${this.id} attempted to register ${kind}:${value}, but it is not declared in manifest contracts.${kind}`,\n );\n }\n }\n\n registerTool(tool: AgentTool): void {\n this._assertContract('tools', tool.name);\n if (this._tools.has(tool.name)) {\n this._logger.warn(`Tool ${tool.name} already registered, overwriting`);\n }\n this._tools.set(tool.name, tool);\n this._logger.info(`Registered tool: ${tool.name}`);\n }\n\n registerHook(event: string, handler: Function, opts?: { priority?: number; once?: boolean }): void {\n this._assertContract('hooks', event);\n if (!this._hooks.has(event)) {\n this._hooks.set(event, new Set());\n }\n this._hooks.get(event)!.add(handler);\n\n if (opts?.once) {\n const wrapper = async (...args: unknown[]) => {\n await handler(...args);\n this._hooks.get(event)?.delete(wrapper);\n };\n this._hooks.get(event)!.add(wrapper);\n }\n\n this._registry.addHook(event as ExtensionHookEvent, handler as ExtensionHookHandler, this.id, opts?.priority ?? 0);\n this._logger.info(`Registered hook: ${event}`);\n }\n\n /**\n * Strongly typed hook registration\n * Provides compile-time type checking and IDE autocomplete\n * \n * @example\n * api.on('before_agent_start', async (event, ctx) => {\n * console.log('Agent starting with prompt:', event.prompt);\n * return { systemPrompt: event.systemPrompt };\n * });\n */\n onHook<K extends ExtensionHookEvent>(\n hookName: K,\n handler: HookHandlerMap[K],\n _opts?: { priority?: number },\n ): void {\n this._assertContract('hooks', hookName);\n // Get execution mode for this hook\n const mode = (HOOK_EXECUTION_MODES as Record<string, HookExecutionMode>)[hookName];\n \n if (!this._typedHooks.has(hookName)) {\n this._typedHooks.set(hookName, new Set());\n }\n \n this._typedHooks.get(hookName)!.add(handler);\n\n this._logger.debug(`Registered typed hook: ${hookName} (mode: ${mode})`);\n }\n\n /**\n * Get typed hooks for a specific event\n */\n _getTypedHooks<K extends ExtensionHookEvent>(hookName: K): HookHandlerMap[K][] {\n const hooks = this._typedHooks.get(hookName);\n return hooks ? Array.from(hooks) as HookHandlerMap[K][] : [];\n }\n\n /** Adds a ChannelPlugin to the extension registry; emits `channel:register` for observability. */\n registerChannel(registration: { plugin: ChannelPlugin }): void {\n const plugin = registration.plugin;\n this._assertContract('channels', plugin.id);\n this._registry.addChannelPlugin(plugin);\n this._eventBus.emit('channel:register', plugin);\n this._logger.info(`Registered channel plugin: ${plugin.id}`);\n }\n\n registerHttpRoute(path: string, handler: HttpRequestHandler): void {\n this._assertContract('httpRoutes', path);\n this._registry.addHttpRoute(path, handler);\n this._eventBus.emit('http:route', { path, handler });\n this._logger.info(`Registered HTTP route: ${path}`);\n }\n\n registerCommand(command: ExtensionCommand): void {\n this._assertContract('commands', command.name);\n const commandId = `ext.${this.id}.${command.name}`;\n\n const definition: CommandDefinition = {\n id: commandId,\n name: command.name,\n aliases: command.aliases,\n description: command.description,\n category: 'extension',\n scope: command.scope ?? ['global'],\n acceptsArgs: command.acceptsArgs,\n examples: command.examples,\n handler: async (ctx, args) => {\n const extensionCtx: ExtensionCommandContext = {\n sessionKey: ctx.sessionKey,\n source: ctx.source,\n isGroup: ctx.isGroup,\n config: ctx.config as SchemaConfig as ExtensionCommandContext['config'],\n reply: (text: string) => ctx.reply(text),\n };\n\n const result = await command.handler(args, extensionCtx);\n\n if (!result) {\n return { content: '', success: true };\n }\n return {\n content: result.content,\n success: result.success ?? true,\n };\n },\n };\n\n commandRegistry.register(definition);\n this._registeredCommandIds.push(commandId);\n this._registry.addCommand(command);\n\n this._eventBus.emit('command:register', command);\n this._logger.info(`Registered chat command: /${command.name}`);\n }\n\n /** Injected from manifest `ui.contributions.commands` by the loader. */\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 onCommand(commandId: string, handler: ExtensionCommandHandler): void {\n const meta = this._manifestCommands.get(commandId);\n if (!meta) {\n throw new Error(\n `Extension ${this.id} attempted to bind command ${commandId}, but it is not declared in manifest ui.contributions.commands`,\n );\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) {\n name = meta.id;\n }\n this.registerCommand({\n name,\n description: meta.title,\n handler,\n });\n this._logger.info(`Bound manifest command \"${commandId}\" as /${name}`);\n }\n\n registerReload(handler: ExtensionReloadHandler): void {\n const prefixes =\n this._reloadConfigPrefixes.length > 0\n ? this._reloadConfigPrefixes\n : [`extensions.${this.id}`];\n\n this._registry.addReloadRegistration({\n extensionId: this.id,\n handler,\n configPrefixes: prefixes,\n });\n this._logger.info('Registered reload handler');\n }\n\n /** Called from loader when manifest declares `reload.configPrefixes`. */\n _setReloadConfigPrefixes(prefixes: string[]): void {\n this._reloadConfigPrefixes = [...prefixes];\n }\n\n registerService(service: ExtensionService): void {\n this._assertContract('services', service.id);\n this._registry.addService(service);\n this._eventBus.emit('service:register', service);\n this._logger.info(`Registered service: ${service.id}`);\n }\n\n registerGatewayMethod(method: string, handler: GatewayMethodHandler): void {\n this._assertContract('gatewayMethods', method);\n this._registry.addGatewayMethod(method, handler);\n this._eventBus.emit('gateway:method', { method, handler });\n this._logger.info(`Registered gateway method: ${method}`);\n }\n\n registerCli(\n factory: (ctx: { program: Command }) => void,\n opts?: { commands: string[] },\n ): void {\n for (const command of opts?.commands ?? []) {\n this._assertContract('cliCommands', command);\n }\n const reg: ExtensionCliRegistration = {\n extensionId: this.id,\n commands: opts?.commands ?? [],\n factory,\n };\n this._registry.addCliRegistration(reg);\n this._eventBus.emit('cli:register', reg);\n this._logger.info(\n `Registered CLI factory${opts?.commands?.length ? ` (${opts.commands.join(', ')})` : ''}`,\n );\n }\n\n resolvePath(input: string): string {\n return this._resolvePath(input);\n }\n\n emit(event: string, data: unknown): void {\n this._eventBus.emit(event, data);\n }\n\n on(event: string, handler: (data: unknown) => void): void {\n this._eventBus.on(event, handler);\n }\n\n off(event: string, handler: (data: unknown) => void): void {\n this._eventBus.off(event, handler);\n }\n\n // Typed Event Bus\n get events(): TypedEventBus {\n return this._typedEventBus;\n }\n\n // Unified Registry Methods\n \n /**\n * Register a full ProviderPlugin\n */\n registerProvider(plugin: import('./types/providers.js').ProviderPlugin): void {\n this._assertContract('providers', plugin.id);\n import('../providers/plugin-registry.js').then(({ getProviderRegistry }) => {\n const registry = getProviderRegistry();\n registry.register(plugin);\n this._logger.info(`Extension registered provider: ${plugin.id}`);\n }).catch((err: unknown) => {\n this._logger.error(`Failed to register provider: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n /**\n * Register a full ProviderPlugin (alias for registerProvider)\n */\n registerProviderPlugin(plugin: import('./types/providers.js').ProviderPlugin): void {\n this.registerProvider(plugin);\n }\n\n registerSpeechProvider(plugin: SpeechProviderPlugin): void {\n this._assertContract('speechProviders', plugin.id);\n registerSpeechProviderInRegistry(plugin);\n if (!this._registeredSpeechProviderIds.includes(plugin.id)) {\n this._registeredSpeechProviderIds.push(plugin.id);\n }\n this._logger.info(`Registered speech provider: ${plugin.id}`);\n }\n\n /** Speech provider ids registered through this extension API instance. */\n getRegisteredSpeechProviderIds(): readonly string[] {\n return [...this._registeredSpeechProviderIds];\n }\n\n registerMediaUnderstandingProvider(plugin: MediaUnderstandingProvider): void {\n this._assertContract('mediaUnderstandingProviders', plugin.id);\n registerMediaUnderstandingProviderInRegistry(plugin);\n if (!this._registeredMediaUnderstandingProviderIds.includes(plugin.id)) {\n this._registeredMediaUnderstandingProviderIds.push(plugin.id);\n }\n this._logger.info(`Registered media understanding provider: ${plugin.id}`);\n }\n\n /** Media understanding provider ids registered through this extension API instance. */\n getRegisteredMediaUnderstandingProviderIds(): readonly string[] {\n return [...this._registeredMediaUnderstandingProviderIds];\n }\n\n /**\n * Register a skills marketplace adapter so it appears in the UI provider picker.\n */\n registerMarketplaceAdapter(registration: {\n adapter: import('../agent/skills/marketplace/adapter.types.js').SkillsMarketplaceAdapter;\n displayName?: string;\n }): void {\n const adapterId = registration.adapter.id;\n this._assertContract('marketplaceAdapters', adapterId);\n _registerMarketplaceAdapter(registration);\n if (!this._registeredMarketplaceAdapterIds.includes(adapterId)) {\n this._registeredMarketplaceAdapterIds.push(adapterId);\n }\n this._logger.info(`Extension registered marketplace adapter: ${adapterId}`);\n }\n\n registerFlag(_name: string, _config: FlagConfig): void {\n // this._registry.registerFlag(name, config, this.id);\n }\n\n getFlag(_name: string): FlagValue {\n return undefined; // this._registry.getFlag(name);\n }\n\n registerShortcut(_key: string, _config: ShortcutConfig): void {\n // this._registry.registerShortcut(key, config, { extensionId: this.id });\n }\n\n registerTui(register: TuiExtensionRegistrar): void {\n this._assertContract('tui', this.id);\n this._registry.addTuiRegistration(this.id, register);\n this._logger.info('Registered TUI contributions (deferred until xopc tui starts)');\n }\n\n setLabel(entryId: string, label: string | undefined): void {\n this._runtime.setLabel?.(entryId, label);\n }\n\n sendUserMessage(\n content: ExtensionUserMessageContent,\n options?: ExtensionSendUserMessageOptions,\n ): void {\n this._runtime.sendUserMessage?.(content, options);\n }\n\n appendEntry<T = unknown>(customType: string, data?: T): void {\n this._runtime.appendEntry?.(customType, data);\n }\n\n sendMessage<T = unknown>(\n message: ExtensionCustomMessage<T>,\n options?: ExtensionSendMessageOptions,\n ): void {\n this._runtime.sendMessage?.(message, options);\n }\n\n // Internal methods for extension manager\n _getTools(): Map<string, AgentTool> {\n return this._tools;\n }\n\n _getHooks(): Map<string, Set<Function>> {\n return this._hooks;\n }\n\n _getEventBus(): EventEmitter {\n return this._eventBus;\n }\n\n _cleanup(): void {\n for (const commandId of this._registeredCommandIds) {\n commandRegistry.unregister(commandId);\n }\n this._registeredCommandIds = [];\n this._manifestCommands.clear();\n\n this._registry.removeReloadRegistration(this.id);\n\n for (const adapterId of this._registeredMarketplaceAdapterIds) {\n _unregisterMarketplaceAdapter(adapterId);\n }\n this._registeredMarketplaceAdapterIds = [];\n\n this._typedEventBus.cleanupAll();\n this._eventBus.removeAllListeners();\n this._hooks.clear();\n this._typedHooks.clear();\n }\n}\n\n// ============================================================================\n// Default Logger\n// ============================================================================\n\nexport function createExtensionLogger(prefix: string): ExtensionLogger {\n const childLogger = createLogger(`Extension:${prefix}`);\n\n return {\n debug: (msg: string) => childLogger.debug(msg),\n info: (msg: string) => childLogger.info(msg),\n warn: (msg: string) => childLogger.warn(msg),\n error: (msg: string) => childLogger.error(msg),\n };\n}\n\n// ============================================================================\n// Path Resolver\n// ============================================================================\n\nexport function createPathResolver(extensionDir: string, workspaceDir: string) {\n return (input: string): string => {\n if (input.startsWith('~')) {\n return input.replace('~', process.env.HOME || '');\n }\n if (input.startsWith('.')) {\n return resolve(extensionDir, input);\n }\n if (!isAbsolute(input)) {\n return resolve(workspaceDir, input);\n }\n return input;\n };\n}\n"],"mappings":"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"}
|
|
@@ -127,6 +127,7 @@ function discoverExtensionsFromDisk(options = {}, config) {
|
|
|
127
127
|
const discovered = /* @__PURE__ */ new Map();
|
|
128
128
|
const paths = resolveDiscoveryPaths(options, config);
|
|
129
129
|
if (paths.bundledDir) discoverInDirectory(paths.bundledDir, "bundled", discovered);
|
|
130
|
+
if (process.env.XOPC_BUNDLED_EXTENSIONS_ROOT || paths.bundledDir?.includes(`${join("dist", "electron", "extensions")}`)) return Array.from(discovered.values());
|
|
130
131
|
discoverInDirectory(paths.globalDir, "global", discovered);
|
|
131
132
|
if (paths.workspaceExtensionsDir) discoverInDirectory(paths.workspaceExtensionsDir, "workspace", discovered);
|
|
132
133
|
return Array.from(discovered.values());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discover-extensions.js","names":[],"sources":["../../../src/extensions/discover-extensions.ts"],"sourcesContent":["/**\n * Filesystem-only extension discovery (manifest parse, no module load).\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\n\nimport { resolveDefaultAgentId } from '../agent/agent-scope.js';\nimport type { Config } from '../config/schema.js';\nimport {\n resolveBundledExtensionsDir,\n resolveExtensionsDir,\n resolveWorkspaceExtensionsDir,\n} from '../config/paths.js';\nimport { createLogger } from '../utils/logger.js';\nimport { normalizeExtensionManifest } from './normalize-manifest.js';\nimport type { ExtensionManifest } from './types/manifest.js';\nimport type { DiscoveredExtension } from './types/loader.js';\n\n/** App config slice used for discovery (any loaded config shape). */\nexport type DiscoverConfig = Record<string, unknown>;\n\nfunction asSchemaConfig(config: DiscoverConfig): Config {\n return config as Config;\n}\n\nconst EXTENSION_MANIFEST_FILE = 'xopc.extension.json';\n\nconst log = createLogger('ExtensionDiscover');\n\nexport type ExtensionSourceOrigin = 'workspace' | 'global' | 'bundled' | 'config';\n\nexport interface ExtensionLoaderOptions {\n workspaceDir?: string;\n extensionsDir?: string;\n workspaceExtensionsDir?: string;\n bundledExtensions?: string[];\n}\n\nfunction isRecord(x: unknown): x is Record<string, unknown> {\n return typeof x === 'object' && x !== null && !Array.isArray(x);\n}\n\nfunction loadManifest(extensionPath: string): ExtensionManifest | null {\n const manifestPath = join(extensionPath, EXTENSION_MANIFEST_FILE);\n\n if (existsSync(manifestPath)) {\n try {\n const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n log.error({ manifestPath }, 'Manifest root must be a JSON object');\n return null;\n }\n return normalizeExtensionManifest(raw as Record<string, unknown>);\n } catch (error) {\n log.error({ err: error, manifestPath }, 'Failed to parse manifest');\n return null;\n }\n }\n\n const packagePath = join(extensionPath, 'package.json');\n if (existsSync(packagePath)) {\n try {\n const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));\n\n if (packageJson.xopc?.extension) {\n const xopcConfig = packageJson.xopc;\n return normalizeExtensionManifest({\n id: xopcConfig.id || packageJson.name,\n name: xopcConfig.name || packageJson.name,\n description: xopcConfig.description || packageJson.description,\n version: xopcConfig.version || packageJson.version || '1.0.0',\n kind: xopcConfig.kind || 'utility',\n main: xopcConfig.main || packageJson.main || 'index.js',\n configSchema: xopcConfig.configSchema,\n });\n }\n\n if (packageJson.name?.startsWith('xopc-extension-')) {\n const id = packageJson.name.replace('xopc-extension-', '');\n return normalizeExtensionManifest({\n id,\n name: packageJson.name,\n description: packageJson.description,\n version: packageJson.version || '1.0.0',\n kind: 'utility',\n main: packageJson.main || 'index.js',\n });\n }\n } catch (error) {\n log.error({ err: error, packagePath }, 'Failed to parse package.json');\n }\n }\n\n return null;\n}\n\nfunction discoverInDirectory(\n dir: string,\n source: ExtensionSourceOrigin,\n discovered: Map<string, DiscoveredExtension>,\n): void {\n if (!existsSync(dir)) {\n return;\n }\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const extensionPath = join(dir, entry);\n\n try {\n if (!existsSync(extensionPath)) continue;\n } catch {\n continue;\n }\n\n const manifest = loadManifest(extensionPath);\n if (!manifest) continue;\n\n const extensionId = manifest.id || entry;\n\n const existing = discovered.get(extensionId);\n if (existing) {\n const priority = { workspace: 3, global: 2, bundled: 1, config: 0 };\n if (priority[source] <= priority[existing.source]) {\n log.debug(\n { extensionId, from: source, existing: existing.source },\n 'Skipping lower priority extension',\n );\n continue;\n }\n log.info(\n { extensionId, from: source, overriding: existing.source },\n 'Extension override by higher priority source',\n );\n }\n\n discovered.set(extensionId, {\n id: extensionId,\n path: extensionPath,\n source,\n manifest,\n });\n }\n}\n\nfunction resolveDiscoveryPaths(\n options: ExtensionLoaderOptions,\n config?: DiscoverConfig,\n): {\n bundledDir: string | null;\n globalDir: string;\n workspaceExtensionsDir: string | null;\n} {\n const cfg = config ? asSchemaConfig(config) : null;\n const aid = cfg ? resolveDefaultAgentId(cfg) : null;\n\n return {\n bundledDir: resolveBundledExtensionsDir(),\n globalDir: options.extensionsDir ?? resolveExtensionsDir(),\n workspaceExtensionsDir:\n options.workspaceExtensionsDir ??\n (cfg && aid ? resolveWorkspaceExtensionsDir(cfg, aid) : null),\n };\n}\n\n/**\n * Discover extensions from workspace, global, and bundled tiers without loading code.\n */\nexport function discoverExtensionsFromDisk(\n options: ExtensionLoaderOptions = {},\n config?: DiscoverConfig,\n): DiscoveredExtension[] {\n const discovered = new Map<string, DiscoveredExtension>();\n const paths = resolveDiscoveryPaths(options, config);\n\n if (paths.bundledDir) {\n discoverInDirectory(paths.bundledDir, 'bundled', discovered);\n }\n\n discoverInDirectory(paths.globalDir, 'global', discovered);\n if (paths.workspaceExtensionsDir) {\n discoverInDirectory(paths.workspaceExtensionsDir, 'workspace', discovered);\n }\n\n return Array.from(discovered.values());\n}\n\n/** True when extension discovery/load should be skipped entirely. */\nexport function areExtensionsGloballyDisabled(config?: DiscoverConfig): boolean {\n if (\n process.env.XOPC_SKIP_EXTENSIONS === '1' ||\n process.env.XOPC_SKIP_EXTENSIONS === 'true'\n ) {\n return true;\n }\n\n const ext =\n config && isRecord(config) && isRecord((config as Record<string, unknown>).extensions)\n ? ((config as Record<string, unknown>).extensions as Record<string, unknown>)\n : undefined;\n\n return ext?.enabled === false;\n}\n"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"discover-extensions.js","names":[],"sources":["../../../src/extensions/discover-extensions.ts"],"sourcesContent":["/**\n * Filesystem-only extension discovery (manifest parse, no module load).\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\n\nimport { resolveDefaultAgentId } from '../agent/agent-scope.js';\nimport type { Config } from '../config/schema.js';\nimport {\n resolveBundledExtensionsDir,\n resolveExtensionsDir,\n resolveWorkspaceExtensionsDir,\n} from '../config/paths.js';\nimport { createLogger } from '../utils/logger.js';\nimport { normalizeExtensionManifest } from './normalize-manifest.js';\nimport type { ExtensionManifest } from './types/manifest.js';\nimport type { DiscoveredExtension } from './types/loader.js';\n\n/** App config slice used for discovery (any loaded config shape). */\nexport type DiscoverConfig = Record<string, unknown>;\n\nfunction asSchemaConfig(config: DiscoverConfig): Config {\n return config as Config;\n}\n\nconst EXTENSION_MANIFEST_FILE = 'xopc.extension.json';\n\nconst log = createLogger('ExtensionDiscover');\n\nexport type ExtensionSourceOrigin = 'workspace' | 'global' | 'bundled' | 'config';\n\nexport interface ExtensionLoaderOptions {\n workspaceDir?: string;\n extensionsDir?: string;\n workspaceExtensionsDir?: string;\n bundledExtensions?: string[];\n}\n\nfunction isRecord(x: unknown): x is Record<string, unknown> {\n return typeof x === 'object' && x !== null && !Array.isArray(x);\n}\n\nfunction loadManifest(extensionPath: string): ExtensionManifest | null {\n const manifestPath = join(extensionPath, EXTENSION_MANIFEST_FILE);\n\n if (existsSync(manifestPath)) {\n try {\n const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n log.error({ manifestPath }, 'Manifest root must be a JSON object');\n return null;\n }\n return normalizeExtensionManifest(raw as Record<string, unknown>);\n } catch (error) {\n log.error({ err: error, manifestPath }, 'Failed to parse manifest');\n return null;\n }\n }\n\n const packagePath = join(extensionPath, 'package.json');\n if (existsSync(packagePath)) {\n try {\n const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));\n\n if (packageJson.xopc?.extension) {\n const xopcConfig = packageJson.xopc;\n return normalizeExtensionManifest({\n id: xopcConfig.id || packageJson.name,\n name: xopcConfig.name || packageJson.name,\n description: xopcConfig.description || packageJson.description,\n version: xopcConfig.version || packageJson.version || '1.0.0',\n kind: xopcConfig.kind || 'utility',\n main: xopcConfig.main || packageJson.main || 'index.js',\n configSchema: xopcConfig.configSchema,\n });\n }\n\n if (packageJson.name?.startsWith('xopc-extension-')) {\n const id = packageJson.name.replace('xopc-extension-', '');\n return normalizeExtensionManifest({\n id,\n name: packageJson.name,\n description: packageJson.description,\n version: packageJson.version || '1.0.0',\n kind: 'utility',\n main: packageJson.main || 'index.js',\n });\n }\n } catch (error) {\n log.error({ err: error, packagePath }, 'Failed to parse package.json');\n }\n }\n\n return null;\n}\n\nfunction discoverInDirectory(\n dir: string,\n source: ExtensionSourceOrigin,\n discovered: Map<string, DiscoveredExtension>,\n): void {\n if (!existsSync(dir)) {\n return;\n }\n\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const extensionPath = join(dir, entry);\n\n try {\n if (!existsSync(extensionPath)) continue;\n } catch {\n continue;\n }\n\n const manifest = loadManifest(extensionPath);\n if (!manifest) continue;\n\n const extensionId = manifest.id || entry;\n\n const existing = discovered.get(extensionId);\n if (existing) {\n const priority = { workspace: 3, global: 2, bundled: 1, config: 0 };\n if (priority[source] <= priority[existing.source]) {\n log.debug(\n { extensionId, from: source, existing: existing.source },\n 'Skipping lower priority extension',\n );\n continue;\n }\n log.info(\n { extensionId, from: source, overriding: existing.source },\n 'Extension override by higher priority source',\n );\n }\n\n discovered.set(extensionId, {\n id: extensionId,\n path: extensionPath,\n source,\n manifest,\n });\n }\n}\n\nfunction resolveDiscoveryPaths(\n options: ExtensionLoaderOptions,\n config?: DiscoverConfig,\n): {\n bundledDir: string | null;\n globalDir: string;\n workspaceExtensionsDir: string | null;\n} {\n const cfg = config ? asSchemaConfig(config) : null;\n const aid = cfg ? resolveDefaultAgentId(cfg) : null;\n\n return {\n bundledDir: resolveBundledExtensionsDir(),\n globalDir: options.extensionsDir ?? resolveExtensionsDir(),\n workspaceExtensionsDir:\n options.workspaceExtensionsDir ??\n (cfg && aid ? resolveWorkspaceExtensionsDir(cfg, aid) : null),\n };\n}\n\n/**\n * Discover extensions from workspace, global, and bundled tiers without loading code.\n */\nexport function discoverExtensionsFromDisk(\n options: ExtensionLoaderOptions = {},\n config?: DiscoverConfig,\n): DiscoveredExtension[] {\n const discovered = new Map<string, DiscoveredExtension>();\n const paths = resolveDiscoveryPaths(options, config);\n\n if (paths.bundledDir) {\n discoverInDirectory(paths.bundledDir, 'bundled', discovered);\n }\n\n // Electron ships self-contained extension bundles. Do not let stale user/global source\n // extensions override those packaged manifests.\n if (process.env.XOPC_BUNDLED_EXTENSIONS_ROOT || paths.bundledDir?.includes(`${join('dist', 'electron', 'extensions')}`)) {\n return Array.from(discovered.values());\n }\n\n discoverInDirectory(paths.globalDir, 'global', discovered);\n if (paths.workspaceExtensionsDir) {\n discoverInDirectory(paths.workspaceExtensionsDir, 'workspace', discovered);\n }\n\n return Array.from(discovered.values());\n}\n\n/** True when extension discovery/load should be skipped entirely. */\nexport function areExtensionsGloballyDisabled(config?: DiscoverConfig): boolean {\n if (\n process.env.XOPC_SKIP_EXTENSIONS === '1' ||\n process.env.XOPC_SKIP_EXTENSIONS === 'true'\n ) {\n return true;\n }\n\n const ext =\n config && isRecord(config) && isRecord((config as Record<string, unknown>).extensions)\n ? ((config as Record<string, unknown>).extensions as Record<string, unknown>)\n : undefined;\n\n return ext?.enabled === false;\n}\n"],"mappings":"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"}
|
|
@@ -24,6 +24,7 @@ export interface ListedExtension {
|
|
|
24
24
|
path: string;
|
|
25
25
|
origin: 'workspace' | 'global' | 'bundled';
|
|
26
26
|
}
|
|
27
|
+
export declare function peekExtensionManifestFromStoreZip(buffer: Buffer): Record<string, unknown> | undefined;
|
|
27
28
|
/** Read `id` from the shallowest xopc.extension.json in a store zip (for --force / preflight). */
|
|
28
29
|
export declare function peekExtensionIdFromStoreZip(buffer: Buffer): string | undefined;
|
|
29
30
|
/**
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { PACKAGE_VERSION, init_package_version } from "../package-version.js";
|
|
1
2
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
3
|
import { init_logger } from "../utils/logger.js";
|
|
3
4
|
import { init_paths, resolveBundledExtensionsDir, resolveExtensionsDir as resolveExtensionsDir$1 } from "../config/paths.js";
|
|
5
|
+
import { checkEngineCompatibility } from "./engine-check.js";
|
|
6
|
+
import { collectExtensionPackageDependencyIssues } from "./package-contract.js";
|
|
4
7
|
import { MAX_EXTENSION_STORE_ZIP_BYTES } from "./store-zip-limits.js";
|
|
5
8
|
import { dirname, isAbsolute, join, resolve, sep } from "path";
|
|
6
9
|
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
@@ -14,6 +17,7 @@ import AdmZip from "adm-zip";
|
|
|
14
17
|
* Supports three-tier storage: workspace, global, bundled
|
|
15
18
|
*/
|
|
16
19
|
init_paths();
|
|
20
|
+
init_package_version();
|
|
17
21
|
init_logger();
|
|
18
22
|
const log = createLogger("ExtensionInstall");
|
|
19
23
|
const NPM_INSTALL_ENV = process.env.XOPC_EXTENSION_NPM_INSTALL?.trim().toLowerCase();
|
|
@@ -111,8 +115,7 @@ function inferExtensionStripPrefix(primaryManifestPath) {
|
|
|
111
115
|
if (!lower.endsWith(suff)) return "";
|
|
112
116
|
return norm.slice(0, norm.length - 19);
|
|
113
117
|
}
|
|
114
|
-
|
|
115
|
-
function peekExtensionIdFromStoreZip(buffer) {
|
|
118
|
+
function readShallowestExtensionManifestFromZip(buffer) {
|
|
116
119
|
if (buffer.length > 10485760) return void 0;
|
|
117
120
|
let zip;
|
|
118
121
|
try {
|
|
@@ -129,14 +132,22 @@ function peekExtensionIdFromStoreZip(buffer) {
|
|
|
129
132
|
if (!entry) return void 0;
|
|
130
133
|
try {
|
|
131
134
|
const raw = entry.getData().toString("utf8");
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
if (!id || id.includes("/") || id.includes("\\")) return void 0;
|
|
135
|
-
return id;
|
|
135
|
+
const parsed = JSON.parse(raw);
|
|
136
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : void 0;
|
|
136
137
|
} catch {
|
|
137
138
|
return;
|
|
138
139
|
}
|
|
139
140
|
}
|
|
141
|
+
function peekExtensionManifestFromStoreZip(buffer) {
|
|
142
|
+
return readShallowestExtensionManifestFromZip(buffer);
|
|
143
|
+
}
|
|
144
|
+
/** Read `id` from the shallowest xopc.extension.json in a store zip (for --force / preflight). */
|
|
145
|
+
function peekExtensionIdFromStoreZip(buffer) {
|
|
146
|
+
const m = readShallowestExtensionManifestFromZip(buffer);
|
|
147
|
+
const id = typeof m?.id === "string" ? m.id.trim() : "";
|
|
148
|
+
if (!id || id.includes("/") || id.includes("\\")) return void 0;
|
|
149
|
+
return id;
|
|
150
|
+
}
|
|
140
151
|
/**
|
|
141
152
|
* Install an extension from an xopc-store zip buffer (layout: manifest at archive root
|
|
142
153
|
* or under a single top-level folder).
|
|
@@ -293,6 +304,13 @@ async function installFromDirectory(sourceDir, extensionsDir) {
|
|
|
293
304
|
if (existsSync(packagePath)) try {
|
|
294
305
|
packageJson = JSON.parse(readFileSync(packagePath, "utf-8"));
|
|
295
306
|
} catch {}
|
|
307
|
+
if (packageJson) {
|
|
308
|
+
const packageIssues = collectExtensionPackageDependencyIssues(packageJson, { strictRuntimeSdkDeps: true });
|
|
309
|
+
if (packageIssues.length > 0) return {
|
|
310
|
+
ok: false,
|
|
311
|
+
error: `Extension package is not installable as an independent package:\n${packageIssues.map((issue) => `- ${issue.message}`).join("\n")}`
|
|
312
|
+
};
|
|
313
|
+
}
|
|
296
314
|
const extensionId = manifest?.id || packageJson?.name;
|
|
297
315
|
if (!extensionId) return {
|
|
298
316
|
ok: false,
|
|
@@ -307,7 +325,32 @@ async function installFromDirectory(sourceDir, extensionsDir) {
|
|
|
307
325
|
ok: false,
|
|
308
326
|
error: `Extension already exists at ${targetDir}. Use update instead.`
|
|
309
327
|
};
|
|
310
|
-
|
|
328
|
+
if (!manifest) return {
|
|
329
|
+
ok: false,
|
|
330
|
+
error: "Extension must include xopc.extension.json"
|
|
331
|
+
};
|
|
332
|
+
if (!manifest.engines?.xopc) return {
|
|
333
|
+
ok: false,
|
|
334
|
+
error: "Extension manifest must declare engines.xopc"
|
|
335
|
+
};
|
|
336
|
+
const engineCheck = checkEngineCompatibility(PACKAGE_VERSION, manifest.engines.xopc);
|
|
337
|
+
if (engineCheck.parseWarning) return {
|
|
338
|
+
ok: false,
|
|
339
|
+
error: engineCheck.reason ?? `Could not parse engines.xopc ${manifest.engines.xopc}`
|
|
340
|
+
};
|
|
341
|
+
if (!engineCheck.compatible) return {
|
|
342
|
+
ok: false,
|
|
343
|
+
error: engineCheck.reason ?? `xopc ${PACKAGE_VERSION} does not satisfy engines.xopc ${manifest.engines.xopc}`
|
|
344
|
+
};
|
|
345
|
+
if (!manifest.main) return {
|
|
346
|
+
ok: false,
|
|
347
|
+
error: "Extension manifest must declare main"
|
|
348
|
+
};
|
|
349
|
+
if (!/\.(mjs|cjs|js)$/i.test(manifest.main)) return {
|
|
350
|
+
ok: false,
|
|
351
|
+
error: `Extension main must point at built JavaScript (.js/.mjs/.cjs), got: ${manifest.main}`
|
|
352
|
+
};
|
|
353
|
+
const mainFile = manifest.main;
|
|
311
354
|
if (!existsSync(join(sourceDir, mainFile))) return {
|
|
312
355
|
ok: false,
|
|
313
356
|
error: `Main entry not found: ${mainFile}`
|
|
@@ -467,6 +510,6 @@ function readManifest(extensionDir) {
|
|
|
467
510
|
}
|
|
468
511
|
}
|
|
469
512
|
//#endregion
|
|
470
|
-
export { installExtensionFromStoreZip, installFromLocal, installFromNpm, listAllExtensions, peekExtensionIdFromStoreZip, removeExtension, resolveExtensionsDir };
|
|
513
|
+
export { installExtensionFromStoreZip, installFromLocal, installFromNpm, listAllExtensions, peekExtensionIdFromStoreZip, peekExtensionManifestFromStoreZip, removeExtension, resolveExtensionsDir };
|
|
471
514
|
|
|
472
515
|
//# sourceMappingURL=install.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","names":["resolveGlobalExtensionsDir"],"sources":["../../../src/extensions/install.ts"],"sourcesContent":["/**\n * Extension Installation Module\n * Supports installing from npm packages, local directories, and xopc-store zips\n * Supports three-tier storage: workspace, global, bundled\n */\n\nimport { execSync } from 'child_process';\nimport AdmZip from 'adm-zip';\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n cpSync,\n rmSync,\n readdirSync,\n mkdtempSync,\n writeFileSync,\n} from 'fs';\nimport { dirname, join, isAbsolute, resolve, sep } from 'path';\nimport { tmpdir } from 'os';\nimport {\n resolveExtensionsDir as resolveGlobalExtensionsDir,\n resolveBundledExtensionsDir,\n} from '../config/paths.js';\nimport { createLogger } from '../utils/logger.js';\nimport { MAX_EXTENSION_STORE_ZIP_BYTES } from './store-zip-limits.js';\n\nconst log = createLogger('ExtensionInstall');\n\nconst NPM_INSTALL_ENV = process.env.XOPC_EXTENSION_NPM_INSTALL?.trim().toLowerCase();\n\nfunction extractExecSyncOutput(error: unknown): string {\n if (!error || typeof error !== 'object') return '';\n const o = error as { stderr?: Buffer | string; stdout?: Buffer | string; message?: string };\n const read = (x?: Buffer | string | null) => {\n if (x == null) return '';\n return Buffer.isBuffer(x) ? x.toString('utf8') : x;\n };\n const stderr = read(o.stderr).trim();\n const stdout = read(o.stdout).trim();\n if (stderr) return stderr;\n if (stdout) return stdout;\n return typeof o.message === 'string' ? o.message.trim() : '';\n}\n\nfunction truncateOutput(text: string, max = 4500): string {\n const t = text.trim();\n if (t.length <= max) return t;\n return `…${t.slice(-max)}`;\n}\n\n/**\n * Install production `node_modules` for an unpacked extension (package.json dependencies).\n * Uses pnpm when `pnpm-lock.yaml` is present, otherwise npm. Set `XOPC_EXTENSION_NPM_INSTALL=skip`\n * to skip (extensions must ship runnable code or you install deps manually).\n */\nfunction installExtensionProdDependencies(extensionDir: string): { ok: true } | { ok: false; error: string } {\n if (NPM_INSTALL_ENV === 'skip' || NPM_INSTALL_ENV === '0' || NPM_INSTALL_ENV === 'false') {\n log.warn(\n { extensionDir, XOPC_EXTENSION_NPM_INSTALL: process.env.XOPC_EXTENSION_NPM_INSTALL },\n 'Skipping extension dependency install (XOPC_EXTENSION_NPM_INSTALL)',\n );\n return { ok: true };\n }\n\n const execOpts = {\n cwd: extensionDir,\n timeout: 300_000,\n encoding: 'utf-8' as const,\n stdio: ['ignore', 'pipe', 'pipe'] as ('ignore' | 'pipe')[],\n maxBuffer: 20 * 1024 * 1024,\n env: process.env,\n ...(process.platform === 'win32'\n ? { shell: (process.env.ComSpec && process.env.ComSpec.trim()) || 'cmd.exe' }\n : {}),\n };\n\n const run = (label: string, command: string): { ok: true } | { ok: false; error: string } => {\n try {\n execSync(command, execOpts);\n return { ok: true };\n } catch (err) {\n const out = truncateOutput(extractExecSyncOutput(err));\n const hint =\n label === 'npm'\n ? ' Check that Node.js/npm is on PATH, the registry is reachable, and peer/engine rules allow install.'\n : ' Check that pnpm is on PATH or remove pnpm-lock.yaml to use npm instead.';\n return {\n ok: false,\n error: `${label} install failed.${hint}${out ? `\\n\\n${out}` : ''}`,\n };\n }\n };\n\n const usePnpm = existsSync(join(extensionDir, 'pnpm-lock.yaml'));\n if (usePnpm) {\n const pnpm = run('pnpm', 'pnpm install --prod --no-frozen-lockfile');\n if (pnpm.ok) return pnpm;\n const npmFallback = run('npm', 'npm install --omit=dev --no-audit --no-fund');\n if (npmFallback.ok) {\n log.warn({ extensionDir }, 'pnpm failed; extension dependencies installed with npm instead');\n return { ok: true };\n }\n return npmFallback;\n }\n\n return run('npm', 'npm install --omit=dev --no-audit --no-fund');\n}\n\nexport interface InstallOptions {\n source: 'npm' | 'local';\n targetDir: string;\n timeoutMs?: number;\n global?: boolean;\n}\n\nexport interface InstallResult {\n ok: boolean;\n extensionId?: string;\n targetDir?: string;\n origin?: 'workspace' | 'global';\n error?: string;\n}\n\nexport interface ListedExtension {\n id: string;\n name?: string;\n version?: string;\n kind?: string;\n path: string;\n origin: 'workspace' | 'global' | 'bundled';\n}\n\ninterface ExtensionManifest {\n id: string;\n name?: string;\n version?: string;\n main?: string;\n}\n\nfunction isSafeZipPath(name: string): boolean {\n if (!name) return false;\n const normalized = name.replace(/\\\\/g, '/');\n if (normalized.includes('..')) return false;\n if (normalized.startsWith('/') || /^\\w:/.test(normalized)) return false;\n for (const p of normalized.split('/')) {\n if (p === '..') return false;\n }\n return true;\n}\n\nfunction isIgnorableZipEntry(name: string): boolean {\n const n = name.replace(/\\\\/g, '/');\n if (n.startsWith('__MACOSX/')) return true;\n const segments = n.split('/').filter(Boolean);\n for (const s of segments) {\n if (s === '.DS_Store' || s === 'Thumbs.db' || s === 'desktop.ini') return true;\n if (s.startsWith('._')) return true;\n }\n return false;\n}\n\nfunction inferExtensionStripPrefix(primaryManifestPath: string): string {\n const norm = primaryManifestPath.replace(/\\\\/g, '/');\n const lower = norm.toLowerCase();\n const suff = 'xopc.extension.json';\n if (lower === suff) return '';\n if (!lower.endsWith(suff)) return '';\n return norm.slice(0, norm.length - suff.length);\n}\n\n/** Read `id` from the shallowest xopc.extension.json in a store zip (for --force / preflight). */\nexport function peekExtensionIdFromStoreZip(buffer: Buffer): string | undefined {\n if (buffer.length > MAX_EXTENSION_STORE_ZIP_BYTES) return undefined;\n let zip: AdmZip;\n try {\n zip = new AdmZip(buffer);\n } catch {\n return undefined;\n }\n const entries = zip\n .getEntries()\n .filter((e) => !e.isDirectory && e.entryName && !isIgnorableZipEntry(e.entryName));\n const safeEntries = entries.filter((e) => isSafeZipPath(e.entryName));\n const names = safeEntries.map((e) => e.entryName.replace(/\\\\/g, '/'));\n const manifestPaths = names.filter((n) => /(^|\\/)xopc\\.extension\\.json$/i.test(n));\n const shallow = manifestPaths.filter((n) => n.split('/').filter(Boolean).length <= 2);\n if (shallow.length === 0) return undefined;\n shallow.sort((a, b) => a.length - b.length);\n const path = shallow[0];\n const entry = safeEntries.find((e) => e.entryName.replace(/\\\\/g, '/') === path);\n if (!entry) return undefined;\n try {\n const raw = entry.getData().toString('utf8');\n const m = JSON.parse(raw) as { id?: string };\n const id = typeof m.id === 'string' ? m.id.trim() : '';\n if (!id || id.includes('/') || id.includes('\\\\')) return undefined;\n return id;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Install an extension from an xopc-store zip buffer (layout: manifest at archive root\n * or under a single top-level folder).\n */\nexport async function installExtensionFromStoreZip(\n buffer: Buffer,\n extensionsDir: string,\n): Promise<InstallResult> {\n if (buffer.length > MAX_EXTENSION_STORE_ZIP_BYTES) {\n return {\n ok: false,\n error: `Extension zip exceeds maximum size (${MAX_EXTENSION_STORE_ZIP_BYTES} bytes)`,\n };\n }\n\n const zip = new AdmZip(buffer);\n const entries = zip\n .getEntries()\n .filter((e) => !e.isDirectory && e.entryName && !isIgnorableZipEntry(e.entryName));\n const safeEntries = entries.filter((e) => isSafeZipPath(e.entryName));\n if (safeEntries.length === 0) {\n return { ok: false, error: 'Zip is empty or invalid' };\n }\n\n const names = safeEntries.map((e) => e.entryName.replace(/\\\\/g, '/'));\n const manifestPaths = names.filter((n) => /(^|\\/)xopc\\.extension\\.json$/i.test(n));\n if (manifestPaths.length === 0) {\n return { ok: false, error: 'Zip must contain xopc.extension.json' };\n }\n const shallow = manifestPaths.filter((n) => n.split('/').filter(Boolean).length <= 2);\n if (shallow.length === 0) {\n return {\n ok: false,\n error:\n 'xopc.extension.json is nested too deeply; use a zip with manifest at archive root or one folder (e.g. my-ext/xopc.extension.json)',\n };\n }\n shallow.sort((a, b) => a.length - b.length);\n const stripPrefix = inferExtensionStripPrefix(shallow[0]);\n if (stripPrefix) {\n const prefixNorm = stripPrefix.replace(/\\\\/g, '/');\n const outside = names.filter((n) => !n.startsWith(prefixNorm) && !isIgnorableZipEntry(n));\n if (outside.length > 0) {\n return {\n ok: false,\n error: `Invalid zip: expected all files under \"${prefixNorm.replace(/\\/$/, '')}/\", but found \"${outside[0]}\".`,\n };\n }\n }\n\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-ext-zip-'));\n const destResolved = resolve(tmpRoot);\n try {\n for (const e of safeEntries) {\n const norm = e.entryName.replace(/\\\\/g, '/');\n let rel: string;\n if (stripPrefix) {\n const prefixNorm = stripPrefix.replace(/\\\\/g, '/');\n if (!norm.startsWith(prefixNorm)) {\n return {\n ok: false,\n error: `Zip entry outside extension prefix \"${prefixNorm.replace(/\\/$/, '')}\": ${norm}`,\n };\n }\n rel = norm.slice(prefixNorm.length).replace(/^\\//, '');\n } else {\n rel = norm;\n }\n if (!rel || rel.includes('..')) {\n return { ok: false, error: `Refusing unsafe zip entry path: ${e.entryName}` };\n }\n const targetPath = join(tmpRoot, rel);\n const resolvedTarget = resolve(targetPath);\n if (!resolvedTarget.startsWith(destResolved + sep) && resolvedTarget !== destResolved) {\n return {\n ok: false,\n error: `Refusing unsafe zip path (possible traversal): ${e.entryName}`,\n };\n }\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, e.getData());\n }\n\n if (!existsSync(join(tmpRoot, 'xopc.extension.json'))) {\n return { ok: false, error: 'Extracted content is missing xopc.extension.json' };\n }\n\n return await installFromDirectory(tmpRoot, extensionsDir);\n } finally {\n try {\n rmSync(tmpRoot, { recursive: true, force: true });\n } catch {\n // ignore\n }\n }\n}\n\n/**\n * Resolve target extensions directory based on options\n */\nexport function resolveExtensionsDir(\n workspaceDir: string,\n global = false,\n): string {\n if (global) {\n const globalDir = resolveGlobalExtensionsDir();\n mkdirSync(globalDir, { recursive: true });\n return globalDir;\n }\n return join(workspaceDir, '.extensions');\n}\n\n/**\n * Install extension from npm package\n */\nexport async function installFromNpm(\n packageSpec: string,\n extensionsDir: string,\n timeoutMs = 120000,\n): Promise<InstallResult> {\n const tmpDir = join(tmpdir(), `xopc-install-${Date.now()}`);\n\n try {\n console.log(`📦 Downloading ${packageSpec} from npm...`);\n\n // Create temp directory\n mkdirSync(tmpDir, { recursive: true });\n\n // Use npm pack to download package\n const result = execSync(`npm pack ${packageSpec} --pack-destination ${tmpDir}`, {\n timeout: timeoutMs,\n encoding: 'utf-8',\n stdio: 'pipe',\n });\n\n const packedFile = result.trim().split('\\n').pop()?.trim();\n if (!packedFile) {\n return { ok: false, error: 'Failed to download package from npm' };\n }\n\n const archivePath = join(tmpDir, packedFile);\n\n // Extract tarball\n console.log(`📂 Extracting ${packedFile}...`);\n execSync(`tar -xzf ${archivePath} -C ${tmpDir}`, {\n timeout: 30000,\n stdio: 'pipe',\n });\n\n // npm pack extracts to 'package' directory\n const extractDir = join(tmpDir, 'package');\n\n // Validate and install\n return await installFromDirectory(extractDir, extensionsDir);\n } catch (error) {\n return {\n ok: false,\n error: `Failed to install from npm: ${error instanceof Error ? error.message : String(error)}`,\n };\n } finally {\n // Cleanup temp directory\n try {\n rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n }\n}\n\n/**\n * Install extension from local directory\n */\nexport async function installFromLocal(\n localPath: string,\n extensionsDir: string,\n): Promise<InstallResult> {\n // Resolve to absolute path\n const sourceDir = isAbsolute(localPath) ? localPath : resolve(process.cwd(), localPath);\n\n if (!existsSync(sourceDir)) {\n return { ok: false, error: `Directory not found: ${sourceDir}` };\n }\n\n console.log(`📂 Installing from local directory: ${sourceDir}...`);\n\n return await installFromDirectory(sourceDir, extensionsDir);\n}\n\n/**\n * Install extension from extracted directory\n */\nasync function installFromDirectory(\n sourceDir: string,\n extensionsDir: string,\n): Promise<InstallResult> {\n // Validate manifest\n const manifestPath = join(sourceDir, 'xopc.extension.json');\n const packagePath = join(sourceDir, 'package.json');\n\n let manifest: ExtensionManifest | null = null;\n let packageJson: { name?: string; version?: string; dependencies?: Record<string, string> } | null =\n null;\n\n // Try to load xopc.extension.json first\n if (existsSync(manifestPath)) {\n try {\n manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')) as ExtensionManifest;\n } catch {\n return { ok: false, error: 'Invalid xopc.extension.json manifest file' };\n }\n }\n\n // Also try package.json for metadata\n if (existsSync(packagePath)) {\n try {\n packageJson = JSON.parse(readFileSync(packagePath, 'utf-8')) as typeof packageJson;\n } catch {\n // Ignore package.json parse errors\n }\n }\n\n // Determine extension ID\n const extensionId = manifest?.id || packageJson?.name;\n if (!extensionId) {\n return {\n ok: false,\n error: 'Extension must have an id in xopc.extension.json or name in package.json',\n };\n }\n\n // Validate extension ID (no path separators)\n if (extensionId.includes('/') || extensionId.includes('\\\\')) {\n return { ok: false, error: 'Extension ID cannot contain path separators' };\n }\n\n // Check if already exists\n const targetDir = join(extensionsDir, extensionId);\n if (existsSync(targetDir)) {\n return { ok: false, error: `Extension already exists at ${targetDir}. Use update instead.` };\n }\n\n // Validate main entry exists\n const mainFile = manifest?.main || 'index.js';\n const mainPath = join(sourceDir, mainFile);\n if (!existsSync(mainPath)) {\n return { ok: false, error: `Main entry not found: ${mainFile}` };\n }\n\n console.log(`📋 Extension: ${manifest?.name || extensionId} (${extensionId})`);\n if (manifest?.version || packageJson?.version) {\n console.log(`🔖 Version: ${manifest?.version || packageJson?.version}`);\n }\n\n // Create target directory\n mkdirSync(targetDir, { recursive: true });\n\n // Copy files\n console.log(`📂 Copying files to ${targetDir}...`);\n cpSync(sourceDir, targetDir, { recursive: true, force: true });\n\n // Install dependencies if package.json exists and has dependencies\n if (packageJson?.dependencies && Object.keys(packageJson.dependencies).length > 0) {\n console.log(`📦 Installing dependencies...`);\n const depResult = installExtensionProdDependencies(targetDir);\n if (depResult.ok === false) {\n rmSync(targetDir, { recursive: true, force: true });\n return { ok: false, error: depResult.error };\n }\n }\n\n const origin = extensionsDir.includes('.xopc/extensions') ? 'global' : 'workspace';\n\n console.log(`✅ Extension ${extensionId} installed successfully!`);\n console.log(`\\nTo enable the extension, add to your config:`);\n console.log(` extensions:`);\n console.log(` enabled: [${extensionId}]`);\n console.log(` ${extensionId}:`);\n console.log(` # your extension options here\\n`);\n\n return { ok: true, extensionId, targetDir, origin };\n}\n\n/**\n * Remove installed extension from all tiers\n */\nexport function removeExtension(\n extensionId: string,\n workspaceDir: string,\n): { ok: boolean; removedFrom?: string; error?: string } {\n // Try workspace first\n const workspaceDir_ = join(workspaceDir, '.extensions');\n const workspaceExtension = join(workspaceDir_, extensionId);\n\n if (existsSync(workspaceExtension)) {\n try {\n rmSync(workspaceExtension, { recursive: true, force: true });\n return { ok: true, removedFrom: 'workspace' };\n } catch (error) {\n return {\n ok: false,\n error: `Failed to remove from workspace: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n }\n\n // Try global\n const globalDir = resolveGlobalExtensionsDir();\n const globalExtension = join(globalDir, extensionId);\n\n if (existsSync(globalExtension)) {\n try {\n rmSync(globalExtension, { recursive: true, force: true });\n return { ok: true, removedFrom: 'global' };\n } catch (error) {\n return {\n ok: false,\n error: `Failed to remove from global: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n }\n\n return { ok: false, error: `Extension not found: ${extensionId}` };\n}\n\n/**\n * List installed extensions from all tiers\n */\nexport function listAllExtensions(workspaceDir: string): ListedExtension[] {\n const extensions: ListedExtension[] = [];\n const seen = new Set<string>();\n\n // Priority 1: Workspace (highest)\n const workspaceExtensionsDir = join(workspaceDir, '.extensions');\n if (existsSync(workspaceExtensionsDir)) {\n for (const entry of readdirSync(workspaceExtensionsDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n\n const extensionDir = join(workspaceExtensionsDir, entry.name);\n const manifest = readManifest(extensionDir);\n\n if (manifest) {\n seen.add(entry.name);\n extensions.push({\n id: entry.name,\n name: manifest.name,\n version: manifest.version,\n path: extensionDir,\n origin: 'workspace',\n });\n }\n }\n }\n\n // Priority 2: Global\n const globalDir = resolveGlobalExtensionsDir();\n if (existsSync(globalDir)) {\n for (const entry of readdirSync(globalDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n if (seen.has(entry.name)) continue; // Skip if already in workspace\n\n const extensionDir = join(globalDir, entry.name);\n const manifest = readManifest(extensionDir);\n\n if (manifest) {\n seen.add(entry.name);\n extensions.push({\n id: entry.name,\n name: manifest.name,\n version: manifest.version,\n path: extensionDir,\n origin: 'global',\n });\n }\n }\n }\n\n // Priority 3: Bundled (lowest)\n const bundledDir = resolveBundledExtensionsDir();\n if (bundledDir && existsSync(bundledDir)) {\n for (const entry of readdirSync(bundledDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n if (seen.has(entry.name)) continue;\n\n const extensionDir = join(bundledDir, entry.name);\n const manifest = readManifest(extensionDir);\n\n if (manifest) {\n extensions.push({\n id: entry.name,\n name: manifest.name,\n version: manifest.version,\n path: extensionDir,\n origin: 'bundled',\n });\n }\n }\n }\n\n return extensions;\n}\n\nfunction readManifest(extensionDir: string): ExtensionManifest | null {\n const manifestPath = join(extensionDir, 'xopc.extension.json');\n\n if (!existsSync(manifestPath)) {\n // Try package.json\n const packagePath = join(extensionDir, 'package.json');\n if (existsSync(packagePath)) {\n try {\n const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));\n return {\n id: pkg.name,\n name: pkg.xopc?.extension?.name || pkg.name,\n version: pkg.version,\n };\n } catch {\n return null;\n }\n }\n return null;\n }\n\n try {\n return JSON.parse(readFileSync(manifestPath, 'utf-8')) as ExtensionManifest;\n } catch {\n return null;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"install.js","names":["resolveGlobalExtensionsDir"],"sources":["../../../src/extensions/install.ts"],"sourcesContent":["/**\n * Extension Installation Module\n * Supports installing from npm packages, local directories, and xopc-store zips\n * Supports three-tier storage: workspace, global, bundled\n */\n\nimport { execSync } from 'child_process';\nimport AdmZip from 'adm-zip';\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n cpSync,\n rmSync,\n readdirSync,\n mkdtempSync,\n writeFileSync,\n} from 'fs';\nimport { dirname, join, isAbsolute, resolve, sep } from 'path';\nimport { tmpdir } from 'os';\nimport {\n resolveExtensionsDir as resolveGlobalExtensionsDir,\n resolveBundledExtensionsDir,\n} from '../config/paths.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { createLogger } from '../utils/logger.js';\nimport { checkEngineCompatibility } from './engine-check.js';\nimport { collectExtensionPackageDependencyIssues } from './package-contract.js';\nimport { MAX_EXTENSION_STORE_ZIP_BYTES } from './store-zip-limits.js';\n\nconst log = createLogger('ExtensionInstall');\n\nconst NPM_INSTALL_ENV = process.env.XOPC_EXTENSION_NPM_INSTALL?.trim().toLowerCase();\n\nfunction extractExecSyncOutput(error: unknown): string {\n if (!error || typeof error !== 'object') return '';\n const o = error as { stderr?: Buffer | string; stdout?: Buffer | string; message?: string };\n const read = (x?: Buffer | string | null) => {\n if (x == null) return '';\n return Buffer.isBuffer(x) ? x.toString('utf8') : x;\n };\n const stderr = read(o.stderr).trim();\n const stdout = read(o.stdout).trim();\n if (stderr) return stderr;\n if (stdout) return stdout;\n return typeof o.message === 'string' ? o.message.trim() : '';\n}\n\nfunction truncateOutput(text: string, max = 4500): string {\n const t = text.trim();\n if (t.length <= max) return t;\n return `…${t.slice(-max)}`;\n}\n\n/**\n * Install production `node_modules` for an unpacked extension (package.json dependencies).\n * Uses pnpm when `pnpm-lock.yaml` is present, otherwise npm. Set `XOPC_EXTENSION_NPM_INSTALL=skip`\n * to skip (extensions must ship runnable code or you install deps manually).\n */\nfunction installExtensionProdDependencies(extensionDir: string): { ok: true } | { ok: false; error: string } {\n if (NPM_INSTALL_ENV === 'skip' || NPM_INSTALL_ENV === '0' || NPM_INSTALL_ENV === 'false') {\n log.warn(\n { extensionDir, XOPC_EXTENSION_NPM_INSTALL: process.env.XOPC_EXTENSION_NPM_INSTALL },\n 'Skipping extension dependency install (XOPC_EXTENSION_NPM_INSTALL)',\n );\n return { ok: true };\n }\n\n const execOpts = {\n cwd: extensionDir,\n timeout: 300_000,\n encoding: 'utf-8' as const,\n stdio: ['ignore', 'pipe', 'pipe'] as ('ignore' | 'pipe')[],\n maxBuffer: 20 * 1024 * 1024,\n env: process.env,\n ...(process.platform === 'win32'\n ? { shell: (process.env.ComSpec && process.env.ComSpec.trim()) || 'cmd.exe' }\n : {}),\n };\n\n const run = (label: string, command: string): { ok: true } | { ok: false; error: string } => {\n try {\n execSync(command, execOpts);\n return { ok: true };\n } catch (err) {\n const out = truncateOutput(extractExecSyncOutput(err));\n const hint =\n label === 'npm'\n ? ' Check that Node.js/npm is on PATH, the registry is reachable, and peer/engine rules allow install.'\n : ' Check that pnpm is on PATH or remove pnpm-lock.yaml to use npm instead.';\n return {\n ok: false,\n error: `${label} install failed.${hint}${out ? `\\n\\n${out}` : ''}`,\n };\n }\n };\n\n const usePnpm = existsSync(join(extensionDir, 'pnpm-lock.yaml'));\n if (usePnpm) {\n const pnpm = run('pnpm', 'pnpm install --prod --no-frozen-lockfile');\n if (pnpm.ok) return pnpm;\n const npmFallback = run('npm', 'npm install --omit=dev --no-audit --no-fund');\n if (npmFallback.ok) {\n log.warn({ extensionDir }, 'pnpm failed; extension dependencies installed with npm instead');\n return { ok: true };\n }\n return npmFallback;\n }\n\n return run('npm', 'npm install --omit=dev --no-audit --no-fund');\n}\n\nexport interface InstallOptions {\n source: 'npm' | 'local';\n targetDir: string;\n timeoutMs?: number;\n global?: boolean;\n}\n\nexport interface InstallResult {\n ok: boolean;\n extensionId?: string;\n targetDir?: string;\n origin?: 'workspace' | 'global';\n error?: string;\n}\n\nexport interface ListedExtension {\n id: string;\n name?: string;\n version?: string;\n kind?: string;\n path: string;\n origin: 'workspace' | 'global' | 'bundled';\n}\n\ninterface ExtensionManifest {\n id: string;\n name?: string;\n version?: string;\n main?: string;\n engines?: {\n xopc?: string;\n extensionApi?: string;\n extensionUiApi?: string;\n };\n}\n\nfunction isSafeZipPath(name: string): boolean {\n if (!name) return false;\n const normalized = name.replace(/\\\\/g, '/');\n if (normalized.includes('..')) return false;\n if (normalized.startsWith('/') || /^\\w:/.test(normalized)) return false;\n for (const p of normalized.split('/')) {\n if (p === '..') return false;\n }\n return true;\n}\n\nfunction isIgnorableZipEntry(name: string): boolean {\n const n = name.replace(/\\\\/g, '/');\n if (n.startsWith('__MACOSX/')) return true;\n const segments = n.split('/').filter(Boolean);\n for (const s of segments) {\n if (s === '.DS_Store' || s === 'Thumbs.db' || s === 'desktop.ini') return true;\n if (s.startsWith('._')) return true;\n }\n return false;\n}\n\nfunction inferExtensionStripPrefix(primaryManifestPath: string): string {\n const norm = primaryManifestPath.replace(/\\\\/g, '/');\n const lower = norm.toLowerCase();\n const suff = 'xopc.extension.json';\n if (lower === suff) return '';\n if (!lower.endsWith(suff)) return '';\n return norm.slice(0, norm.length - suff.length);\n}\n\nfunction readShallowestExtensionManifestFromZip(buffer: Buffer): Record<string, unknown> | undefined {\n if (buffer.length > MAX_EXTENSION_STORE_ZIP_BYTES) return undefined;\n let zip: AdmZip;\n try {\n zip = new AdmZip(buffer);\n } catch {\n return undefined;\n }\n const entries = zip\n .getEntries()\n .filter((e) => !e.isDirectory && e.entryName && !isIgnorableZipEntry(e.entryName));\n const safeEntries = entries.filter((e) => isSafeZipPath(e.entryName));\n const names = safeEntries.map((e) => e.entryName.replace(/\\\\/g, '/'));\n const manifestPaths = names.filter((n) => /(^|\\/)xopc\\.extension\\.json$/i.test(n));\n const shallow = manifestPaths.filter((n) => n.split('/').filter(Boolean).length <= 2);\n if (shallow.length === 0) return undefined;\n shallow.sort((a, b) => a.length - b.length);\n const path = shallow[0];\n const entry = safeEntries.find((e) => e.entryName.replace(/\\\\/g, '/') === path);\n if (!entry) return undefined;\n try {\n const raw = entry.getData().toString('utf8');\n const parsed = JSON.parse(raw) as unknown;\n return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function peekExtensionManifestFromStoreZip(buffer: Buffer): Record<string, unknown> | undefined {\n return readShallowestExtensionManifestFromZip(buffer);\n}\n\n/** Read `id` from the shallowest xopc.extension.json in a store zip (for --force / preflight). */\nexport function peekExtensionIdFromStoreZip(buffer: Buffer): string | undefined {\n const m = readShallowestExtensionManifestFromZip(buffer) as { id?: string } | undefined;\n const id = typeof m?.id === 'string' ? m.id.trim() : '';\n if (!id || id.includes('/') || id.includes('\\\\')) return undefined;\n return id;\n}\n\n/**\n * Install an extension from an xopc-store zip buffer (layout: manifest at archive root\n * or under a single top-level folder).\n */\nexport async function installExtensionFromStoreZip(\n buffer: Buffer,\n extensionsDir: string,\n): Promise<InstallResult> {\n if (buffer.length > MAX_EXTENSION_STORE_ZIP_BYTES) {\n return {\n ok: false,\n error: `Extension zip exceeds maximum size (${MAX_EXTENSION_STORE_ZIP_BYTES} bytes)`,\n };\n }\n\n const zip = new AdmZip(buffer);\n const entries = zip\n .getEntries()\n .filter((e) => !e.isDirectory && e.entryName && !isIgnorableZipEntry(e.entryName));\n const safeEntries = entries.filter((e) => isSafeZipPath(e.entryName));\n if (safeEntries.length === 0) {\n return { ok: false, error: 'Zip is empty or invalid' };\n }\n\n const names = safeEntries.map((e) => e.entryName.replace(/\\\\/g, '/'));\n const manifestPaths = names.filter((n) => /(^|\\/)xopc\\.extension\\.json$/i.test(n));\n if (manifestPaths.length === 0) {\n return { ok: false, error: 'Zip must contain xopc.extension.json' };\n }\n const shallow = manifestPaths.filter((n) => n.split('/').filter(Boolean).length <= 2);\n if (shallow.length === 0) {\n return {\n ok: false,\n error:\n 'xopc.extension.json is nested too deeply; use a zip with manifest at archive root or one folder (e.g. my-ext/xopc.extension.json)',\n };\n }\n shallow.sort((a, b) => a.length - b.length);\n const stripPrefix = inferExtensionStripPrefix(shallow[0]);\n if (stripPrefix) {\n const prefixNorm = stripPrefix.replace(/\\\\/g, '/');\n const outside = names.filter((n) => !n.startsWith(prefixNorm) && !isIgnorableZipEntry(n));\n if (outside.length > 0) {\n return {\n ok: false,\n error: `Invalid zip: expected all files under \"${prefixNorm.replace(/\\/$/, '')}/\", but found \"${outside[0]}\".`,\n };\n }\n }\n\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-ext-zip-'));\n const destResolved = resolve(tmpRoot);\n try {\n for (const e of safeEntries) {\n const norm = e.entryName.replace(/\\\\/g, '/');\n let rel: string;\n if (stripPrefix) {\n const prefixNorm = stripPrefix.replace(/\\\\/g, '/');\n if (!norm.startsWith(prefixNorm)) {\n return {\n ok: false,\n error: `Zip entry outside extension prefix \"${prefixNorm.replace(/\\/$/, '')}\": ${norm}`,\n };\n }\n rel = norm.slice(prefixNorm.length).replace(/^\\//, '');\n } else {\n rel = norm;\n }\n if (!rel || rel.includes('..')) {\n return { ok: false, error: `Refusing unsafe zip entry path: ${e.entryName}` };\n }\n const targetPath = join(tmpRoot, rel);\n const resolvedTarget = resolve(targetPath);\n if (!resolvedTarget.startsWith(destResolved + sep) && resolvedTarget !== destResolved) {\n return {\n ok: false,\n error: `Refusing unsafe zip path (possible traversal): ${e.entryName}`,\n };\n }\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, e.getData());\n }\n\n if (!existsSync(join(tmpRoot, 'xopc.extension.json'))) {\n return { ok: false, error: 'Extracted content is missing xopc.extension.json' };\n }\n\n return await installFromDirectory(tmpRoot, extensionsDir);\n } finally {\n try {\n rmSync(tmpRoot, { recursive: true, force: true });\n } catch {\n // ignore\n }\n }\n}\n\n/**\n * Resolve target extensions directory based on options\n */\nexport function resolveExtensionsDir(\n workspaceDir: string,\n global = false,\n): string {\n if (global) {\n const globalDir = resolveGlobalExtensionsDir();\n mkdirSync(globalDir, { recursive: true });\n return globalDir;\n }\n return join(workspaceDir, '.extensions');\n}\n\n/**\n * Install extension from npm package\n */\nexport async function installFromNpm(\n packageSpec: string,\n extensionsDir: string,\n timeoutMs = 120000,\n): Promise<InstallResult> {\n const tmpDir = join(tmpdir(), `xopc-install-${Date.now()}`);\n\n try {\n console.log(`📦 Downloading ${packageSpec} from npm...`);\n\n // Create temp directory\n mkdirSync(tmpDir, { recursive: true });\n\n // Use npm pack to download package\n const result = execSync(`npm pack ${packageSpec} --pack-destination ${tmpDir}`, {\n timeout: timeoutMs,\n encoding: 'utf-8',\n stdio: 'pipe',\n });\n\n const packedFile = result.trim().split('\\n').pop()?.trim();\n if (!packedFile) {\n return { ok: false, error: 'Failed to download package from npm' };\n }\n\n const archivePath = join(tmpDir, packedFile);\n\n // Extract tarball\n console.log(`📂 Extracting ${packedFile}...`);\n execSync(`tar -xzf ${archivePath} -C ${tmpDir}`, {\n timeout: 30000,\n stdio: 'pipe',\n });\n\n // npm pack extracts to 'package' directory\n const extractDir = join(tmpDir, 'package');\n\n // Validate and install\n return await installFromDirectory(extractDir, extensionsDir);\n } catch (error) {\n return {\n ok: false,\n error: `Failed to install from npm: ${error instanceof Error ? error.message : String(error)}`,\n };\n } finally {\n // Cleanup temp directory\n try {\n rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n }\n}\n\n/**\n * Install extension from local directory\n */\nexport async function installFromLocal(\n localPath: string,\n extensionsDir: string,\n): Promise<InstallResult> {\n // Resolve to absolute path\n const sourceDir = isAbsolute(localPath) ? localPath : resolve(process.cwd(), localPath);\n\n if (!existsSync(sourceDir)) {\n return { ok: false, error: `Directory not found: ${sourceDir}` };\n }\n\n console.log(`📂 Installing from local directory: ${sourceDir}...`);\n\n return await installFromDirectory(sourceDir, extensionsDir);\n}\n\n/**\n * Install extension from extracted directory\n */\nasync function installFromDirectory(\n sourceDir: string,\n extensionsDir: string,\n): Promise<InstallResult> {\n // Validate manifest\n const manifestPath = join(sourceDir, 'xopc.extension.json');\n const packagePath = join(sourceDir, 'package.json');\n\n let manifest: ExtensionManifest | null = null;\n let packageJson: { name?: string; version?: string; dependencies?: Record<string, string> } | null =\n null;\n\n // Try to load xopc.extension.json first\n if (existsSync(manifestPath)) {\n try {\n manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')) as ExtensionManifest;\n } catch {\n return { ok: false, error: 'Invalid xopc.extension.json manifest file' };\n }\n }\n\n // Also try package.json for metadata\n if (existsSync(packagePath)) {\n try {\n packageJson = JSON.parse(readFileSync(packagePath, 'utf-8')) as typeof packageJson;\n } catch {\n // Ignore package.json parse errors\n }\n }\n\n if (packageJson) {\n const packageIssues = collectExtensionPackageDependencyIssues(\n packageJson as Record<string, unknown>,\n { strictRuntimeSdkDeps: true },\n );\n if (packageIssues.length > 0) {\n return {\n ok: false,\n error: `Extension package is not installable as an independent package:\\n${packageIssues\n .map((issue) => `- ${issue.message}`)\n .join('\\n')}`,\n };\n }\n }\n\n // Determine extension ID\n const extensionId = manifest?.id || packageJson?.name;\n if (!extensionId) {\n return {\n ok: false,\n error: 'Extension must have an id in xopc.extension.json or name in package.json',\n };\n }\n\n // Validate extension ID (no path separators)\n if (extensionId.includes('/') || extensionId.includes('\\\\')) {\n return { ok: false, error: 'Extension ID cannot contain path separators' };\n }\n\n // Check if already exists\n const targetDir = join(extensionsDir, extensionId);\n if (existsSync(targetDir)) {\n return { ok: false, error: `Extension already exists at ${targetDir}. Use update instead.` };\n }\n\n if (!manifest) {\n return { ok: false, error: 'Extension must include xopc.extension.json' };\n }\n\n if (!manifest.engines?.xopc) {\n return { ok: false, error: 'Extension manifest must declare engines.xopc' };\n }\n const engineCheck = checkEngineCompatibility(PACKAGE_VERSION, manifest.engines.xopc);\n if (engineCheck.parseWarning) {\n return {\n ok: false,\n error: engineCheck.reason ?? `Could not parse engines.xopc ${manifest.engines.xopc}`,\n };\n }\n if (!engineCheck.compatible) {\n return {\n ok: false,\n error: engineCheck.reason ?? `xopc ${PACKAGE_VERSION} does not satisfy engines.xopc ${manifest.engines.xopc}`,\n };\n }\n\n if (!manifest.main) {\n return { ok: false, error: 'Extension manifest must declare main' };\n }\n\n if (!/\\.(mjs|cjs|js)$/i.test(manifest.main)) {\n return {\n ok: false,\n error: `Extension main must point at built JavaScript (.js/.mjs/.cjs), got: ${manifest.main}`,\n };\n }\n\n // Validate main entry exists\n const mainFile = manifest.main;\n const mainPath = join(sourceDir, mainFile);\n if (!existsSync(mainPath)) {\n return { ok: false, error: `Main entry not found: ${mainFile}` };\n }\n\n console.log(`📋 Extension: ${manifest?.name || extensionId} (${extensionId})`);\n if (manifest?.version || packageJson?.version) {\n console.log(`🔖 Version: ${manifest?.version || packageJson?.version}`);\n }\n\n // Create target directory\n mkdirSync(targetDir, { recursive: true });\n\n // Copy files\n console.log(`📂 Copying files to ${targetDir}...`);\n cpSync(sourceDir, targetDir, { recursive: true, force: true });\n\n // Install dependencies if package.json exists and has dependencies\n if (packageJson?.dependencies && Object.keys(packageJson.dependencies).length > 0) {\n console.log(`📦 Installing dependencies...`);\n const depResult = installExtensionProdDependencies(targetDir);\n if (depResult.ok === false) {\n rmSync(targetDir, { recursive: true, force: true });\n return { ok: false, error: depResult.error };\n }\n }\n\n const origin = extensionsDir.includes('.xopc/extensions') ? 'global' : 'workspace';\n\n console.log(`✅ Extension ${extensionId} installed successfully!`);\n console.log(`\\nTo enable the extension, add to your config:`);\n console.log(` extensions:`);\n console.log(` enabled: [${extensionId}]`);\n console.log(` ${extensionId}:`);\n console.log(` # your extension options here\\n`);\n\n return { ok: true, extensionId, targetDir, origin };\n}\n\n/**\n * Remove installed extension from all tiers\n */\nexport function removeExtension(\n extensionId: string,\n workspaceDir: string,\n): { ok: boolean; removedFrom?: string; error?: string } {\n // Try workspace first\n const workspaceDir_ = join(workspaceDir, '.extensions');\n const workspaceExtension = join(workspaceDir_, extensionId);\n\n if (existsSync(workspaceExtension)) {\n try {\n rmSync(workspaceExtension, { recursive: true, force: true });\n return { ok: true, removedFrom: 'workspace' };\n } catch (error) {\n return {\n ok: false,\n error: `Failed to remove from workspace: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n }\n\n // Try global\n const globalDir = resolveGlobalExtensionsDir();\n const globalExtension = join(globalDir, extensionId);\n\n if (existsSync(globalExtension)) {\n try {\n rmSync(globalExtension, { recursive: true, force: true });\n return { ok: true, removedFrom: 'global' };\n } catch (error) {\n return {\n ok: false,\n error: `Failed to remove from global: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n }\n\n return { ok: false, error: `Extension not found: ${extensionId}` };\n}\n\n/**\n * List installed extensions from all tiers\n */\nexport function listAllExtensions(workspaceDir: string): ListedExtension[] {\n const extensions: ListedExtension[] = [];\n const seen = new Set<string>();\n\n // Priority 1: Workspace (highest)\n const workspaceExtensionsDir = join(workspaceDir, '.extensions');\n if (existsSync(workspaceExtensionsDir)) {\n for (const entry of readdirSync(workspaceExtensionsDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n\n const extensionDir = join(workspaceExtensionsDir, entry.name);\n const manifest = readManifest(extensionDir);\n\n if (manifest) {\n seen.add(entry.name);\n extensions.push({\n id: entry.name,\n name: manifest.name,\n version: manifest.version,\n path: extensionDir,\n origin: 'workspace',\n });\n }\n }\n }\n\n // Priority 2: Global\n const globalDir = resolveGlobalExtensionsDir();\n if (existsSync(globalDir)) {\n for (const entry of readdirSync(globalDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n if (seen.has(entry.name)) continue; // Skip if already in workspace\n\n const extensionDir = join(globalDir, entry.name);\n const manifest = readManifest(extensionDir);\n\n if (manifest) {\n seen.add(entry.name);\n extensions.push({\n id: entry.name,\n name: manifest.name,\n version: manifest.version,\n path: extensionDir,\n origin: 'global',\n });\n }\n }\n }\n\n // Priority 3: Bundled (lowest)\n const bundledDir = resolveBundledExtensionsDir();\n if (bundledDir && existsSync(bundledDir)) {\n for (const entry of readdirSync(bundledDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n if (seen.has(entry.name)) continue;\n\n const extensionDir = join(bundledDir, entry.name);\n const manifest = readManifest(extensionDir);\n\n if (manifest) {\n extensions.push({\n id: entry.name,\n name: manifest.name,\n version: manifest.version,\n path: extensionDir,\n origin: 'bundled',\n });\n }\n }\n }\n\n return extensions;\n}\n\nfunction readManifest(extensionDir: string): ExtensionManifest | null {\n const manifestPath = join(extensionDir, 'xopc.extension.json');\n\n if (!existsSync(manifestPath)) {\n // Try package.json\n const packagePath = join(extensionDir, 'package.json');\n if (existsSync(packagePath)) {\n try {\n const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));\n return {\n id: pkg.name,\n name: pkg.xopc?.extension?.name || pkg.name,\n version: pkg.version,\n };\n } catch {\n return null;\n }\n }\n return null;\n }\n\n try {\n return JSON.parse(readFileSync(manifestPath, 'utf-8')) as ExtensionManifest;\n } catch {\n return null;\n }\n}\n"],"mappings":"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"}
|
|
@@ -119,10 +119,6 @@ export declare class ExtensionLoader {
|
|
|
119
119
|
*/
|
|
120
120
|
private claimExtensionSlots;
|
|
121
121
|
loadManifest(extensionPath: string): ExtensionManifest | null;
|
|
122
|
-
/**
|
|
123
|
-
* Build candidate extension entry paths. After `tsdown`, manifests may still
|
|
124
|
-
* point at `*.ts` while the shipped tree only contains the emitted `*.js`.
|
|
125
|
-
*/
|
|
126
122
|
private moduleEntryCandidates;
|
|
127
123
|
private loadModule;
|
|
128
124
|
private createExtensionApi;
|