Files
langship.sh/web/lib/node-catalog.ts
T
patel-lyzr 0284ff768a feat: enhance agent execution flow with multi-execution view and improved error handling
- Added multi-execution view to display multiple execution statuses.
- Improved error handling in agent triggering to surface failures.
- Updated execution view to support live updates via SSE and added a log panel for node logs.
- Enhanced pipeline canvas to display node statuses during execution.
- Introduced new fields in the build node form for Docker configurations.
- Updated API to support execution streaming and enhanced node catalog descriptions.
2026-05-13 22:15:08 +05:30

218 lines
5.6 KiB
TypeScript

// Catalog of node types the canvas can drop. These are the Langship CI/CD
// pipeline primitives — Trigger → Build → Test → Eval → Policy → Approval
// → Deploy → Promote → Rollback. The canonical type strings keep the
// `flow-nodes-base.` prefix so they line up with the executor registry in
// pkg/executors.
//
// Keep this in sync with pkg/executors/registry.go::RegisterAll(). Anything
// listed here without a matching executor will fail at run time with
// "executor not implemented for node type".
import type { ComponentType } from "react";
import {
Play,
Hammer,
TestTube2,
Gauge,
ShieldCheck,
Pause,
Rocket,
ArrowUpFromLine,
Undo2,
CircleSlash,
Settings2,
} from "lucide-react";
export type CatalogEntry = {
/** Runtime type string: flow-nodes-base.X */
type: string;
/** Display label */
label: string;
/** Short description shown in palette/inspector */
description: string;
/** Lucide icon */
icon: ComponentType<{ className?: string }>;
/** Tailwind colour class for the node header */
color: string;
/** Number of outputs (for source handles) */
outputs: number;
/** Default parameters when dropped */
defaults: Record<string, unknown>;
/** Default Settings (retry etc.) */
settings?: Record<string, unknown>;
/** Group in palette */
group: "trigger" | "build" | "verify" | "gate" | "deploy" | "passthrough";
};
export const CATALOG: CatalogEntry[] = [
{
type: "flow-nodes-base.trigger",
label: "Trigger",
description: "Pipeline entry point. Every pipeline needs exactly one.",
icon: Play,
color: "bg-emerald-500",
outputs: 1,
defaults: { mode: "manual" },
group: "trigger",
},
{
type: "flow-nodes-base.build",
label: "Build",
description: "Clone the agent repo and build an OCI image via BuildKit (or run a shell command).",
icon: Hammer,
color: "bg-amber-500",
outputs: 1,
defaults: {
mode: "docker",
dockerfile: "Dockerfile",
context: ".",
imageName: "",
registry: "registry:5000",
platform: "linux/amd64",
buildArgs: "",
// shell-mode fallback fields:
command: "docker build -t $AGENT_NAME:$COMMIT_SHA .",
workdir: ".",
timeoutSeconds: 600,
},
settings: { retryOnFail: true, maxTries: 2, waitBetweenTries: 5000 },
group: "build",
},
{
type: "flow-nodes-base.test",
label: "Test",
description: "Run unit / integration tests against the build artifact.",
icon: TestTube2,
color: "bg-sky-500",
outputs: 1,
defaults: {
command: "pytest -q",
workdir: ".",
timeoutSeconds: 600,
},
group: "verify",
},
{
type: "flow-nodes-base.eval",
label: "Eval",
description: "Run agent evals (LLM benchmarks, scoring suites).",
icon: Gauge,
color: "bg-violet-500",
outputs: 1,
defaults: {
suite: "default",
threshold: 0.8,
metric: "accuracy",
},
group: "verify",
},
{
type: "flow-nodes-base.policy",
label: "Policy",
description: "Apply governance rules (budget, safety, compliance gates).",
icon: ShieldCheck,
color: "bg-indigo-500",
outputs: 1,
defaults: {
rules: ["max_monthly_spend_usd:1000", "no_pii_in_outputs"],
mode: "enforce",
},
group: "gate",
},
{
type: "flow-nodes-base.waitForApproval",
label: "Approval",
description: "Pause until a human (or quorum) approves continuation.",
icon: Pause,
color: "bg-fuchsia-500",
outputs: 1,
defaults: {
reason: "Manual review",
reviewers: [],
},
group: "gate",
},
{
type: "flow-nodes-base.deploy",
label: "Deploy",
description: "Ship the artifact to a runtime (K8s / Bedrock / Vertex).",
icon: Rocket,
color: "bg-rose-500",
outputs: 1,
defaults: {
runtime: "kubernetes",
env: "dev",
target: "",
},
group: "deploy",
},
{
type: "flow-nodes-base.promote",
label: "Promote",
description: "Promote a deployed artifact to the next environment.",
icon: ArrowUpFromLine,
color: "bg-orange-500",
outputs: 1,
defaults: {
fromEnv: "staging",
toEnv: "prod",
},
group: "deploy",
},
{
type: "flow-nodes-base.rollback",
label: "Rollback",
description: "Revert to a previous deployed revision.",
icon: Undo2,
color: "bg-red-600",
outputs: 1,
defaults: {
revision: "previous",
},
group: "deploy",
},
{
type: "flow-nodes-base.set",
label: "Set",
description: "Define or transform fields on each item.",
icon: Settings2,
color: "bg-slate-500",
outputs: 1,
defaults: { values: { string: [] } },
group: "passthrough",
},
{
type: "flow-nodes-base.noOp",
label: "No-op",
description: "Pass items through unchanged. Useful as a placeholder.",
icon: CircleSlash,
color: "bg-slate-400",
outputs: 1,
defaults: {},
group: "passthrough",
},
];
export const GROUP_LABELS: Record<CatalogEntry["group"], string> = {
trigger: "Trigger",
build: "Build",
verify: "Verify",
gate: "Gate",
deploy: "Deploy",
passthrough: "Pass-through",
};
/** Look up by runtime type. Returns undefined for unsupported types so the
* caller can render a fallback "unsupported" node. */
export function lookup(type: string): CatalogEntry | undefined {
return CATALOG.find((c) => c.type === type);
}
/** Suggest a unique name like "Build", "Build 2", "Build 3". */
export function uniqueName(base: string, existing: Set<string>): string {
if (!existing.has(base)) return base;
let i = 2;
while (existing.has(`${base} ${i}`)) i++;
return `${base} ${i}`;
}