@codemation/host 0.1.6 → 0.2.0
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/CHANGELOG.md +24 -0
- package/dist/{AppConfigFactory-BiFHnorf.d.ts → AppConfigFactory-DHdAGOmC.d.ts} +3 -3
- package/dist/{AppContainerFactory-BRU02PTm.js → AppContainerFactory-BKaAUIi-.js} +91 -27
- package/dist/AppContainerFactory-BKaAUIi-.js.map +1 -0
- package/dist/{CodemationConfig-DuGk7uN5.d.ts → CodemationConfig-DBbMU3HB.d.ts} +2 -2
- package/dist/{CodemationConfigNormalizer-BP2-0ZDE.d.ts → CodemationConfigNormalizer-C8wC0skq.d.ts} +2 -2
- package/dist/{CodemationConsumerConfigLoader-D5CSz3TQ.d.ts → CodemationConsumerConfigLoader-Ceh6sB3X.d.ts} +3 -2
- package/dist/{CodemationConsumerConfigLoader-C_ISRrpI.js → CodemationConsumerConfigLoader-D6LFSlp5.js} +20 -7
- package/dist/CodemationConsumerConfigLoader-D6LFSlp5.js.map +1 -0
- package/dist/{CodemationPluginListMerger-DFzGgfyI.d.ts → CodemationPluginListMerger-Cx9DnyR-.d.ts} +5 -5
- package/dist/{CredentialServices-Bhejvys-.d.ts → CredentialServices-CYETzKyb.d.ts} +14 -5
- package/dist/{CredentialServices-xVxVA9Tq.js → CredentialServices-D8BBZH1i.js} +40 -6
- package/dist/CredentialServices-D8BBZH1i.js.map +1 -0
- package/dist/{PublicFrontendBootstrapFactory-CSgWyTra.d.ts → PublicFrontendBootstrapFactory-DeMjp3cs.d.ts} +5 -2
- package/dist/consumer.d.ts +4 -4
- package/dist/consumer.js +1 -1
- package/dist/credentials.d.ts +3 -3
- package/dist/credentials.js +1 -1
- package/dist/devServerSidecar.d.ts +1 -1
- package/dist/{index-BQaZZmOm.d.ts → index-dK05sTQ4.d.ts} +50 -60
- package/dist/index.d.ts +97 -12
- package/dist/index.js +4 -4
- package/dist/nextServer.d.ts +16 -8
- package/dist/nextServer.js +2 -2
- package/dist/{persistenceServer-fdldtXJH.d.ts → persistenceServer-D9vUTMqc.d.ts} +2 -2
- package/dist/persistenceServer.d.ts +5 -5
- package/dist/{server-ChTCEc6R.js → server-C-WZcsId.js} +5 -6
- package/dist/{server-ChTCEc6R.js.map → server-C-WZcsId.js.map} +1 -1
- package/dist/{server-Cpzpy1Ar.d.ts → server-fxqY2WEi.d.ts} +5 -5
- package/dist/server.d.ts +8 -8
- package/dist/server.js +4 -4
- package/package.json +6 -5
- package/playwright.config.ts +10 -6
- package/src/application/contracts/WorkflowViewContracts.ts +3 -0
- package/src/application/mapping/WorkflowDefinitionMapper.ts +117 -19
- package/src/bootstrap/AppContainerFactory.ts +2 -0
- package/src/domain/credentials/CredentialInstanceService.ts +8 -1
- package/src/domain/credentials/CredentialOAuth2ScopeResolver.ts +61 -0
- package/src/domain/credentials/CredentialServices.ts +1 -0
- package/src/domain/credentials/OAuth2ConnectServiceFactory.ts +9 -2
- package/src/presentation/http/routeHandlers/CredentialHttpRouteHandler.ts +2 -1
- package/src/presentation/http/routeHandlers/WorkflowHttpRouteHandler.ts +4 -2
- package/src/presentation/server/CodemationConsumerConfigLoader.ts +28 -5
- package/src/presentation/server/CodemationPluginDiscovery.ts +4 -6
- package/dist/AppContainerFactory-BRU02PTm.js.map +0 -1
- package/dist/CodemationConsumerConfigLoader-C_ISRrpI.js.map +0 -1
- package/dist/CredentialServices-xVxVA9Tq.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-ChTCEc6R.js","names":["config: CodemationConfig","consumerRoot: string","configSource?: string","workflowSources: ReadonlyArray<string>","env?: Readonly<NodeJS.ProcessEnv>","consumerConfigLoader: CodemationConsumerConfigLoader","appConfigFactory: AppConfigFactory","discoveredPackages: CodemationDiscoveredPluginPackage[]","resolvedPackages: CodemationResolvedPluginPackage[]","packageRoots: string[]"],"sources":["../src/presentation/http/CodemationServerGatewayFactory.ts","../src/presentation/server/AppConfigLoader.ts","../src/presentation/server/CodemationPluginDiscovery.ts"],"sourcesContent":["import { accessSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { QueryBus } from \"../../application/bus/QueryBus\";\nimport type { WorkflowDto, WorkflowSummary } from \"../../application/contracts/WorkflowViewContracts\";\nimport { WorkflowDefinitionMapper } from \"../../application/mapping/WorkflowDefinitionMapper\";\nimport { GetWorkflowDetailQuery } from \"../../application/queries/GetWorkflowDetailQuery\";\nimport { GetWorkflowSummariesQuery } from \"../../application/queries/GetWorkflowSummariesQuery\";\nimport { ApplicationTokens } from \"../../applicationTokens\";\nimport { AppContainerFactory } from \"../../bootstrap/AppContainerFactory\";\nimport { AppContainerLifecycle } from \"../../bootstrap/AppContainerLifecycle\";\nimport { FrontendRuntime } from \"../../bootstrap/runtime/FrontendRuntime\";\nimport { AppConfigFactory } from \"../../bootstrap/runtime/AppConfigFactory\";\nimport type { CodemationConfig } from \"../config/CodemationConfig\";\nimport { CodemationConfigNormalizer } from \"../config/CodemationConfigNormalizer\";\nimport { CodemationHonoApiApp } from \"./hono/CodemationHonoApiAppFactory\";\n\ntype ServerGatewayContext = Readonly<{\n container: import(\"@codemation/core\").Container;\n httpApi: CodemationHonoApiApp;\n queryBus: QueryBus;\n workflowDefinitionMapper: WorkflowDefinitionMapper;\n}>;\n\nexport class CodemationServerGateway {\n private static readonly contextsByConfig = new WeakMap<object, Promise<ServerGatewayContext>>();\n\n constructor(\n private readonly config: CodemationConfig,\n private readonly consumerRoot: string,\n private readonly configSource?: string,\n private readonly workflowSources: ReadonlyArray<string> = [],\n private readonly env?: Readonly<NodeJS.ProcessEnv>,\n ) {}\n\n async dispatch(request: Request): Promise<Response> {\n return await (await this.getContext()).httpApi.fetch(request);\n }\n\n async prepare(): Promise<void> {\n await this.getContext();\n }\n\n async close(): Promise<void> {\n const cachedContext = CodemationServerGateway.contextsByConfig.get(this.config as object);\n if (!cachedContext) {\n return;\n }\n CodemationServerGateway.contextsByConfig.delete(this.config as object);\n await (await cachedContext).container.resolve(AppContainerLifecycle).stop();\n }\n\n async loadWorkflowSummaries(): Promise<ReadonlyArray<WorkflowSummary>> {\n const context = await this.getContext();\n const workflows = await context.queryBus.execute(new GetWorkflowSummariesQuery());\n return workflows.map((workflow) => context.workflowDefinitionMapper.toSummary(workflow));\n }\n\n async loadWorkflowDetail(workflowId: string): Promise<WorkflowDto> {\n const context = await this.getContext();\n const workflow = await context.queryBus.execute(new GetWorkflowDetailQuery(workflowId));\n if (!workflow) {\n throw new Error(`Unknown workflowId: ${workflowId}`);\n }\n return await context.workflowDefinitionMapper.map(workflow);\n }\n\n private getContext(): Promise<ServerGatewayContext> {\n const cachedContext = CodemationServerGateway.contextsByConfig.get(this.config as object);\n if (cachedContext) {\n return cachedContext;\n }\n const nextContext = this.createContext();\n CodemationServerGateway.contextsByConfig.set(this.config as object, nextContext);\n return nextContext;\n }\n\n private async createContext(): Promise<ServerGatewayContext> {\n const repoRoot = this.detectWorkspaceRoot(this.consumerRoot);\n // This gateway is the config/env boundary that materializes AppConfig from raw inputs.\n // eslint-disable-next-line no-restricted-properties\n const env = this.env ?? process.env;\n const appConfig = new AppConfigFactory().create({\n repoRoot,\n consumerRoot: this.consumerRoot,\n env,\n config: new CodemationConfigNormalizer().normalize(this.config),\n workflowSources: this.resolveWorkflowSources(),\n });\n const container = await new AppContainerFactory().create({\n appConfig,\n sharedWorkflowWebsocketServer: null,\n });\n await container.resolve(FrontendRuntime).start();\n return {\n container,\n httpApi: container.resolve(CodemationHonoApiApp),\n queryBus: container.resolve(ApplicationTokens.QueryBus),\n workflowDefinitionMapper: container.resolve(WorkflowDefinitionMapper),\n };\n }\n\n private resolveWorkflowSources(): ReadonlyArray<string> {\n if (this.workflowSources.length > 0) {\n return [...this.workflowSources];\n }\n if (!this.configSource || !this.config.workflows || this.config.workflows.length === 0) {\n return [];\n }\n return [this.configSource];\n }\n private detectWorkspaceRoot(startDirectory: string): string {\n let currentDirectory = path.resolve(startDirectory);\n while (true) {\n try {\n accessSync(path.resolve(currentDirectory, \"pnpm-workspace.yaml\"));\n return currentDirectory;\n } catch {\n const parentDirectory = path.dirname(currentDirectory);\n if (parentDirectory === currentDirectory) {\n return startDirectory;\n }\n currentDirectory = parentDirectory;\n }\n }\n }\n}\n","import type { AppConfig } from \"../config/AppConfig\";\nimport { CodemationConsumerConfigLoader } from \"./CodemationConsumerConfigLoader\";\nimport { AppConfigFactory } from \"../../bootstrap/runtime/AppConfigFactory\";\n\nexport type AppConfigLoadResult = Readonly<{\n appConfig: AppConfig;\n bootstrapSource: string | null;\n}>;\n\nexport class AppConfigLoader {\n constructor(\n private readonly consumerConfigLoader: CodemationConsumerConfigLoader = new CodemationConsumerConfigLoader(),\n private readonly appConfigFactory: AppConfigFactory = new AppConfigFactory(),\n ) {}\n\n async load(\n args: Readonly<{\n consumerRoot: string;\n repoRoot: string;\n env: NodeJS.ProcessEnv;\n configPathOverride?: string;\n }>,\n ): Promise<AppConfigLoadResult> {\n const resolution = await this.consumerConfigLoader.load({\n consumerRoot: args.consumerRoot,\n configPathOverride: args.configPathOverride,\n });\n return {\n appConfig: this.appConfigFactory.create({\n repoRoot: args.repoRoot,\n consumerRoot: args.consumerRoot,\n env: args.env,\n config: resolution.config,\n workflowSources: resolution.workflowSources,\n }),\n bootstrapSource: resolution.bootstrapSource,\n };\n }\n}\n","import type { CodemationPackageManifest } from \"../config/CodemationPackageManifest\";\nimport { CodemationPluginPackageMetadata, type CodemationPlugin } from \"../config/CodemationPlugin\";\nimport { readFile, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nexport type CodemationDiscoveredPluginPackage = Readonly<{\n packageName: string;\n packageRoot: string;\n pluginEntry: string;\n developmentEntry?: string;\n}>;\n\nexport type CodemationResolvedPluginPackage = Readonly<\n CodemationDiscoveredPluginPackage & {\n plugin: CodemationPlugin;\n }\n>;\n\ntype PackageJsonShape = Readonly<{\n codemation?: CodemationPackageManifest;\n name?: string;\n exports?: Readonly<Record<string, unknown>>;\n}>;\n\nexport class CodemationPluginDiscovery {\n private readonly pluginPackageMetadata = new CodemationPluginPackageMetadata();\n\n async discover(consumerRoot: string): Promise<ReadonlyArray<CodemationDiscoveredPluginPackage>> {\n const nodeModulesRoot = path.resolve(consumerRoot, \"node_modules\");\n const packageRoots = await this.collectPackageRoots(nodeModulesRoot);\n const discoveredPackages: CodemationDiscoveredPluginPackage[] = [];\n for (const packageRoot of packageRoots) {\n const packageJson = await this.readPackageJson(path.resolve(packageRoot, \"package.json\"));\n const pluginManifest = packageJson.codemation?.plugin;\n if (!packageJson.name || typeof pluginManifest !== \"string\" || pluginManifest.trim().length === 0) {\n continue;\n }\n discoveredPackages.push({\n packageName: packageJson.name,\n packageRoot,\n pluginEntry: pluginManifest,\n developmentEntry: await this.resolveDevelopmentPluginEntry(packageRoot),\n });\n }\n return discoveredPackages.sort((left, right) => left.packageName.localeCompare(right.packageName));\n }\n\n async resolvePlugins(consumerRoot: string): Promise<ReadonlyArray<CodemationResolvedPluginPackage>> {\n const discoveredPackages = await this.discover(consumerRoot);\n return await this.resolveDiscoveredPackages(discoveredPackages);\n }\n\n async resolveDiscoveredPackages(\n discoveredPackages: ReadonlyArray<CodemationDiscoveredPluginPackage>,\n ): Promise<ReadonlyArray<CodemationResolvedPluginPackage>> {\n const resolvedPackages: CodemationResolvedPluginPackage[] = [];\n for (const discoveredPackage of discoveredPackages) {\n resolvedPackages.push({\n ...discoveredPackage,\n plugin: await this.loadPlugin(discoveredPackage),\n });\n }\n return resolvedPackages;\n }\n\n private async collectPackageRoots(nodeModulesRoot: string): Promise<ReadonlyArray<string>> {\n try {\n const entries = await readdir(nodeModulesRoot, { withFileTypes: true });\n const packageRoots: string[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) {\n continue;\n }\n if (entry.name.startsWith(\"@\")) {\n const scopedEntries = await readdir(path.resolve(nodeModulesRoot, entry.name), { withFileTypes: true });\n for (const scopedEntry of scopedEntries) {\n if (scopedEntry.isDirectory() || scopedEntry.isSymbolicLink()) {\n packageRoots.push(path.resolve(nodeModulesRoot, entry.name, scopedEntry.name));\n }\n }\n continue;\n }\n packageRoots.push(path.resolve(nodeModulesRoot, entry.name));\n }\n return packageRoots;\n } catch {\n return [];\n }\n }\n\n private async readPackageJson(packageJsonPath: string): Promise<PackageJsonShape> {\n try {\n const rawPackageJson = await readFile(packageJsonPath, \"utf8\");\n return JSON.parse(rawPackageJson) as PackageJsonShape;\n } catch {\n return {};\n }\n }\n\n private async loadPlugin(discoveredPackage: CodemationDiscoveredPluginPackage): Promise<CodemationPlugin> {\n const pluginModulePath = path.resolve(discoveredPackage.packageRoot, this.resolvePluginEntry(discoveredPackage));\n const importedModule = (await import(\n /* webpackIgnore: true */ this.resolvePluginModuleSpecifier(pluginModulePath)\n )) as Record<string, unknown>;\n const exportedValue = importedModule.default;\n const plugin = this.resolvePluginValue(exportedValue);\n if (!plugin) {\n throw new Error(`Plugin package \"${discoveredPackage.packageName}\" did not default-export a Codemation plugin.`);\n }\n return this.pluginPackageMetadata.attachPackageName(plugin, discoveredPackage.packageName);\n }\n\n private resolvePluginValue(value: unknown): CodemationPlugin | null {\n if (this.isPluginConfig(value)) {\n return value;\n }\n return null;\n }\n\n private isPluginConfig(value: unknown): value is CodemationPlugin {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n const pluginValue = value as {\n credentialTypes?: unknown;\n register?: unknown;\n sandbox?: unknown;\n };\n if (pluginValue.register !== undefined && typeof pluginValue.register !== \"function\") {\n return false;\n }\n if (pluginValue.credentialTypes !== undefined && !Array.isArray(pluginValue.credentialTypes)) {\n return false;\n }\n return (\n pluginValue.register !== undefined ||\n pluginValue.credentialTypes !== undefined ||\n pluginValue.sandbox !== undefined ||\n Object.keys(pluginValue).length === 0\n );\n }\n\n private resolvePluginEntry(discoveredPackage: CodemationDiscoveredPluginPackage): string {\n if (\n process.env.CODEMATION_PREFER_PLUGIN_SOURCE_ENTRY === \"true\" &&\n typeof discoveredPackage.developmentEntry === \"string\" &&\n discoveredPackage.developmentEntry.trim().length > 0\n ) {\n return discoveredPackage.developmentEntry;\n }\n return discoveredPackage.pluginEntry;\n }\n\n private async resolveDevelopmentPluginEntry(packageRoot: string): Promise<string | undefined> {\n const candidates = [\n path.resolve(packageRoot, \"codemation.plugin.ts\"),\n path.resolve(packageRoot, \"codemation.plugin.js\"),\n path.resolve(packageRoot, \"src\", \"codemation.plugin.ts\"),\n path.resolve(packageRoot, \"src\", \"codemation.plugin.js\"),\n ];\n for (const candidate of candidates) {\n if (await this.exists(candidate)) {\n return path.relative(packageRoot, candidate);\n }\n }\n return undefined;\n }\n\n private async exists(filePath: string): Promise<boolean> {\n try {\n await readFile(filePath, \"utf8\");\n return true;\n } catch {\n return false;\n }\n }\n\n private resolvePluginModuleSpecifier(pluginModulePath: string): string {\n return pathToFileURL(pluginModulePath).href;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server-C-WZcsId.js","names":["config: CodemationConfig","consumerRoot: string","configSource?: string","workflowSources: ReadonlyArray<string>","env?: Readonly<NodeJS.ProcessEnv>","consumerConfigLoader: CodemationConsumerConfigLoader","appConfigFactory: AppConfigFactory","discoveredPackages: CodemationDiscoveredPluginPackage[]","resolvedPackages: CodemationResolvedPluginPackage[]","packageRoots: string[]"],"sources":["../src/presentation/http/CodemationServerGatewayFactory.ts","../src/presentation/server/AppConfigLoader.ts","../src/presentation/server/CodemationPluginDiscovery.ts"],"sourcesContent":["import { accessSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { QueryBus } from \"../../application/bus/QueryBus\";\nimport type { WorkflowDto, WorkflowSummary } from \"../../application/contracts/WorkflowViewContracts\";\nimport { WorkflowDefinitionMapper } from \"../../application/mapping/WorkflowDefinitionMapper\";\nimport { GetWorkflowDetailQuery } from \"../../application/queries/GetWorkflowDetailQuery\";\nimport { GetWorkflowSummariesQuery } from \"../../application/queries/GetWorkflowSummariesQuery\";\nimport { ApplicationTokens } from \"../../applicationTokens\";\nimport { AppContainerFactory } from \"../../bootstrap/AppContainerFactory\";\nimport { AppContainerLifecycle } from \"../../bootstrap/AppContainerLifecycle\";\nimport { FrontendRuntime } from \"../../bootstrap/runtime/FrontendRuntime\";\nimport { AppConfigFactory } from \"../../bootstrap/runtime/AppConfigFactory\";\nimport type { CodemationConfig } from \"../config/CodemationConfig\";\nimport { CodemationConfigNormalizer } from \"../config/CodemationConfigNormalizer\";\nimport { CodemationHonoApiApp } from \"./hono/CodemationHonoApiAppFactory\";\n\ntype ServerGatewayContext = Readonly<{\n container: import(\"@codemation/core\").Container;\n httpApi: CodemationHonoApiApp;\n queryBus: QueryBus;\n workflowDefinitionMapper: WorkflowDefinitionMapper;\n}>;\n\nexport class CodemationServerGateway {\n private static readonly contextsByConfig = new WeakMap<object, Promise<ServerGatewayContext>>();\n\n constructor(\n private readonly config: CodemationConfig,\n private readonly consumerRoot: string,\n private readonly configSource?: string,\n private readonly workflowSources: ReadonlyArray<string> = [],\n private readonly env?: Readonly<NodeJS.ProcessEnv>,\n ) {}\n\n async dispatch(request: Request): Promise<Response> {\n return await (await this.getContext()).httpApi.fetch(request);\n }\n\n async prepare(): Promise<void> {\n await this.getContext();\n }\n\n async close(): Promise<void> {\n const cachedContext = CodemationServerGateway.contextsByConfig.get(this.config as object);\n if (!cachedContext) {\n return;\n }\n CodemationServerGateway.contextsByConfig.delete(this.config as object);\n await (await cachedContext).container.resolve(AppContainerLifecycle).stop();\n }\n\n async loadWorkflowSummaries(): Promise<ReadonlyArray<WorkflowSummary>> {\n const context = await this.getContext();\n const workflows = await context.queryBus.execute(new GetWorkflowSummariesQuery());\n return workflows.map((workflow) => context.workflowDefinitionMapper.toSummary(workflow));\n }\n\n async loadWorkflowDetail(workflowId: string): Promise<WorkflowDto> {\n const context = await this.getContext();\n const workflow = await context.queryBus.execute(new GetWorkflowDetailQuery(workflowId));\n if (!workflow) {\n throw new Error(`Unknown workflowId: ${workflowId}`);\n }\n return await context.workflowDefinitionMapper.map(workflow);\n }\n\n private getContext(): Promise<ServerGatewayContext> {\n const cachedContext = CodemationServerGateway.contextsByConfig.get(this.config as object);\n if (cachedContext) {\n return cachedContext;\n }\n const nextContext = this.createContext();\n CodemationServerGateway.contextsByConfig.set(this.config as object, nextContext);\n return nextContext;\n }\n\n private async createContext(): Promise<ServerGatewayContext> {\n const repoRoot = this.detectWorkspaceRoot(this.consumerRoot);\n // This gateway is the config/env boundary that materializes AppConfig from raw inputs.\n // eslint-disable-next-line no-restricted-properties\n const env = this.env ?? process.env;\n const appConfig = new AppConfigFactory().create({\n repoRoot,\n consumerRoot: this.consumerRoot,\n env,\n config: new CodemationConfigNormalizer().normalize(this.config),\n workflowSources: this.resolveWorkflowSources(),\n });\n const container = await new AppContainerFactory().create({\n appConfig,\n sharedWorkflowWebsocketServer: null,\n });\n await container.resolve(FrontendRuntime).start();\n return {\n container,\n httpApi: container.resolve(CodemationHonoApiApp),\n queryBus: container.resolve(ApplicationTokens.QueryBus),\n workflowDefinitionMapper: container.resolve(WorkflowDefinitionMapper),\n };\n }\n\n private resolveWorkflowSources(): ReadonlyArray<string> {\n if (this.workflowSources.length > 0) {\n return [...this.workflowSources];\n }\n if (!this.configSource || !this.config.workflows || this.config.workflows.length === 0) {\n return [];\n }\n return [this.configSource];\n }\n private detectWorkspaceRoot(startDirectory: string): string {\n let currentDirectory = path.resolve(startDirectory);\n while (true) {\n try {\n accessSync(path.resolve(currentDirectory, \"pnpm-workspace.yaml\"));\n return currentDirectory;\n } catch {\n const parentDirectory = path.dirname(currentDirectory);\n if (parentDirectory === currentDirectory) {\n return startDirectory;\n }\n currentDirectory = parentDirectory;\n }\n }\n }\n}\n","import type { AppConfig } from \"../config/AppConfig\";\nimport { CodemationConsumerConfigLoader } from \"./CodemationConsumerConfigLoader\";\nimport { AppConfigFactory } from \"../../bootstrap/runtime/AppConfigFactory\";\n\nexport type AppConfigLoadResult = Readonly<{\n appConfig: AppConfig;\n bootstrapSource: string | null;\n}>;\n\nexport class AppConfigLoader {\n constructor(\n private readonly consumerConfigLoader: CodemationConsumerConfigLoader = new CodemationConsumerConfigLoader(),\n private readonly appConfigFactory: AppConfigFactory = new AppConfigFactory(),\n ) {}\n\n async load(\n args: Readonly<{\n consumerRoot: string;\n repoRoot: string;\n env: NodeJS.ProcessEnv;\n configPathOverride?: string;\n }>,\n ): Promise<AppConfigLoadResult> {\n const resolution = await this.consumerConfigLoader.load({\n consumerRoot: args.consumerRoot,\n configPathOverride: args.configPathOverride,\n });\n return {\n appConfig: this.appConfigFactory.create({\n repoRoot: args.repoRoot,\n consumerRoot: args.consumerRoot,\n env: args.env,\n config: resolution.config,\n workflowSources: resolution.workflowSources,\n }),\n bootstrapSource: resolution.bootstrapSource,\n };\n }\n}\n","import type { CodemationPackageManifest } from \"../config/CodemationPackageManifest\";\nimport { CodemationPluginPackageMetadata, type CodemationPlugin } from \"../config/CodemationPlugin\";\nimport { readFile, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nexport type CodemationDiscoveredPluginPackage = Readonly<{\n packageName: string;\n packageRoot: string;\n pluginEntry: string;\n developmentEntry?: string;\n}>;\n\nexport type CodemationResolvedPluginPackage = Readonly<\n CodemationDiscoveredPluginPackage & {\n plugin: CodemationPlugin;\n }\n>;\n\ntype PackageJsonShape = Readonly<{\n codemation?: CodemationPackageManifest;\n name?: string;\n exports?: Readonly<Record<string, unknown>>;\n}>;\n\nexport class CodemationPluginDiscovery {\n private readonly pluginPackageMetadata = new CodemationPluginPackageMetadata();\n\n async discover(consumerRoot: string): Promise<ReadonlyArray<CodemationDiscoveredPluginPackage>> {\n const nodeModulesRoot = path.resolve(consumerRoot, \"node_modules\");\n const packageRoots = await this.collectPackageRoots(nodeModulesRoot);\n const discoveredPackages: CodemationDiscoveredPluginPackage[] = [];\n for (const packageRoot of packageRoots) {\n const packageJson = await this.readPackageJson(path.resolve(packageRoot, \"package.json\"));\n const pluginManifest = packageJson.codemation?.plugin;\n if (!packageJson.name || typeof pluginManifest !== \"string\" || pluginManifest.trim().length === 0) {\n continue;\n }\n discoveredPackages.push({\n packageName: packageJson.name,\n packageRoot,\n pluginEntry: pluginManifest,\n developmentEntry: await this.resolveDevelopmentPluginEntry(packageRoot),\n });\n }\n return discoveredPackages.sort((left, right) => left.packageName.localeCompare(right.packageName));\n }\n\n async resolvePlugins(consumerRoot: string): Promise<ReadonlyArray<CodemationResolvedPluginPackage>> {\n const discoveredPackages = await this.discover(consumerRoot);\n return await this.resolveDiscoveredPackages(discoveredPackages);\n }\n\n async resolveDiscoveredPackages(\n discoveredPackages: ReadonlyArray<CodemationDiscoveredPluginPackage>,\n ): Promise<ReadonlyArray<CodemationResolvedPluginPackage>> {\n const resolvedPackages: CodemationResolvedPluginPackage[] = [];\n for (const discoveredPackage of discoveredPackages) {\n resolvedPackages.push({\n ...discoveredPackage,\n plugin: await this.loadPlugin(discoveredPackage),\n });\n }\n return resolvedPackages;\n }\n\n private async collectPackageRoots(nodeModulesRoot: string): Promise<ReadonlyArray<string>> {\n try {\n const entries = await readdir(nodeModulesRoot, { withFileTypes: true });\n const packageRoots: string[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) {\n continue;\n }\n if (entry.name.startsWith(\"@\")) {\n const scopedEntries = await readdir(path.resolve(nodeModulesRoot, entry.name), { withFileTypes: true });\n for (const scopedEntry of scopedEntries) {\n if (scopedEntry.isDirectory() || scopedEntry.isSymbolicLink()) {\n packageRoots.push(path.resolve(nodeModulesRoot, entry.name, scopedEntry.name));\n }\n }\n continue;\n }\n packageRoots.push(path.resolve(nodeModulesRoot, entry.name));\n }\n return packageRoots;\n } catch {\n return [];\n }\n }\n\n private async readPackageJson(packageJsonPath: string): Promise<PackageJsonShape> {\n try {\n const rawPackageJson = await readFile(packageJsonPath, \"utf8\");\n return JSON.parse(rawPackageJson) as PackageJsonShape;\n } catch {\n return {};\n }\n }\n\n private async loadPlugin(discoveredPackage: CodemationDiscoveredPluginPackage): Promise<CodemationPlugin> {\n const pluginModulePath = path.resolve(discoveredPackage.packageRoot, this.resolvePluginEntry(discoveredPackage));\n const importedModule = (await import(\n /* webpackIgnore: true */ this.resolvePluginModuleSpecifier(pluginModulePath)\n )) as Record<string, unknown>;\n const exportedValue = importedModule.default;\n const plugin = this.resolvePluginValue(exportedValue);\n if (!plugin) {\n throw new Error(`Plugin package \"${discoveredPackage.packageName}\" did not default-export a Codemation plugin.`);\n }\n return this.pluginPackageMetadata.attachPackageName(plugin, discoveredPackage.packageName);\n }\n\n private resolvePluginValue(value: unknown): CodemationPlugin | null {\n if (this.isPluginConfig(value)) {\n return value;\n }\n return null;\n }\n\n private isPluginConfig(value: unknown): value is CodemationPlugin {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n const pluginValue = value as {\n credentialTypes?: unknown;\n register?: unknown;\n sandbox?: unknown;\n };\n if (pluginValue.register !== undefined && typeof pluginValue.register !== \"function\") {\n return false;\n }\n if (pluginValue.credentialTypes !== undefined && !Array.isArray(pluginValue.credentialTypes)) {\n return false;\n }\n return (\n pluginValue.register !== undefined ||\n pluginValue.credentialTypes !== undefined ||\n pluginValue.sandbox !== undefined ||\n Object.keys(pluginValue).length === 0\n );\n }\n\n private resolvePluginEntry(discoveredPackage: CodemationDiscoveredPluginPackage): string {\n const preferSource =\n process.env.CODEMATION_PREFER_PLUGIN_SOURCE_ENTRY === \"true\" &&\n typeof discoveredPackage.developmentEntry === \"string\" &&\n discoveredPackage.developmentEntry.trim().length > 0;\n const selectedEntry = preferSource ? discoveredPackage.developmentEntry : discoveredPackage.pluginEntry;\n return selectedEntry;\n }\n\n private async resolveDevelopmentPluginEntry(packageRoot: string): Promise<string | undefined> {\n const candidates = [\n path.resolve(packageRoot, \"codemation.plugin.ts\"),\n path.resolve(packageRoot, \"codemation.plugin.js\"),\n path.resolve(packageRoot, \"src\", \"codemation.plugin.ts\"),\n path.resolve(packageRoot, \"src\", \"codemation.plugin.js\"),\n ];\n for (const candidate of candidates) {\n if (await this.exists(candidate)) {\n return path.relative(packageRoot, candidate);\n }\n }\n return undefined;\n }\n\n private async exists(filePath: string): Promise<boolean> {\n try {\n await readFile(filePath, \"utf8\");\n return true;\n } catch {\n return false;\n }\n }\n\n private resolvePluginModuleSpecifier(pluginModulePath: string): string {\n return pathToFileURL(pluginModulePath).href;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as CodemationConfig, g as AppConfig, y as CodemationPlugin } from "./CodemationConfig-
|
|
2
|
-
import { t as CodemationConsumerConfigLoader } from "./CodemationConsumerConfigLoader-
|
|
3
|
-
import { t as AppConfigFactory } from "./AppConfigFactory-
|
|
4
|
-
import { a as WorkflowDto, o as WorkflowSummary } from "./PublicFrontendBootstrapFactory-
|
|
1
|
+
import { a as CodemationConfig, g as AppConfig, y as CodemationPlugin } from "./CodemationConfig-DBbMU3HB.js";
|
|
2
|
+
import { t as CodemationConsumerConfigLoader } from "./CodemationConsumerConfigLoader-Ceh6sB3X.js";
|
|
3
|
+
import { t as AppConfigFactory } from "./AppConfigFactory-DHdAGOmC.js";
|
|
4
|
+
import { a as WorkflowDto, o as WorkflowSummary } from "./PublicFrontendBootstrapFactory-DeMjp3cs.js";
|
|
5
5
|
|
|
6
6
|
//#region src/presentation/http/ApiPaths.d.ts
|
|
7
7
|
declare class ApiPaths {
|
|
@@ -150,4 +150,4 @@ declare class WorkflowDiscoveryPathSegmentsComputer {
|
|
|
150
150
|
}
|
|
151
151
|
//#endregion
|
|
152
152
|
export { CodemationResolvedPluginPackage as a, CodemationServerGateway as c, CodemationPluginDiscovery as i, ApiPaths as l, WorkflowModulePathFinder as n, AppConfigLoadResult as o, CodemationDiscoveredPluginPackage as r, AppConfigLoader as s, WorkflowDiscoveryPathSegmentsComputer as t };
|
|
153
|
-
//# sourceMappingURL=server-
|
|
153
|
+
//# sourceMappingURL=server-fxqY2WEi.d.ts.map
|
package/dist/server.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import "./CodemationAuthConfig-7hEfICPf.js";
|
|
2
2
|
import { n as InternalAuthBootstrap, r as FrontendAppConfig, t as PublicFrontendBootstrap } from "./PublicFrontendBootstrap-DCniMBGu.js";
|
|
3
3
|
import { i as CodemationFrontendAuthSnapshotJsonCodec, n as InternalAuthBootstrapJsonCodec, r as FrontendAppConfigJsonCodec, t as PublicFrontendBootstrapJsonCodec } from "./PublicFrontendBootstrapJsonCodec-BE0mhe1v.js";
|
|
4
|
-
import "./index-
|
|
5
|
-
import "./CodemationConfig-
|
|
6
|
-
import "./CodemationConfigNormalizer-
|
|
7
|
-
import { i as CodemationConsumerAppResolver, n as CodemationConsumerConfigResolution, r as CodemationConsumerApp, t as CodemationConsumerConfigLoader } from "./CodemationConsumerConfigLoader-
|
|
8
|
-
import { r as PrismaDatabaseClient } from "./AppConfigFactory-
|
|
9
|
-
import { t as CodemationPostgresPrismaClientFactory } from "./persistenceServer-
|
|
10
|
-
import { a as CodemationResolvedPluginPackage, c as CodemationServerGateway, i as CodemationPluginDiscovery, l as ApiPaths, n as WorkflowModulePathFinder, o as AppConfigLoadResult, r as CodemationDiscoveredPluginPackage, s as AppConfigLoader, t as WorkflowDiscoveryPathSegmentsComputer } from "./server-
|
|
11
|
-
import { i as CodemationFrontendAuthSnapshotFactory, n as InternalAuthBootstrapFactory, r as FrontendAppConfigFactory, t as PublicFrontendBootstrapFactory } from "./PublicFrontendBootstrapFactory-
|
|
4
|
+
import "./index-dK05sTQ4.js";
|
|
5
|
+
import "./CodemationConfig-DBbMU3HB.js";
|
|
6
|
+
import "./CodemationConfigNormalizer-C8wC0skq.js";
|
|
7
|
+
import { i as CodemationConsumerAppResolver, n as CodemationConsumerConfigResolution, r as CodemationConsumerApp, t as CodemationConsumerConfigLoader } from "./CodemationConsumerConfigLoader-Ceh6sB3X.js";
|
|
8
|
+
import { r as PrismaDatabaseClient } from "./AppConfigFactory-DHdAGOmC.js";
|
|
9
|
+
import { t as CodemationPostgresPrismaClientFactory } from "./persistenceServer-D9vUTMqc.js";
|
|
10
|
+
import { a as CodemationResolvedPluginPackage, c as CodemationServerGateway, i as CodemationPluginDiscovery, l as ApiPaths, n as WorkflowModulePathFinder, o as AppConfigLoadResult, r as CodemationDiscoveredPluginPackage, s as AppConfigLoader, t as WorkflowDiscoveryPathSegmentsComputer } from "./server-fxqY2WEi.js";
|
|
11
|
+
import { i as CodemationFrontendAuthSnapshotFactory, n as InternalAuthBootstrapFactory, r as FrontendAppConfigFactory, t as PublicFrontendBootstrapFactory } from "./PublicFrontendBootstrapFactory-DeMjp3cs.js";
|
|
12
12
|
export { ApiPaths, AppConfigLoadResult, AppConfigLoader, CodemationConsumerApp, CodemationConsumerAppResolver, CodemationConsumerConfigLoader, CodemationConsumerConfigResolution, CodemationDiscoveredPluginPackage, CodemationFrontendAuthSnapshotFactory, CodemationFrontendAuthSnapshotJsonCodec, CodemationPluginDiscovery, CodemationPostgresPrismaClientFactory, CodemationResolvedPluginPackage, CodemationServerGateway, FrontendAppConfig, FrontendAppConfigFactory, FrontendAppConfigJsonCodec, InternalAuthBootstrap, InternalAuthBootstrapFactory, InternalAuthBootstrapJsonCodec, PrismaDatabaseClient as PrismaClient, PublicFrontendBootstrap, PublicFrontendBootstrapFactory, PublicFrontendBootstrapJsonCodec, WorkflowDiscoveryPathSegmentsComputer, WorkflowModulePathFinder };
|
package/dist/server.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import "./ConsoleLogger-ClPU7jtc.js";
|
|
2
2
|
import { i as CodemationFrontendAuthSnapshotJsonCodec, n as InternalAuthBootstrapJsonCodec, r as FrontendAppConfigJsonCodec, t as PublicFrontendBootstrapJsonCodec } from "./PublicFrontendBootstrapJsonCodec-BdiVGG5R.js";
|
|
3
|
-
import { i as CodemationConsumerAppResolver, n as WorkflowDiscoveryPathSegmentsComputer, r as WorkflowModulePathFinder, t as CodemationConsumerConfigLoader } from "./CodemationConsumerConfigLoader-
|
|
3
|
+
import { i as CodemationConsumerAppResolver, n as WorkflowDiscoveryPathSegmentsComputer, r as WorkflowModulePathFinder, t as CodemationConsumerConfigLoader } from "./CodemationConsumerConfigLoader-D6LFSlp5.js";
|
|
4
4
|
import "./ServerLoggerFactory-BltIIDfQ.js";
|
|
5
5
|
import "./decorateParam-DrsXNPuw.js";
|
|
6
6
|
import "./decorate-B0PP651O.js";
|
|
7
|
-
import "./CredentialServices-
|
|
8
|
-
import { _ as CodemationFrontendAuthSnapshotFactory, g as FrontendAppConfigFactory, h as InternalAuthBootstrapFactory, m as PublicFrontendBootstrapFactory, y as ApiPaths } from "./AppContainerFactory-
|
|
7
|
+
import "./CredentialServices-D8BBZH1i.js";
|
|
8
|
+
import { _ as CodemationFrontendAuthSnapshotFactory, g as FrontendAppConfigFactory, h as InternalAuthBootstrapFactory, m as PublicFrontendBootstrapFactory, y as ApiPaths } from "./AppContainerFactory-BKaAUIi-.js";
|
|
9
9
|
import "./AppConfigFactory-ByT1D8dM.js";
|
|
10
10
|
import { t as CodemationPostgresPrismaClientFactory } from "./persistenceServer-DMvIOGW8.js";
|
|
11
|
-
import { n as AppConfigLoader, r as CodemationServerGateway, t as CodemationPluginDiscovery } from "./server-
|
|
11
|
+
import { n as AppConfigLoader, r as CodemationServerGateway, t as CodemationPluginDiscovery } from "./server-C-WZcsId.js";
|
|
12
12
|
|
|
13
13
|
export { ApiPaths, AppConfigLoader, CodemationConsumerAppResolver, CodemationConsumerConfigLoader, CodemationFrontendAuthSnapshotFactory, CodemationFrontendAuthSnapshotJsonCodec, CodemationPluginDiscovery, CodemationPostgresPrismaClientFactory, CodemationServerGateway, FrontendAppConfigFactory, FrontendAppConfigJsonCodec, InternalAuthBootstrapFactory, InternalAuthBootstrapJsonCodec, PublicFrontendBootstrapFactory, PublicFrontendBootstrapJsonCodec, WorkflowDiscoveryPathSegmentsComputer, WorkflowModulePathFinder };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemation/host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -112,9 +112,9 @@
|
|
|
112
112
|
"tsx": "^4.21.0",
|
|
113
113
|
"ws": "^8.19.0",
|
|
114
114
|
"zxcvbn": "^4.4.2",
|
|
115
|
-
"@codemation/core": "0.
|
|
116
|
-
"@codemation/core-nodes": "0.
|
|
117
|
-
"@codemation/eventbus-redis": "0.0.
|
|
115
|
+
"@codemation/core": "0.5.0",
|
|
116
|
+
"@codemation/core-nodes": "0.1.1",
|
|
117
|
+
"@codemation/eventbus-redis": "0.0.27"
|
|
118
118
|
},
|
|
119
119
|
"devDependencies": {
|
|
120
120
|
"@playwright/test": "^1.58.2",
|
|
@@ -139,9 +139,10 @@
|
|
|
139
139
|
"tsdown": "^0.15.5",
|
|
140
140
|
"typescript": "^5.9.3",
|
|
141
141
|
"vitest": "^4.0.18",
|
|
142
|
-
"@codemation/core-nodes-gmail": "0.0
|
|
142
|
+
"@codemation/core-nodes-gmail": "0.1.0"
|
|
143
143
|
},
|
|
144
144
|
"scripts": {
|
|
145
|
+
"changeset:verify": "pnpm --workspace-root run changeset:verify",
|
|
145
146
|
"prisma:generate": "node ./scripts/generate-prisma-clients.mjs && node ./scripts/ensure-prisma-runtime-sourcemaps.mjs",
|
|
146
147
|
"build": "tsdown src/index.ts src/client.ts src/credentials.ts src/consumer.ts src/nextServer.ts src/server.ts src/devServerSidecar.ts src/persistenceServer.ts --out-dir dist --sourcemap",
|
|
147
148
|
"dev": "tsdown src/index.ts src/client.ts src/credentials.ts src/consumer.ts src/nextServer.ts src/server.ts src/devServerSidecar.ts src/persistenceServer.ts --out-dir dist --sourcemap --watch",
|
package/playwright.config.ts
CHANGED
|
@@ -16,10 +16,14 @@ if (!fs.existsSync(preparedPath)) {
|
|
|
16
16
|
|
|
17
17
|
const prepared = JSON.parse(fs.readFileSync(preparedPath, "utf8")) as CodemationPlaywrightPreparedEnvironment;
|
|
18
18
|
|
|
19
|
-
const webServerEnv:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
const webServerEnv: Record<string, string> = Object.fromEntries(
|
|
20
|
+
Object.entries({
|
|
21
|
+
...process.env,
|
|
22
|
+
...prepared.serverEnv,
|
|
23
|
+
}).filter((entry): entry is [string, string] => typeof entry[1] === "string"),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const baseUrl = String(prepared.serverEnv.AUTH_URL ?? "http://localhost:3001");
|
|
23
27
|
|
|
24
28
|
export default defineConfig({
|
|
25
29
|
/**
|
|
@@ -49,7 +53,7 @@ export default defineConfig({
|
|
|
49
53
|
],
|
|
50
54
|
use: {
|
|
51
55
|
/** Align with app URL and AUTH_URL so Auth.js cookies are not split across localhost vs 127.0.0.1. */
|
|
52
|
-
baseURL:
|
|
56
|
+
baseURL: baseUrl,
|
|
53
57
|
trace: "retain-on-failure",
|
|
54
58
|
video: "retain-on-failure",
|
|
55
59
|
},
|
|
@@ -64,7 +68,7 @@ export default defineConfig({
|
|
|
64
68
|
command: "pnpm run e2e:serve-web",
|
|
65
69
|
cwd: repoRoot,
|
|
66
70
|
env: webServerEnv,
|
|
67
|
-
url:
|
|
71
|
+
url: baseUrl,
|
|
68
72
|
// Always start a fresh server so DATABASE_URL/AUTH_SECRET from `.e2e-prepared.json` match the DB that was provisioned for this run (reuse can leave a stale server on port 3001).
|
|
69
73
|
reuseExistingServer: false,
|
|
70
74
|
timeout: 180_000,
|
|
@@ -12,6 +12,9 @@ export type WorkflowNodeDto = Readonly<{
|
|
|
12
12
|
hasNodeErrorHandler?: boolean;
|
|
13
13
|
/** When true, downstream nodes may run even when this node outputs zero items. */
|
|
14
14
|
continueWhenEmptyOutput?: boolean;
|
|
15
|
+
/** Declared I/O ports from node config (unioned with ports inferred from edges on the canvas). */
|
|
16
|
+
declaredOutputPorts?: ReadonlyArray<string>;
|
|
17
|
+
declaredInputPorts?: ReadonlyArray<string>;
|
|
15
18
|
}>;
|
|
16
19
|
|
|
17
20
|
export type WorkflowEdgeDto = Readonly<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NodeDefinition, WorkflowActivationPolicy, WorkflowDefinition } from "@codemation/core";
|
|
2
2
|
import {
|
|
3
3
|
AgentConfigInspector,
|
|
4
4
|
AgentConnectionNodeCollector,
|
|
@@ -30,13 +30,14 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
mapSync(workflow: WorkflowDefinition): WorkflowDto {
|
|
33
|
+
const mapped = this.mapNodesAndEdges(workflow);
|
|
33
34
|
return {
|
|
34
35
|
id: workflow.id,
|
|
35
36
|
name: workflow.name,
|
|
36
37
|
active: this.workflowActivationPolicy.isActive(workflow.id),
|
|
37
38
|
hasWorkflowErrorHandler: this.policyUi.workflowHasErrorHandler(workflow),
|
|
38
|
-
nodes:
|
|
39
|
-
edges:
|
|
39
|
+
nodes: mapped.nodes,
|
|
40
|
+
edges: mapped.edges,
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -61,19 +62,54 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
61
62
|
return map;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
private
|
|
65
|
+
private mapNodesAndEdges(
|
|
66
|
+
workflow: WorkflowDefinition,
|
|
67
|
+
): Readonly<{ nodes: ReadonlyArray<WorkflowNodeDto>; edges: WorkflowDto["edges"] }> {
|
|
65
68
|
const connectionChildMeta = this.buildConnectionChildMeta(workflow);
|
|
66
69
|
const materializedConnectionNodeIds = new Set(connectionChildMeta.keys());
|
|
70
|
+
const nodesById = new Map(workflow.nodes.map((node) => [node.id, node] as const));
|
|
71
|
+
const agentConnectionDescriptors = this.buildAgentConnectionDescriptorIndex(workflow);
|
|
72
|
+
return {
|
|
73
|
+
nodes: this.toNodes({
|
|
74
|
+
workflow,
|
|
75
|
+
connectionChildMeta,
|
|
76
|
+
materializedConnectionNodeIds,
|
|
77
|
+
nodesById,
|
|
78
|
+
agentConnectionDescriptors,
|
|
79
|
+
}),
|
|
80
|
+
edges: this.toEdges({
|
|
81
|
+
workflow,
|
|
82
|
+
materializedConnectionNodeIds,
|
|
83
|
+
agentConnectionDescriptors,
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private toNodes(
|
|
89
|
+
args: Readonly<{
|
|
90
|
+
workflow: WorkflowDefinition;
|
|
91
|
+
connectionChildMeta: ReadonlyMap<string, Readonly<{ parentNodeId: string; connectionName: string }>>;
|
|
92
|
+
materializedConnectionNodeIds: ReadonlySet<string>;
|
|
93
|
+
nodesById: ReadonlyMap<string, WorkflowDefinition["nodes"][number]>;
|
|
94
|
+
agentConnectionDescriptors: Readonly<{
|
|
95
|
+
byAgentNodeId: ReadonlyMap<string, ReadonlyArray<AgentConnectionNodeDescriptor>>;
|
|
96
|
+
byChildNodeIdByAgentNodeId: ReadonlyMap<string, ReadonlyMap<string, AgentConnectionNodeDescriptor>>;
|
|
97
|
+
}>;
|
|
98
|
+
}>,
|
|
99
|
+
): ReadonlyArray<WorkflowNodeDto> {
|
|
100
|
+
const workflow = args.workflow;
|
|
101
|
+
const connectionChildMeta = args.connectionChildMeta;
|
|
102
|
+
const materializedConnectionNodeIds = args.materializedConnectionNodeIds;
|
|
103
|
+
const nodesById = args.nodesById;
|
|
104
|
+
const agentConnectionDescriptors = args.agentConnectionDescriptors;
|
|
67
105
|
const nodes: WorkflowNodeDto[] = [];
|
|
68
106
|
for (const node of workflow.nodes) {
|
|
69
107
|
const conn = connectionChildMeta.get(node.id);
|
|
70
108
|
if (conn) {
|
|
71
|
-
const parentNode =
|
|
109
|
+
const parentNode = nodesById.get(conn.parentNodeId);
|
|
72
110
|
let role: string = conn.connectionName === "llm" ? "languageModel" : "tool";
|
|
73
111
|
if (parentNode && AgentConfigInspector.isAgentNodeConfig(parentNode.config)) {
|
|
74
|
-
const descriptor =
|
|
75
|
-
(d) => d.nodeId === node.id,
|
|
76
|
-
);
|
|
112
|
+
const descriptor = agentConnectionDescriptors.byChildNodeIdByAgentNodeId.get(conn.parentNodeId)?.get(node.id);
|
|
77
113
|
if (descriptor) {
|
|
78
114
|
role = descriptor.role;
|
|
79
115
|
}
|
|
@@ -87,6 +123,7 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
87
123
|
icon: node.config?.icon,
|
|
88
124
|
retryPolicySummary: this.policyUi.nodeRetrySummary(node.config),
|
|
89
125
|
hasNodeErrorHandler: this.policyUi.nodeHasErrorHandler(node.config),
|
|
126
|
+
...this.nodePortFieldsFromConfig(node.config),
|
|
90
127
|
parentNodeId: conn.parentNodeId,
|
|
91
128
|
});
|
|
92
129
|
continue;
|
|
@@ -100,17 +137,32 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
100
137
|
icon: node.config?.icon,
|
|
101
138
|
retryPolicySummary: this.policyUi.nodeRetrySummary(node.config),
|
|
102
139
|
hasNodeErrorHandler: this.policyUi.nodeHasErrorHandler(node.config),
|
|
140
|
+
...this.nodePortFieldsFromConfig(node.config),
|
|
103
141
|
});
|
|
104
142
|
if (AgentConfigInspector.isAgentNodeConfig(node.config)) {
|
|
105
|
-
this.appendVirtualConnectionNodes(
|
|
143
|
+
this.appendVirtualConnectionNodes(
|
|
144
|
+
materializedConnectionNodeIds,
|
|
145
|
+
nodes,
|
|
146
|
+
agentConnectionDescriptors.byAgentNodeId.get(node.id) ?? [],
|
|
147
|
+
);
|
|
106
148
|
}
|
|
107
149
|
}
|
|
108
150
|
return nodes;
|
|
109
151
|
}
|
|
110
152
|
|
|
111
|
-
private toEdges(
|
|
112
|
-
|
|
113
|
-
|
|
153
|
+
private toEdges(
|
|
154
|
+
args: Readonly<{
|
|
155
|
+
workflow: WorkflowDefinition;
|
|
156
|
+
materializedConnectionNodeIds: ReadonlySet<string>;
|
|
157
|
+
agentConnectionDescriptors: Readonly<{
|
|
158
|
+
byAgentNodeId: ReadonlyMap<string, ReadonlyArray<AgentConnectionNodeDescriptor>>;
|
|
159
|
+
byChildNodeIdByAgentNodeId: ReadonlyMap<string, ReadonlyMap<string, AgentConnectionNodeDescriptor>>;
|
|
160
|
+
}>;
|
|
161
|
+
}>,
|
|
162
|
+
): WorkflowDto["edges"] {
|
|
163
|
+
const workflow = args.workflow;
|
|
164
|
+
const materializedConnectionNodeIds = args.materializedConnectionNodeIds;
|
|
165
|
+
const agentConnectionDescriptors = args.agentConnectionDescriptors;
|
|
114
166
|
const edges: WorkflowEdgeDto[] = [...workflow.edges];
|
|
115
167
|
const edgeKeys = new Set(edges.map((edge) => this.edgeKey(edge.from.nodeId, edge.to.nodeId, edge.to.input)));
|
|
116
168
|
this.appendMaterializedConnectionEdges(workflow, edgeKeys, edges);
|
|
@@ -118,7 +170,12 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
118
170
|
if (!AgentConfigInspector.isAgentNodeConfig(node.config)) {
|
|
119
171
|
continue;
|
|
120
172
|
}
|
|
121
|
-
this.appendVirtualConnectionEdges(
|
|
173
|
+
this.appendVirtualConnectionEdges(
|
|
174
|
+
materializedConnectionNodeIds,
|
|
175
|
+
edgeKeys,
|
|
176
|
+
edges,
|
|
177
|
+
agentConnectionDescriptors.byAgentNodeId.get(node.id) ?? [],
|
|
178
|
+
);
|
|
122
179
|
}
|
|
123
180
|
return edges;
|
|
124
181
|
}
|
|
@@ -143,13 +200,33 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
143
200
|
}
|
|
144
201
|
}
|
|
145
202
|
|
|
203
|
+
private buildAgentConnectionDescriptorIndex(workflow: WorkflowDefinition): Readonly<{
|
|
204
|
+
byAgentNodeId: ReadonlyMap<string, ReadonlyArray<AgentConnectionNodeDescriptor>>;
|
|
205
|
+
byChildNodeIdByAgentNodeId: ReadonlyMap<string, ReadonlyMap<string, AgentConnectionNodeDescriptor>>;
|
|
206
|
+
}> {
|
|
207
|
+
const byAgentNodeId = new Map<string, ReadonlyArray<AgentConnectionNodeDescriptor>>();
|
|
208
|
+
const byChildNodeIdByAgentNodeId = new Map<string, ReadonlyMap<string, AgentConnectionNodeDescriptor>>();
|
|
209
|
+
for (const node of workflow.nodes) {
|
|
210
|
+
if (!AgentConfigInspector.isAgentNodeConfig(node.config)) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const descriptors = AgentConnectionNodeCollector.collect(node.id, node.config);
|
|
214
|
+
byAgentNodeId.set(node.id, descriptors);
|
|
215
|
+
const byChildId = new Map<string, AgentConnectionNodeDescriptor>();
|
|
216
|
+
for (const descriptor of descriptors) {
|
|
217
|
+
byChildId.set(descriptor.nodeId, descriptor);
|
|
218
|
+
}
|
|
219
|
+
byChildNodeIdByAgentNodeId.set(node.id, byChildId);
|
|
220
|
+
}
|
|
221
|
+
return { byAgentNodeId, byChildNodeIdByAgentNodeId };
|
|
222
|
+
}
|
|
223
|
+
|
|
146
224
|
private appendVirtualConnectionNodes(
|
|
147
|
-
rootAgentNodeId: string,
|
|
148
|
-
agentConfig: AgentNodeConfig<any, any>,
|
|
149
225
|
materializedConnectionNodeIds: ReadonlySet<string>,
|
|
150
226
|
nodes: WorkflowNodeDto[],
|
|
227
|
+
descriptors: ReadonlyArray<AgentConnectionNodeDescriptor>,
|
|
151
228
|
): void {
|
|
152
|
-
for (const connectionNode of
|
|
229
|
+
for (const connectionNode of descriptors) {
|
|
153
230
|
if (materializedConnectionNodeIds.has(connectionNode.nodeId)) {
|
|
154
231
|
continue;
|
|
155
232
|
}
|
|
@@ -158,13 +235,12 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
158
235
|
}
|
|
159
236
|
|
|
160
237
|
private appendVirtualConnectionEdges(
|
|
161
|
-
rootAgentNodeId: string,
|
|
162
|
-
agentConfig: AgentNodeConfig<any, any>,
|
|
163
238
|
materializedConnectionNodeIds: ReadonlySet<string>,
|
|
164
239
|
edgeKeys: Set<string>,
|
|
165
240
|
edges: WorkflowEdgeDto[],
|
|
241
|
+
descriptors: ReadonlyArray<AgentConnectionNodeDescriptor>,
|
|
166
242
|
): void {
|
|
167
|
-
for (const connectionNode of
|
|
243
|
+
for (const connectionNode of descriptors) {
|
|
168
244
|
if (materializedConnectionNodeIds.has(connectionNode.nodeId)) {
|
|
169
245
|
continue;
|
|
170
246
|
}
|
|
@@ -196,6 +272,28 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
196
272
|
};
|
|
197
273
|
}
|
|
198
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Omit optional port fields when undefined so persisted snapshot DTOs (which never serialize
|
|
277
|
+
* undefined keys) stay aligned with live workflow mapping.
|
|
278
|
+
*/
|
|
279
|
+
private nodePortFieldsFromConfig(
|
|
280
|
+
config: NodeDefinition["config"] | undefined,
|
|
281
|
+
): Pick<WorkflowNodeDto, "continueWhenEmptyOutput" | "declaredOutputPorts" | "declaredInputPorts"> {
|
|
282
|
+
if (!config || typeof config !== "object") {
|
|
283
|
+
return {};
|
|
284
|
+
}
|
|
285
|
+
const c = config as {
|
|
286
|
+
continueWhenEmptyOutput?: boolean;
|
|
287
|
+
declaredOutputPorts?: readonly string[];
|
|
288
|
+
declaredInputPorts?: readonly string[];
|
|
289
|
+
};
|
|
290
|
+
return {
|
|
291
|
+
...(c.continueWhenEmptyOutput !== undefined && { continueWhenEmptyOutput: c.continueWhenEmptyOutput }),
|
|
292
|
+
...(c.declaredOutputPorts !== undefined && { declaredOutputPorts: c.declaredOutputPorts }),
|
|
293
|
+
...(c.declaredInputPorts !== undefined && { declaredInputPorts: c.declaredInputPorts }),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
199
297
|
private nodeTypeName(node: NodeDefinition): string {
|
|
200
298
|
const configToken = node.config?.type as Readonly<{ name?: unknown }> | undefined;
|
|
201
299
|
if (typeof configToken?.name === "string" && configToken.name) {
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
GetRunStateQueryHandler,
|
|
64
64
|
GetWorkflowDebuggerOverlayQueryHandler,
|
|
65
65
|
GetWorkflowDetailQueryHandler,
|
|
66
|
+
GetWorkflowRunDetailQueryHandler,
|
|
66
67
|
GetWorkflowOverlayBinaryAttachmentQueryHandler,
|
|
67
68
|
GetWorkflowSummariesQueryHandler,
|
|
68
69
|
ListWorkflowRunsQueryHandler,
|
|
@@ -194,6 +195,7 @@ export class AppContainerFactory {
|
|
|
194
195
|
VerifyUserInviteQueryHandler,
|
|
195
196
|
GetRunBinaryAttachmentQueryHandler,
|
|
196
197
|
GetRunStateQueryHandler,
|
|
198
|
+
GetWorkflowRunDetailQueryHandler,
|
|
197
199
|
GetWorkflowDebuggerOverlayQueryHandler,
|
|
198
200
|
GetWorkflowDetailQueryHandler,
|
|
199
201
|
GetWorkflowOverlayBinaryAttachmentQueryHandler,
|
|
@@ -23,6 +23,7 @@ import { ApplicationTokens } from "../../applicationTokens";
|
|
|
23
23
|
|
|
24
24
|
import { CredentialFieldEnvOverlayService } from "./CredentialFieldEnvOverlayService";
|
|
25
25
|
import { CredentialMaterialResolver } from "./CredentialMaterialResolver";
|
|
26
|
+
import { CredentialOAuth2ScopeResolver } from "./CredentialOAuth2ScopeResolver";
|
|
26
27
|
import { CredentialSecretCipher } from "./CredentialSecretCipher";
|
|
27
28
|
import type {
|
|
28
29
|
CredentialInstanceRecord,
|
|
@@ -49,6 +50,8 @@ export class CredentialInstanceService {
|
|
|
49
50
|
private readonly credentialFieldEnvOverlayService: CredentialFieldEnvOverlayService,
|
|
50
51
|
@inject(CredentialMaterialResolver)
|
|
51
52
|
private readonly credentialMaterialResolver: CredentialMaterialResolver,
|
|
53
|
+
@inject(CredentialOAuth2ScopeResolver)
|
|
54
|
+
private readonly credentialOAuth2ScopeResolver: CredentialOAuth2ScopeResolver,
|
|
52
55
|
@inject(CoreTokens.CredentialSessionService)
|
|
53
56
|
private readonly credentialSessionService: MutableCredentialSessionService,
|
|
54
57
|
) {}
|
|
@@ -373,10 +376,14 @@ export class CredentialInstanceService {
|
|
|
373
376
|
"providerId" in credentialType.definition.auth ? credentialType.definition.auth.providerId : "custom";
|
|
374
377
|
const material = await this.credentialStore.getOAuth2Material(instance.instanceId);
|
|
375
378
|
if (!material) {
|
|
379
|
+
const requestedScopes = this.credentialOAuth2ScopeResolver.resolveRequestedScopes(
|
|
380
|
+
credentialType.definition.auth,
|
|
381
|
+
instance.publicConfig,
|
|
382
|
+
);
|
|
376
383
|
return {
|
|
377
384
|
status: "disconnected",
|
|
378
385
|
providerId,
|
|
379
|
-
scopes: [...
|
|
386
|
+
scopes: [...requestedScopes],
|
|
380
387
|
};
|
|
381
388
|
}
|
|
382
389
|
return {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { CredentialOAuth2AuthDefinition } from "@codemation/core";
|
|
2
|
+
import { injectable } from "@codemation/core";
|
|
3
|
+
import type { JsonRecord } from "./CredentialServices";
|
|
4
|
+
|
|
5
|
+
@injectable()
|
|
6
|
+
export class CredentialOAuth2ScopeResolver {
|
|
7
|
+
resolveRequestedScopes(auth: CredentialOAuth2AuthDefinition, publicConfig: JsonRecord): ReadonlyArray<string> {
|
|
8
|
+
const scopesFromPublicConfig = auth.scopesFromPublicConfig;
|
|
9
|
+
if (!scopesFromPublicConfig) {
|
|
10
|
+
return [...auth.scopes];
|
|
11
|
+
}
|
|
12
|
+
const preset = this.resolveString(publicConfig[scopesFromPublicConfig.presetFieldKey]);
|
|
13
|
+
if (!preset) {
|
|
14
|
+
return [...auth.scopes];
|
|
15
|
+
}
|
|
16
|
+
const presetScopes = scopesFromPublicConfig.presetScopes[preset];
|
|
17
|
+
if (presetScopes) {
|
|
18
|
+
return [...presetScopes];
|
|
19
|
+
}
|
|
20
|
+
const customPresetKey = scopesFromPublicConfig.customPresetKey ?? "custom";
|
|
21
|
+
if (preset !== customPresetKey) {
|
|
22
|
+
return [...auth.scopes];
|
|
23
|
+
}
|
|
24
|
+
const customScopes = this.resolveScopeList(
|
|
25
|
+
publicConfig[scopesFromPublicConfig.customScopesFieldKey ?? "customScopes"],
|
|
26
|
+
);
|
|
27
|
+
if (customScopes.length > 0) {
|
|
28
|
+
return customScopes;
|
|
29
|
+
}
|
|
30
|
+
return [...auth.scopes];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private resolveString(value: unknown): string | undefined {
|
|
34
|
+
if (typeof value !== "string") {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const normalized = value.trim();
|
|
38
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private resolveScopeList(value: unknown): ReadonlyArray<string> {
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
return this.dedupe(
|
|
44
|
+
value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter((entry) => entry.length > 0),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
if (typeof value !== "string") {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
return this.dedupe(
|
|
51
|
+
value
|
|
52
|
+
.split(/[\s,]+/)
|
|
53
|
+
.map((entry) => entry.trim())
|
|
54
|
+
.filter((entry) => entry.length > 0),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private dedupe(entries: ReadonlyArray<string>): ReadonlyArray<string> {
|
|
59
|
+
return [...new Set(entries)];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -138,6 +138,7 @@ export { CredentialTypeRegistryImpl } from "./CredentialTypeRegistryImpl";
|
|
|
138
138
|
export { CredentialBindingService } from "./CredentialBindingService";
|
|
139
139
|
export { CredentialInstanceService } from "./CredentialInstanceService";
|
|
140
140
|
export { CredentialMaterialResolver } from "./CredentialMaterialResolver";
|
|
141
|
+
export { CredentialOAuth2ScopeResolver } from "./CredentialOAuth2ScopeResolver";
|
|
141
142
|
export { CredentialRuntimeMaterialService } from "./CredentialRuntimeMaterialService";
|
|
142
143
|
export { CredentialFieldEnvOverlayService } from "./CredentialFieldEnvOverlayService";
|
|
143
144
|
export { CredentialSecretCipher } from "./CredentialSecretCipher";
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
CredentialFieldEnvOverlayService,
|
|
10
10
|
CredentialInstanceService,
|
|
11
11
|
CredentialMaterialResolver,
|
|
12
|
+
CredentialOAuth2ScopeResolver,
|
|
12
13
|
CredentialRuntimeMaterialService,
|
|
13
14
|
CredentialSecretCipher,
|
|
14
15
|
CredentialTypeRegistryImpl,
|
|
@@ -47,6 +48,8 @@ export class OAuth2ConnectService {
|
|
|
47
48
|
private readonly credentialMaterialResolver: CredentialMaterialResolver,
|
|
48
49
|
@inject(CredentialSecretCipher)
|
|
49
50
|
private readonly credentialSecretCipher: CredentialSecretCipher,
|
|
51
|
+
@inject(CredentialOAuth2ScopeResolver)
|
|
52
|
+
private readonly credentialOAuth2ScopeResolver: CredentialOAuth2ScopeResolver,
|
|
50
53
|
@inject(OAuth2ProviderRegistry)
|
|
51
54
|
private readonly oauth2ProviderRegistry: OAuth2ProviderRegistry,
|
|
52
55
|
@inject(ApplicationTokens.AppConfig)
|
|
@@ -63,6 +66,10 @@ export class OAuth2ConnectService {
|
|
|
63
66
|
material: emptyMaterial,
|
|
64
67
|
});
|
|
65
68
|
const provider = this.oauth2ProviderRegistry.resolve(credentialType.definition, resolvedPublicConfig);
|
|
69
|
+
const requestedScopes = this.credentialOAuth2ScopeResolver.resolveRequestedScopes(
|
|
70
|
+
credentialType.definition.auth!,
|
|
71
|
+
resolvedPublicConfig,
|
|
72
|
+
);
|
|
66
73
|
const redirectUri = this.getRedirectUri(requestOrigin);
|
|
67
74
|
const state = this.createOpaqueValue();
|
|
68
75
|
const codeVerifier = this.createOpaqueValue();
|
|
@@ -74,7 +81,7 @@ export class OAuth2ConnectService {
|
|
|
74
81
|
instanceId,
|
|
75
82
|
codeVerifier,
|
|
76
83
|
providerId: provider.providerId,
|
|
77
|
-
requestedScopes
|
|
84
|
+
requestedScopes,
|
|
78
85
|
createdAt: createdAt.toISOString(),
|
|
79
86
|
expiresAt: expiresAt.toISOString(),
|
|
80
87
|
});
|
|
@@ -85,7 +92,7 @@ export class OAuth2ConnectService {
|
|
|
85
92
|
this.oauth2ProviderRegistry.resolveClientId(credentialType.definition.auth!, resolvedPublicConfig),
|
|
86
93
|
);
|
|
87
94
|
authorizeUrl.searchParams.set("redirect_uri", redirectUri);
|
|
88
|
-
authorizeUrl.searchParams.set("scope",
|
|
95
|
+
authorizeUrl.searchParams.set("scope", requestedScopes.join(" "));
|
|
89
96
|
authorizeUrl.searchParams.set("state", state);
|
|
90
97
|
authorizeUrl.searchParams.set("code_challenge", codeChallenge);
|
|
91
98
|
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
@@ -121,7 +121,8 @@ export class CredentialHttpRouteHandler {
|
|
|
121
121
|
|
|
122
122
|
async getWorkflowCredentialHealth(_: Request, params: ServerHttpRouteParams): Promise<Response> {
|
|
123
123
|
try {
|
|
124
|
-
|
|
124
|
+
const health = await this.queryBus.execute(new GetWorkflowCredentialHealthQuery(params.workflowId!));
|
|
125
|
+
return Response.json(health);
|
|
125
126
|
} catch (error) {
|
|
126
127
|
return ServerHttpErrorResponseFactory.fromUnknown(error);
|
|
127
128
|
}
|