mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-07-09 17:28:45 +00:00
feat(coding-agent): add InlineExtension type for named inline extension factories (#6267)
* feat(coding-agent): add InlineExtension type for named inline extension factories * test(coding-agent): update utilities and add regression test for InlineExtension
This commit is contained in:
parent
c8ada4e76e
commit
b3dff19a04
13 changed files with 164 additions and 19 deletions
|
|
@ -604,6 +604,27 @@ const { session } = await createAgentSession({ resourceLoader: loader });
|
||||||
|
|
||||||
Extensions can register tools, subscribe to events, add commands, and more. See [extensions.md](extensions.md) for the full API.
|
Extensions can register tools, subscribe to events, add commands, and more. See [extensions.md](extensions.md) for the full API.
|
||||||
|
|
||||||
|
**Named inline extensions:** By default, inline factories display as `<inline:1>`, `<inline:2>`, etc. in the startup Extensions list. To show a descriptive name instead, wrap the factory:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { InlineExtension } from "@earendil-works/pi-coding-agent";
|
||||||
|
|
||||||
|
const myProvider: InlineExtension = {
|
||||||
|
name: "my-provider",
|
||||||
|
factory: (pi) => {
|
||||||
|
pi.on("agent_start", () => {
|
||||||
|
console.log("[my-provider] Agent starting");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const loader = new DefaultResourceLoader({
|
||||||
|
extensionFactories: [myProvider],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This displays as `<inline:my-provider>` instead of `<inline:1>`. Bare factory functions are still accepted for backward compatibility.
|
||||||
|
|
||||||
**Event Bus:** Extensions can communicate via `pi.events`. Pass a shared `eventBus` to `DefaultResourceLoader` if you need to emit or listen from outside:
|
**Event Bus:** Extensions can communicate via `pi.events`. Pass a shared `eventBus` to `DefaultResourceLoader` if you need to emit or listen from outside:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
|
@ -1161,6 +1182,7 @@ createGrepTool, createFindTool, createLsTool
|
||||||
type CreateAgentSessionOptions
|
type CreateAgentSessionOptions
|
||||||
type CreateAgentSessionResult
|
type CreateAgentSessionResult
|
||||||
type ExtensionFactory
|
type ExtensionFactory
|
||||||
|
type InlineExtension
|
||||||
type ExtensionAPI
|
type ExtensionAPI
|
||||||
type ToolDefinition
|
type ToolDefinition
|
||||||
type Skill
|
type Skill
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ export type {
|
||||||
GetThinkingLevelHandler,
|
GetThinkingLevelHandler,
|
||||||
GrepToolCallEvent,
|
GrepToolCallEvent,
|
||||||
GrepToolResultEvent,
|
GrepToolResultEvent,
|
||||||
|
InlineExtension,
|
||||||
// Events - Input
|
// Events - Input
|
||||||
InputEvent,
|
InputEvent,
|
||||||
InputEventResult,
|
InputEventResult,
|
||||||
|
|
|
||||||
|
|
@ -1446,6 +1446,14 @@ export interface ProviderModelConfig {
|
||||||
/** Extension factory function type. Supports both sync and async initialization. */
|
/** Extension factory function type. Supports both sync and async initialization. */
|
||||||
export type ExtensionFactory = (pi: ExtensionAPI) => void | Promise<void>;
|
export type ExtensionFactory = (pi: ExtensionAPI) => void | Promise<void>;
|
||||||
|
|
||||||
|
export type InlineExtension =
|
||||||
|
| ExtensionFactory
|
||||||
|
| {
|
||||||
|
/** Display name shown as `<inline:name>` in the startup Extensions list. */
|
||||||
|
name: string;
|
||||||
|
factory: ExtensionFactory;
|
||||||
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Loaded Extension Types
|
// Loaded Extension Types
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ export {
|
||||||
ExtensionRunner,
|
ExtensionRunner,
|
||||||
type ExtensionShortcut,
|
type ExtensionShortcut,
|
||||||
type ExtensionUIContext,
|
type ExtensionUIContext,
|
||||||
|
type InlineExtension,
|
||||||
type LoadExtensionsResult,
|
type LoadExtensionsResult,
|
||||||
type MessageRenderer,
|
type MessageRenderer,
|
||||||
type RegisteredCommand,
|
type RegisteredCommand,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import {
|
||||||
loadExtensionFromFactory,
|
loadExtensionFromFactory,
|
||||||
loadExtensionsCached,
|
loadExtensionsCached,
|
||||||
} from "./extensions/loader.ts";
|
} from "./extensions/loader.ts";
|
||||||
import type { Extension, ExtensionFactory, ExtensionRuntime, LoadExtensionsResult } from "./extensions/types.ts";
|
import type { Extension, ExtensionRuntime, InlineExtension, LoadExtensionsResult } from "./extensions/types.ts";
|
||||||
import { DefaultPackageManager, type PathMetadata, type ResolvedResource } from "./package-manager.ts";
|
import { DefaultPackageManager, type PathMetadata, type ResolvedResource } from "./package-manager.ts";
|
||||||
import type { PromptTemplate } from "./prompt-templates.ts";
|
import type { PromptTemplate } from "./prompt-templates.ts";
|
||||||
import { loadPromptTemplates } from "./prompt-templates.ts";
|
import { loadPromptTemplates } from "./prompt-templates.ts";
|
||||||
|
|
@ -131,7 +131,7 @@ export interface DefaultResourceLoaderOptions {
|
||||||
additionalSkillPaths?: string[];
|
additionalSkillPaths?: string[];
|
||||||
additionalPromptTemplatePaths?: string[];
|
additionalPromptTemplatePaths?: string[];
|
||||||
additionalThemePaths?: string[];
|
additionalThemePaths?: string[];
|
||||||
extensionFactories?: ExtensionFactory[];
|
extensionFactories?: InlineExtension[];
|
||||||
noExtensions?: boolean;
|
noExtensions?: boolean;
|
||||||
noSkills?: boolean;
|
noSkills?: boolean;
|
||||||
noPromptTemplates?: boolean;
|
noPromptTemplates?: boolean;
|
||||||
|
|
@ -169,7 +169,7 @@ export class DefaultResourceLoader implements ResourceLoader {
|
||||||
private additionalSkillPaths: string[];
|
private additionalSkillPaths: string[];
|
||||||
private additionalPromptTemplatePaths: string[];
|
private additionalPromptTemplatePaths: string[];
|
||||||
private additionalThemePaths: string[];
|
private additionalThemePaths: string[];
|
||||||
private extensionFactories: ExtensionFactory[];
|
private extensionFactories: InlineExtension[];
|
||||||
private noExtensions: boolean;
|
private noExtensions: boolean;
|
||||||
private noSkills: boolean;
|
private noSkills: boolean;
|
||||||
private noPromptTemplates: boolean;
|
private noPromptTemplates: boolean;
|
||||||
|
|
@ -896,8 +896,10 @@ export class DefaultResourceLoader implements ResourceLoader {
|
||||||
const extensions: Extension[] = [];
|
const extensions: Extension[] = [];
|
||||||
const errors: Array<{ path: string; error: string }> = [];
|
const errors: Array<{ path: string; error: string }> = [];
|
||||||
|
|
||||||
for (const [index, factory] of this.extensionFactories.entries()) {
|
for (const [index, input] of this.extensionFactories.entries()) {
|
||||||
const extensionPath = `<inline:${index + 1}>`;
|
const isNamed = typeof input !== "function";
|
||||||
|
const factory = isNamed ? input.factory : input;
|
||||||
|
const extensionPath = `<inline:${isNamed ? input.name : index + 1}>`;
|
||||||
try {
|
try {
|
||||||
const extension = await loadExtensionFromFactory(factory, this.cwd, this.eventBus, runtime, extensionPath);
|
const extension = await loadExtensionFromFactory(factory, this.cwd, this.eventBus, runtime, extensionPath);
|
||||||
extensions.push(extension);
|
extensions.push(extension);
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ export type {
|
||||||
ExtensionCommandContext,
|
ExtensionCommandContext,
|
||||||
ExtensionContext,
|
ExtensionContext,
|
||||||
ExtensionFactory,
|
ExtensionFactory,
|
||||||
|
InlineExtension,
|
||||||
SlashCommandInfo,
|
SlashCommandInfo,
|
||||||
SlashCommandSource,
|
SlashCommandSource,
|
||||||
ToolDefinition,
|
ToolDefinition,
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ export type {
|
||||||
ExtensionWidgetOptions,
|
ExtensionWidgetOptions,
|
||||||
FindToolCallEvent,
|
FindToolCallEvent,
|
||||||
GrepToolCallEvent,
|
GrepToolCallEvent,
|
||||||
|
InlineExtension,
|
||||||
InputEvent,
|
InputEvent,
|
||||||
InputEventResult,
|
InputEventResult,
|
||||||
InputSource,
|
InputSource,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import {
|
||||||
import { formatNoModelsAvailableMessage } from "./core/auth-guidance.ts";
|
import { formatNoModelsAvailableMessage } from "./core/auth-guidance.ts";
|
||||||
import { AuthStorage } from "./core/auth-storage.ts";
|
import { AuthStorage } from "./core/auth-storage.ts";
|
||||||
import { exportFromFile } from "./core/export-html/index.ts";
|
import { exportFromFile } from "./core/export-html/index.ts";
|
||||||
import type { ExtensionFactory } from "./core/extensions/types.ts";
|
import type { InlineExtension } from "./core/extensions/types.ts";
|
||||||
import { applyHttpProxySettings, configureHttpDispatcher } from "./core/http-dispatcher.ts";
|
import { applyHttpProxySettings, configureHttpDispatcher } from "./core/http-dispatcher.ts";
|
||||||
import type { ModelRegistry } from "./core/model-registry.ts";
|
import type { ModelRegistry } from "./core/model-registry.ts";
|
||||||
import { resolveCliModel, resolveModelScope, type ScopedModel } from "./core/model-resolver.ts";
|
import { resolveCliModel, resolveModelScope, type ScopedModel } from "./core/model-resolver.ts";
|
||||||
|
|
@ -462,7 +462,7 @@ async function promptForMissingSessionCwd(
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MainOptions {
|
export interface MainOptions {
|
||||||
extensionFactories?: ExtensionFactory[];
|
extensionFactories?: InlineExtension[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function main(args: string[], options?: MainOptions) {
|
export async function main(args: string[], options?: MainOptions) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import {
|
||||||
type SelfUpdatePackageTarget,
|
type SelfUpdatePackageTarget,
|
||||||
VERSION,
|
VERSION,
|
||||||
} from "./config.ts";
|
} from "./config.ts";
|
||||||
import type { ExtensionFactory } from "./core/extensions/types.ts";
|
import type { InlineExtension } from "./core/extensions/types.ts";
|
||||||
import { DefaultPackageManager } from "./core/package-manager.ts";
|
import { DefaultPackageManager } from "./core/package-manager.ts";
|
||||||
import { type AppMode, resolveProjectTrusted } from "./core/project-trust.ts";
|
import { type AppMode, resolveProjectTrusted } from "./core/project-trust.ts";
|
||||||
import { DefaultResourceLoader } from "./core/resource-loader.ts";
|
import { DefaultResourceLoader } from "./core/resource-loader.ts";
|
||||||
|
|
@ -484,7 +484,7 @@ function prepareWindowsNpmSelfUpdate(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PackageCommandRuntimeOptions {
|
export interface PackageCommandRuntimeOptions {
|
||||||
extensionFactories?: ExtensionFactory[];
|
extensionFactories?: InlineExtension[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommandSettingsResult {
|
interface CommandSettingsResult {
|
||||||
|
|
@ -507,7 +507,7 @@ async function createCommandSettingsManager(options: {
|
||||||
agentDir: string;
|
agentDir: string;
|
||||||
projectTrustOverride?: boolean;
|
projectTrustOverride?: boolean;
|
||||||
useSavedProjectTrustOnly?: boolean;
|
useSavedProjectTrustOnly?: boolean;
|
||||||
extensionFactories?: ExtensionFactory[];
|
extensionFactories?: InlineExtension[];
|
||||||
}): Promise<CommandSettingsResult> {
|
}): Promise<CommandSettingsResult> {
|
||||||
const settingsManager = SettingsManager.create(options.cwd, options.agentDir, { projectTrusted: false });
|
const settingsManager = SettingsManager.create(options.cwd, options.agentDir, { projectTrusted: false });
|
||||||
const projectTrustWarnings: string[] = [];
|
const projectTrustWarnings: string[] = [];
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import { ModelRegistry } from "../../src/core/model-registry.ts";
|
||||||
import { SessionManager } from "../../src/core/session-manager.ts";
|
import { SessionManager } from "../../src/core/session-manager.ts";
|
||||||
import type { Settings } from "../../src/core/settings-manager.ts";
|
import type { Settings } from "../../src/core/settings-manager.ts";
|
||||||
import { SettingsManager } from "../../src/core/settings-manager.ts";
|
import { SettingsManager } from "../../src/core/settings-manager.ts";
|
||||||
import type { ExtensionFactory, ResourceLoader } from "../../src/index.ts";
|
import type { InlineExtension, ResourceLoader } from "../../src/index.ts";
|
||||||
import {
|
import {
|
||||||
type CreateTestExtensionsResultInput,
|
type CreateTestExtensionsResultInput,
|
||||||
createTestExtensionsResult,
|
createTestExtensionsResult,
|
||||||
|
|
@ -69,7 +69,7 @@ export interface HarnessOptions {
|
||||||
allowedToolNames?: string[];
|
allowedToolNames?: string[];
|
||||||
excludedToolNames?: string[];
|
excludedToolNames?: string[];
|
||||||
resourceLoader?: ResourceLoader;
|
resourceLoader?: ResourceLoader;
|
||||||
extensionFactories?: Array<ExtensionFactory | CreateTestExtensionsResultInput>;
|
extensionFactories?: Array<InlineExtension | CreateTestExtensionsResultInput>;
|
||||||
withConfiguredAuth?: boolean;
|
withConfiguredAuth?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
import { DefaultResourceLoader } from "../../../src/core/resource-loader.ts";
|
||||||
|
import type { ExtensionAPI } from "../../../src/index.ts";
|
||||||
|
|
||||||
|
const noop: (pi: ExtensionAPI) => void = () => {};
|
||||||
|
|
||||||
|
describe("inline extension naming", () => {
|
||||||
|
const roots: string[] = [];
|
||||||
|
|
||||||
|
function fixture(name: string) {
|
||||||
|
const root = join(tmpdir(), `pi-inline-naming-${name}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
||||||
|
const cwd = join(root, "project");
|
||||||
|
const agentDir = join(root, "agent");
|
||||||
|
mkdirSync(cwd, { recursive: true });
|
||||||
|
mkdirSync(agentDir, { recursive: true });
|
||||||
|
roots.push(root);
|
||||||
|
return { root, cwd, agentDir };
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
roots.length = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
while (roots.length > 0) {
|
||||||
|
const root = roots.pop();
|
||||||
|
if (root && existsSync(root)) {
|
||||||
|
rmSync(root, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("displays bare factories as <inline:N>", async () => {
|
||||||
|
const { cwd, agentDir } = fixture("bare");
|
||||||
|
const loader = new DefaultResourceLoader({
|
||||||
|
cwd,
|
||||||
|
agentDir,
|
||||||
|
noSkills: true,
|
||||||
|
noPromptTemplates: true,
|
||||||
|
noThemes: true,
|
||||||
|
extensionFactories: [noop, noop],
|
||||||
|
});
|
||||||
|
|
||||||
|
await loader.reload();
|
||||||
|
|
||||||
|
const result = loader.getExtensions();
|
||||||
|
|
||||||
|
expect(result.extensions).toHaveLength(2);
|
||||||
|
expect(result.extensions[0].path).toBe("<inline:1>");
|
||||||
|
expect(result.extensions[1].path).toBe("<inline:2>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("displays named wrappers as <inline:name>", async () => {
|
||||||
|
const { cwd, agentDir } = fixture("named");
|
||||||
|
const loader = new DefaultResourceLoader({
|
||||||
|
cwd,
|
||||||
|
agentDir,
|
||||||
|
noSkills: true,
|
||||||
|
noPromptTemplates: true,
|
||||||
|
noThemes: true,
|
||||||
|
extensionFactories: [
|
||||||
|
{ name: "my-provider", factory: noop },
|
||||||
|
{ name: "my-commands", factory: noop },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await loader.reload();
|
||||||
|
|
||||||
|
const result = loader.getExtensions();
|
||||||
|
|
||||||
|
expect(result.extensions).toHaveLength(2);
|
||||||
|
expect(result.extensions[0].path).toBe("<inline:my-provider>");
|
||||||
|
expect(result.extensions[1].path).toBe("<inline:my-commands>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports mixed bare and named factories", async () => {
|
||||||
|
const { cwd, agentDir } = fixture("mixed");
|
||||||
|
const loader = new DefaultResourceLoader({
|
||||||
|
cwd,
|
||||||
|
agentDir,
|
||||||
|
noSkills: true,
|
||||||
|
noPromptTemplates: true,
|
||||||
|
noThemes: true,
|
||||||
|
extensionFactories: [noop, { name: "named-ext", factory: noop }, noop],
|
||||||
|
});
|
||||||
|
|
||||||
|
await loader.reload();
|
||||||
|
|
||||||
|
const result = loader.getExtensions();
|
||||||
|
|
||||||
|
expect(result.extensions).toHaveLength(3);
|
||||||
|
expect(result.extensions[0].path).toBe("<inline:1>");
|
||||||
|
expect(result.extensions[1].path).toBe("<inline:named-ext>");
|
||||||
|
expect(result.extensions[2].path).toBe("<inline:3>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -32,7 +32,7 @@ import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||||
import { SessionManager } from "../src/core/session-manager.ts";
|
import { SessionManager } from "../src/core/session-manager.ts";
|
||||||
import type { Settings } from "../src/core/settings-manager.ts";
|
import type { Settings } from "../src/core/settings-manager.ts";
|
||||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||||
import type { ExtensionFactory, ResourceLoader } from "../src/index.ts";
|
import type { InlineExtension, ResourceLoader } from "../src/index.ts";
|
||||||
import {
|
import {
|
||||||
type CreateTestExtensionsResultInput,
|
type CreateTestExtensionsResultInput,
|
||||||
createTestExtensionsResult,
|
createTestExtensionsResult,
|
||||||
|
|
@ -335,7 +335,7 @@ export interface HarnessOptions {
|
||||||
/** Optional resource loader override. */
|
/** Optional resource loader override. */
|
||||||
resourceLoader?: ResourceLoader;
|
resourceLoader?: ResourceLoader;
|
||||||
/** Inline extensions to load into the session resource loader. */
|
/** Inline extensions to load into the session resource loader. */
|
||||||
extensionFactories?: Array<ExtensionFactory | CreateTestExtensionsResultInput>;
|
extensionFactories?: Array<InlineExtension | CreateTestExtensionsResultInput>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Harness {
|
export interface Harness {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,12 @@ import { getOAuthApiKey } from "@earendil-works/pi-ai/oauth";
|
||||||
import { AgentSession } from "../src/core/agent-session.ts";
|
import { AgentSession } from "../src/core/agent-session.ts";
|
||||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||||
import { createEventBus } from "../src/core/event-bus.ts";
|
import { createEventBus } from "../src/core/event-bus.ts";
|
||||||
import type { Extension, ExtensionFactory, LoadExtensionsResult } from "../src/core/extensions/index.ts";
|
import type {
|
||||||
|
Extension,
|
||||||
|
ExtensionFactory,
|
||||||
|
InlineExtension,
|
||||||
|
LoadExtensionsResult,
|
||||||
|
} from "../src/core/extensions/index.ts";
|
||||||
import { createExtensionRuntime, loadExtensionFromFactory } from "../src/core/extensions/loader.ts";
|
import { createExtensionRuntime, loadExtensionFromFactory } from "../src/core/extensions/loader.ts";
|
||||||
import { ModelRegistry } from "../src/core/model-registry.ts";
|
import { ModelRegistry } from "../src/core/model-registry.ts";
|
||||||
import type { ResourceLoader } from "../src/core/resource-loader.ts";
|
import type { ResourceLoader } from "../src/core/resource-loader.ts";
|
||||||
|
|
@ -181,8 +186,10 @@ export interface CreateTestExtensionsResultInput {
|
||||||
path?: string;
|
path?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TestExtensionInput = InlineExtension | CreateTestExtensionsResultInput;
|
||||||
|
|
||||||
export async function createTestExtensionsResult(
|
export async function createTestExtensionsResult(
|
||||||
inputs: Array<ExtensionFactory | CreateTestExtensionsResultInput>,
|
inputs: TestExtensionInput[],
|
||||||
cwd = process.cwd(),
|
cwd = process.cwd(),
|
||||||
): Promise<LoadExtensionsResult> {
|
): Promise<LoadExtensionsResult> {
|
||||||
const runtime = createExtensionRuntime();
|
const runtime = createExtensionRuntime();
|
||||||
|
|
@ -190,9 +197,12 @@ export async function createTestExtensionsResult(
|
||||||
const extensions: Extension[] = [];
|
const extensions: Extension[] = [];
|
||||||
|
|
||||||
for (const [index, input] of inputs.entries()) {
|
for (const [index, input] of inputs.entries()) {
|
||||||
const factory = typeof input === "function" ? input : input.factory;
|
const isObject = typeof input !== "function";
|
||||||
const extensionPath =
|
const hasName = isObject && "name" in input;
|
||||||
typeof input === "function" ? `<inline:${index + 1}>` : (input.path ?? `<inline:${index + 1}>`);
|
const hasPath = isObject && "path" in input && typeof input.path === "string" && input.path !== "";
|
||||||
|
const factory = isObject ? input.factory : input;
|
||||||
|
const extensionPath = hasName ? `<inline:${input.name}>` : hasPath ? input.path : `<inline:${index + 1}>`;
|
||||||
|
|
||||||
extensions.push(await loadExtensionFromFactory(factory, cwd, eventBus, runtime, extensionPath));
|
extensions.push(await loadExtensionFromFactory(factory, cwd, eventBus, runtime, extensionPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue