sdk

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 1 Imported by: 0

README

Sokel Plugin SDK

Go Reference PyPI npm License

English · 简体中文

Write a Sokel plugin in Go, Python or TypeScript. Declare what your operations take and return; the SDK handles registration, transport, credentials, file transfer, heartbeats and reconnects.

OnIssuesList(p, func(ctx sokel.Ctx, in *IssuesListIn) (*IssuesListOut, error) {
    issues, err := client.ListIssues(ctx, in.Project, in.State)
    if err != nil {
        return nil, err
    }
    return &IssuesListOut{Issues: issues, Count: len(issues)}, nil
})

That handler signature is generated from your declaration. There is no map[string]any anywhere in your code, and no second copy of the contract to keep in sync by hand.

The same is true in the other two languages — the declaration just lives in a language-neutral sokel.yaml instead of a Go package:

async def issues_list(ctx: Ctx, in_: IssuesListIn) -> IssuesListOut:
    issues = await client.list_issues(in_.project, in_.state)
    return IssuesListOut(issues=issues, count=len(issues))
onIssuesList(p, async (ctx, in_) => {
  const issues = await client.listIssues(in_.project, in_.state);
  return { issues, count: issues.length };
});

Why plugins dial out

A plugin connects to the platform, not the other way round. No inbound port, no public IP, no firewall hole. A plugin running on a NAS in your basement is callable from the platform just like one running in the cloud — which is also why something inherently local, like a coding agent on your own laptop, can be a plugin at all.

Which SDK

Language Install Declare the contract in Getting started
Go go get github.com/sokel-dev/sokel-plugin-sdk a schema/ package (Go builders) below
Python pip install sokel-plugin-sdk sokel.yaml sdk-python/README.md
TypeScript npm install @sokel-dev/plugin-sdk sokel.yaml sdk-node/README.md

All three speak the same JSON-over-NATS wire protocol and report the same contract JSON; a reference plugin (examples/kitchen-sink) is implemented twice and asserted against one golden file, so the SDKs cannot drift apart in how they read the protocol.

Install

The library:

go get github.com/sokel-dev/sokel-plugin-sdk

The sokel-gen CLI — scaffolds plugins and generates the typed code from your declarations:

go install github.com/sokel-dev/sokel-plugin-sdk/cmd/sokel-gen@latest

Requires Go 1.25 or newer. You can skip the install and use go run github.com/sokel-dev/sokel-plugin-sdk/cmd/sokel-gen instead — that is the form used in //go:generate lines, so the version is pinned by your go.mod rather than by whatever you last installed.

How it works

Four steps, always in this order: declare → generate → implement → connect.

Start from a working skeleton rather than an empty directory:

sokel-gen init ./my-plugin
cd my-plugin && go mod tidy && sokel-gen && go build ./...

That scaffolds schema/, main.go, an embedded user-facing doc and both README files, with one real operation already wired end to end. The rest of this section is what init gave you — change it into your own plugin.

1. Declare the contract in a schema/ package — inputs, outputs, events, credential fields:

package schema

import (
	"github.com/sokel-dev/sokel-plugin-sdk/contract"
	"github.com/sokel-dev/sokel-plugin-sdk/contract/field"
)

type IssuesList struct{}

func (IssuesList) Meta() contract.Meta {
	return contract.Meta{ID: "issues_list", Label: "List issues"}
}

func (IssuesList) Inputs() []contract.FieldSpec {
	return []contract.FieldSpec{
		field.String("project").Label("Project"),
		field.Enum("state",
			field.Opt("opened", "Open"),
			field.Opt("closed", "Closed")).Default("opened"),
	}
}

func (IssuesList) Outputs() []contract.FieldSpec {
	return []contract.FieldSpec{
		field.Array("issues", []Issue{}).Label("Issues"),
		field.Int("count").Label("Count"),
	}
}

2. Generate the typed Go from that declaration:

//go:generate go run github.com/sokel-dev/sokel-plugin-sdk/cmd/sokel-gen
go generate ./...

This writes zz_types.go (the In/Out structs) and zz_register.go (an OnXxx function per operation). Don't hand-edit them.

The contract is produced at compile time, not by runtime reflection. A mistake in the declaration fails the build instead of surfacing on some later call. sokel-gen check verifies the generated files are current — wire it into CI, because forgetting to regenerate is the classic way codegen goes wrong.

3. Implement the handlers — the signatures are fully concrete, so the compiler checks your work.

4. Connect back to the platform:

p := sokel.New(sokel.Config{
	Endpoint: sokel.Env("ENDPOINT"),
	Token:    sokel.Env("TOKEN"),
	Name:     "my-plugin",
})
OnIssuesList(p, handleIssuesList)
log.Fatal(p.Run())

Configuration

The SDK reads everything from SOKEL_-prefixed environment variables:

Variable Required Meaning
SOKEL_ENDPOINT yes nats://broker:4222, or an https:// platform URL to discover the broker from
SOKEL_TOKEN yes Access-group token (skp_…) identifying plugin + workspace
SOKEL_NATS_TOKEN no Broker-level auth, if the broker requires it
SOKEL_NATS_CA no Custom CA bundle for tls:// brokers
SOKEL_INSTANCE_ID no Pin a replica identity across restarts
SOKEL_REGION no Region label for the replica

Credentials are never stored by the plugin. The platform injects the resolved fields with each call; read them typed with sokel.CredentialAs[T].

Packages

Package What it is
sokel The runtime: register, dispatch, emit results, files, events, webhooks
contract The contract types — field specs, metadata, credential and event shapes
contract/field Builders for declaring fields (field.String, field.Enum, …)
sokelgen The code generator behind sokel-gen
cmd/sokel-gen The CLI — see below
pluginenv Reads the SOKEL_ environment variables

The sokel-gen CLI

Command What it does
sokel-gen Generate for the current directory — the form used in //go:generate
sokel-gen init <dir> Scaffold a new plugin that builds and runs as-is (-lang go|python|ts)
sokel-gen generate [dir...] Generate; a directory holding many plugins is walked automatically
sokel-gen check [dir...] Verify the generated files are current, write nothing — for CI
sokel-gen export <json|yaml|ts|python> [dir] Print the contract in another form
sokel-gen migrate [dir] Turn an old struct+tag plugin into a schema/ declaration
sokel-gen docs [topic] Print the sokel.yaml format guide / JSON Schema / reference declaration
sokel-gen example [lang] Print the reference plugin: declaration, Python impl, TypeScript impl

generate and check take -schema <name> when the declaration package isn't called schema.

Plugins are found by looking for a schema/ directory or a sokel.yaml, not by reading //go:generate lines. That distinction matters: go generate ./... silently skips a plugin whose directive someone forgot to write, and a skipped plugin's contract drifts with nothing going red. Four first-party plugins were in exactly that state before this was checked.

sokel-gen check ./plugins        # every plugin under ./plugins, one command
Docs in the binary

The format guide, the JSON Schema and a reference declaration covering every contract shape are embedded in the sokel-gen binary — no checkout, no network:

sokel-gen docs        # how to write sokel.yaml
sokel-gen example     # a real declaration using every shape; copy and edit

That is mostly for agents: pointing an LLM at four commands (docsexampleinit -lang python|tsgenerate) is enough for it to write a working plugin, and generate reports every problem in the declaration at once.

check runs every plugin before reporting, so CI shows you all the stale ones at once instead of one per run.

Examples

Example What it shows
examples/sysinfo A complete Go plugin: two operations, a file input, an embedded user-facing doc
examples/kitchen-sink Every contract shape at once — declared once, implemented in Python and TypeScript, both asserted against one golden contract
cd examples/sysinfo
SOKEL_ENDPOINT=nats://localhost:4222 SOKEL_TOKEN=skp_xxx go run .

One declaration, many targets

A contract can be declared from either entry point, and both produce the same intermediate representation:

schema/ package (Go builders) ──┐
                                ├──▶ IR ──┬──▶ typed Go     zz_types.go / zz_register.go
sokel.yaml (language-neutral) ──┘         ├──▶ typed Python sokel_gen.py (pydantic models)
                                          ├──▶ typed TS     sokel.gen.ts (interfaces)
                                          ├──▶ export json  the contract itself
                                          └──▶ export yaml  a sokel.yaml, from a Go declaration

Go plugins use the schema/ package: the contract is executable Go, a misspelled method is a compile error, and existing Go types can be reused directly. Python and TypeScript plugins use sokel.yaml — declaring a few fields should not start with "learn a Go builder API".

sokel-gen init -lang python ./my-plugin   # or -lang ts
sokel-gen generate ./my-plugin            # sokel.yaml → typed models + registration
sokel-gen export yaml ./plugins/gitlab    # the reverse: Go declaration → sokel.yaml

The format is documented in docs/manifest.md. YAML and JSON are the same format (parsed through one path), and unknown keys are an error rather than a silently dropped field.

The exported JSON deliberately omits Go type names — it carries the contract, not the Go implementation detail. This matters because the wire protocol is JSON over NATS with base64 bytes: no gob, no protobuf, nothing Go-specific. This SDK is one implementation of that protocol, not the definition of it. A Rust SDK is the remaining target, and adding one is a renderer over the existing IR plus a runtime, not a second parser.

Releasing

One tag ships all three SDKs at the same version — Go from the tag itself, Python and TypeScript through .github/workflows/release.yml. The procedure and the one-time registry setup are in RELEASING.md.

# bump sdk-node/package.json + sdk-python/pyproject.toml, then
git tag v0.3.0 && git push origin main --tags

Every gate in that pipeline exists because of a failure that only shows up after publishing: version drift between the tag and the packages, stale generated files, a package whose build step was skipped and therefore ships empty.

Status

The Sokel platform itself is not open source yet. Until it is, this SDK is useful for reading the plugin model and preparing a plugin — but a plugin needs a running Sokel instance to dial into.

License

Apache-2.0. See LICENSE.

Documentation

Overview

Package sdk 只做一件事:把「写一个插件需要读的东西」编进 sokel-gen 二进制。

为什么要编进去:`go install` 装来的 sokel-gen 手边没有这个仓库。 而需要读这些东西的往往不是人——让 AI 照着写插件时,它能执行命令、拿到 stdout, 却未必能访问 GitHub。`sokel-gen docs` / `sokel-gen example` 就是给它准备的入口。

这里**只引用不复制**:文档、schema、参考声明都还是仓库里那一份, 编进二进制的是同一个文件——复制一份的话,两份迟早不一样,而读到旧那份的人不会知道。

Index

Constants

This section is empty.

Variables

View Source
var ExampleManifest string

ExampleManifest 覆盖全部契约形态的参考声明(examples/kitchen-sink/sokel.yaml)。

View Source
var ExampleNode string

ExampleNode 与上面那份声明配套的 TypeScript 实现。

View Source
var ExamplePython string

ExamplePython 与上面那份声明配套的 Python 实现。

View Source
var ManifestDoc string

ManifestDoc sokel.yaml 的写法说明(docs/manifest.md)。

View Source
var Schema string

Schema sokel.yaml 的 JSON Schema:编辑器补全用,也能喂给会读 schema 的工具。

Functions

This section is empty.

Types

This section is empty.

Directories

Path Synopsis
cmd
sokel-gen command
sokel-gen:Sokel 插件的契约工具链。
sokel-gen:Sokel 插件的契约工具链。
Package contract:插件契约的**唯一定义** —— 字段类型、声明、与 Go 类型之间的绑定。
Package contract:插件契约的**唯一定义** —— 字段类型、声明、与 Go 类型之间的绑定。
auth
Package auth 构造凭证的获取方式声明。
Package auth 构造凭证的获取方式声明。
field
Package field 提供声明插件契约的 builder。
Package field 提供声明插件契约的 builder。
examples
sysinfo command
sysinfo 插件:返回「插件运行系统」的基础信息。
sysinfo 插件:返回「插件运行系统」的基础信息。
Package plugin:插件实现与**传输**之间的接缝。
Package plugin:插件实现与**传输**之间的接缝。
Package pluginenv:插件侧环境变量的统一读法。
Package pluginenv:插件侧环境变量的统一读法。
Package sokel is the Sokel plugin SDK for Go.
Package sokel is the Sokel plugin SDK for Go.
field
Package field 是 plugin-core/contract/field 的转发。
Package field 是 plugin-core/contract/field 的转发。
Package sokelgen 从**源码**(AST)推导插件契约,取代运行时反射。
Package sokelgen 从**源码**(AST)推导插件契约,取代运行时反射。
internal/demoschema
Package demoschema 是生成器的验证语料:覆盖 builder 的主要形态。
Package demoschema 是生成器的验证语料:覆盖 builder 的主要形态。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL