Plugin Type Definitions
Complete type reference for CyanPrint plugins
Plugin Type Definitions
Complete type reference for the CyanPrint plugin SDK.
Core Types
CyanPluginInput
Prop
Type
Type Safety
The config property is typed as unknown. You must cast it to your expected type or use type guards to validate the configuration at runtime.
PluginOutput
Prop
Type
LambdaPluginFn
/*** Function signature for plugin handler*/type LambdaPluginFn = (input: CyanPluginInput) => Promise<PluginOutput>;
# Function signature for plugin handler# Async function that takes PluginInput and returns PluginOutputasync def handler(input: PluginInput) -> PluginOutput:...
// Function signature for plugin handler// Async method that takes PluginInput and returns Task<PluginOutput>public static async Task<PluginOutput> Main(PluginInput input){// ...}
Entry Point
StartPluginWithLambda
/*** Register a plugin handler function.* Starts HTTP server on port 5552.** @param handler - Async function that processes the directory*/function StartPluginWithLambda(handler: LambdaPluginFn): void;
# Register a plugin handler function.# Starts HTTP server on port 5552.## @handler - Async function that processes the directoryfrom atomicloud_cyan_sdk import start_plugin_with_fn@start_plugin_with_fnasync def main(input: PluginInput) -> PluginOutput:...
// Register a plugin handler function.// Starts HTTP server on port 5552.//// Apply [PluginMain] attribute to your static async methodusing Atomicloud.Cyan.Sdk;[PluginMain]public static async Task<PluginOutput> Main(PluginInput input){// ...}
Usage Example
Complete Plugin with Types
plugin.ts
import { StartPluginWithLambda, type CyanPluginInput, type PluginOutput } from '@atomicloud/cyan-sdk';import { $ } from 'bun';// Define your config interfaceinterface GitSetupConfig {init?: boolean;addAll?: boolean;initialCommit?: boolean;commitMessage?: string;branch?: string;}// Define your plugin function with proper typingasync function setupGit(input: CyanPluginInput): Promise<PluginOutput> {const { directory } = input;// Type-safe config with defaultsconst config: GitSetupConfig = {init: true,addAll: true,initialCommit: true,commitMessage: 'Initial commit',branch: 'main',...(input.config as Partial<GitSetupConfig>)};if (config.init) {await $`git -C ${directory} init`.quiet();if (config.branch) {await $`git -C ${directory} checkout -b ${config.branch}`.quiet();}}if (config.addAll) {await $`git -C ${directory} add .`.quiet();}if (config.initialCommit && config.commitMessage) {await $`git -C ${directory} commit -m ${config.commitMessage}`.quiet();}return { directory };}// Register pluginStartPluginWithLambda(setupGit);
plugin.py
from atomicloud_cyan_sdk import start_plugin_with_fn, PluginInput, PluginOutputfrom dataclasses import dataclassfrom typing import Optionalimport subprocess# Define your config class@dataclassclass GitSetupConfig:init: bool = TrueaddAll: bool = TrueinitialCommit: bool = TruecommitMessage: str = 'Initial commit'branch: str = 'main'# Define your plugin function with proper typing@start_plugin_with_fnasync def setup_git(input: PluginInput) -> PluginOutput:directory = input.directory# Type-safe config with defaultsdefaults = GitSetupConfig()config = GitSetupConfig(init=input.config.get('init', defaults.init),addAll=input.config.get('addAll', defaults.addAll),initialCommit=input.config.get('initialCommit', defaults.initialCommit),commitMessage=input.config.get('commitMessage', defaults.commitMessage),branch=input.config.get('branch', defaults.branch))if config.init:subprocess.run(['git', '-C', directory, 'init'], capture_output=True)if config.branch:subprocess.run(['git', '-C', directory, 'checkout', '-b', config.branch], capture_output=True)if config.addAll:subprocess.run(['git', '-C', directory, 'add', '.'], capture_output=True)if config.initialCommit and config.commitMessage:subprocess.run(['git', '-C', directory, 'commit', '-m', config.commitMessage], capture_output=True)return {'directory': directory}
Plugin.cs
using Atomicloud.Cyan.Sdk;using System.Diagnostics;using System.Text.Json;// Define your config classpublic class GitSetupConfig{public bool Init { get; set; } = true;public bool AddAll { get; set; } = true;public bool InitialCommit { get; set; } = true;public string CommitMessage { get; set; } = "Initial commit";public string Branch { get; set; } = "main";}[PluginMain]public static class Plugin{public static async Task<PluginOutput> Main(PluginInput input){var directory = input.Directory;// Type-safe config with defaultsvar config = new GitSetupConfig();if (input.Config != null){var json = JsonDocument.Parse(input.Config.ToString());var root = json.RootElement;if (root.TryGetProperty("init", out var init))config.Init = init.GetBoolean();if (root.TryGetProperty("addAll", out var addAll))config.AddAll = addAll.GetBoolean();if (root.TryGetProperty("initialCommit", out var initialCommit))config.InitialCommit = initialCommit.GetBoolean();if (root.TryGetProperty("commitMessage", out var commitMessage))config.CommitMessage = commitMessage.GetString() ?? "Initial commit";if (root.TryGetProperty("branch", out var branch))config.Branch = branch.GetString() ?? "main";}if (config.Init){Process.Start("git", $"-C {directory} init")?.WaitForExit();if (!string.IsNullOrEmpty(config.Branch)){Process.Start("git", $"-C {directory} checkout -b {config.Branch}")?.WaitForExit();}}if (config.AddAll){Process.Start("git", $"-C {directory} add .")?.WaitForExit();}if (config.InitialCommit && !string.IsNullOrEmpty(config.CommitMessage)){Process.Start("git", $"-C {directory} commit -m \"{config.CommitMessage}\"")?.WaitForExit();}return new PluginOutput { Directory = directory };}}
Type Guards
Validating Config
type-guards.ts
import { StartPluginWithLambda } from '@atomicloud/cyan-sdk';interface MyConfig {enabled: boolean;items: string[];}function isMyConfig(config: unknown): config is MyConfig {const c = config as Record<string, unknown>;return (typeof c?.enabled === 'boolean' &&Array.isArray(c?.items) &&c.items.every((i: unknown) => typeof i === 'string'));}StartPluginWithLambda(async (input: CyanPluginInput) => {if (!isMyConfig(input.config)) {throw new Error('Invalid config');}// Now TypeScript knows input.config is MyConfigif (input.config.enabled) {for (const item of input.config.items) {console.log(item);}}return { directory: input.directory };});
type_guards.py
from atomicloud_cyan_sdk import start_plugin_with_fn, PluginInput, PluginOutputfrom typing import Any, Listdef is_valid_config(config: dict) -> bool:return (isinstance(config.get('enabled'), bool) andisinstance(config.get('items'), list) andall(isinstance(i, str) for i in config.get('items', [])))@start_plugin_with_fnasync def main(input: PluginInput) -> PluginOutput:if not is_valid_config(input.config):raise ValueError('Invalid config')# Now we know config is validif input.config['enabled']:for item in input.config['items']:print(item)return {'directory': input.directory}
TypeGuards.cs
using Atomicloud.Cyan.Sdk;using System.Text.Json;public class MyConfig{public bool Enabled { get; set; }public List<string> Items { get; set; } = new();}public static class ConfigValidator{public static bool IsValidConfig(JsonElement config){if (config.ValueKind != JsonValueKind.Object)return false;bool hasEnabled = false;bool hasItems = false;if (config.TryGetProperty("enabled", out var enabled)){hasEnabled = enabled.ValueKind == JsonValueKind.True || enabled.ValueKind == JsonValueKind.False;}if (config.TryGetProperty("items", out var items)){hasItems = items.ValueKind == JsonValueKind.Array;}return hasEnabled && hasItems;}}[PluginMain]public static class Plugin{public static async Task<PluginOutput> Main(PluginInput input){var json = JsonDocument.Parse(input.Config.ToString());if (!ConfigValidator.IsValidConfig(json.RootElement)){throw new ArgumentException("Invalid config");}// Now we know config is validvar config = JsonSerializer.Deserialize<MyConfig>(input.Config.ToString());if (config?.Enabled == true){foreach (var item in config.Items){Console.WriteLine(item);}}return new PluginOutput { Directory = input.Directory };}}
With Defaults
parse-config.ts
interface Config {option1: boolean;option2: string;}function parseConfig(config: unknown): Config {const c = config as Record<string, unknown> | undefined;return {option1: typeof c?.option1 === 'boolean' ? c.option1 : true,option2: typeof c?.option2 === 'string' ? c.option2 : 'default'};}
parse_config.py
from dataclasses import dataclassfrom typing import Any, Optional@dataclassclass Config:option1: booloption2: strdef parse_config(config: Optional[dict]) -> Config:return Config(option1=config.get('option1') if isinstance(config.get('option1'), bool) else True,option2=config.get('option2') if isinstance(config.get('option2'), str) else 'default')
ParseConfig.cs
public class Config{public bool Option1 { get; set; }public string Option2 { get; set; }}public static Config ParseConfig(JsonElement config){return new Config{Option1 = config.TryGetProperty("option1", out var opt1) && opt1.ValueKind == JsonValueKind.True,Option2 = config.TryGetProperty("option2", out var opt2) && opt2.ValueKind == JsonValueKind.String? opt2.GetString() ?? "default": "default"};}
Related
- StartPluginWithLambda - Entry point
- Plugin Input/Output - Interface details