Documentation
¶
Overview ¶
Package extlibs provides external libraries that need explicit registration
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration ¶
Package extlibs provides external libraries that need explicit registration
Index ¶
- Constants
- Variables
- func CloseKVStore()
- func CreateJSONRPCErrorInstance(code int64, message string, data object.Object) *object.Instance
- func CreateRequestInstance(method, path, body string, headers, query, pathParams map[string]string, ...) *object.Instance
- func CreateWebSocketClientInstance(conn *WebSocketServerConn) *object.Instance
- func EditFile(ctx context.Context, path, search, replace string) (int, error)
- func Find(ctx context.Context, root string, opts FindOptions) ([]string, error)
- func InitKVStore(path string) error
- func IsJSONRPCError(obj object.Object) bool
- func KVStoreDB(store object.Object) *snapshotkv.DB
- func NewCsvLibrary() *object.Library
- func NewFindLibrary(config fssecurity.Config) *object.Library
- func NewGlobLibrary(config fssecurity.Config) *object.Library
- func NewGrepLibrary(config fssecurity.Config) *object.Library
- func NewInputBuiltin(stdin io.Reader) *object.Builtin
- func NewKVSubLibrary() *object.Library
- func NewKVSubLibraryWithSecurity(allowedPaths []string) *object.Library
- func NewOSLibrary(config fssecurity.Config) (*object.Library, *object.Library)
- func NewPathlibLibrary(config fssecurity.Config) *object.Library
- func NewSandboxLibrary(allowedPaths []string) *object.Library
- func NewSecretLibrary(registry *secretprovider.Registry) *object.Library
- func NewSedLibrary(config fssecurity.Config) *object.Library
- func NewShlexLibrary() *object.Library
- func NewShutilLibrary(config fssecurity.Config) *object.Library
- func NewSysLibrary(argv []string, stdin io.Reader) *object.Library
- func NewTarfileLibrary(config fssecurity.Config) *object.Library
- func NewTempfileLibrary(config fssecurity.Config) *object.Library
- func NewXmlLibrary() *object.Library
- func NewZipfileLibrary(config fssecurity.Config) *object.Library
- func RegisterCleanup(fn func())
- func RegisterCsvLibrary(registrar object.LibraryRegistrar)
- func RegisterFSLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterFindLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterGlobLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterGrepLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterHTMLParserLibrary(registrar interface{ ... })
- func RegisterLoggingLibrary(registrar interface{ ... }, loggerInstance logger.Logger)
- func RegisterLoggingLibraryDefault(registrar interface{ ... })
- func RegisterMarkdownLibrary(registrar interface{ ... })
- func RegisterOSLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterPathlibLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterRequestsLibrary(registrar interface{ ... }, cfg ...*netsecurity.Config)
- func RegisterRuntimeHTTPLibrary(registrar interface{ ... })
- func RegisterRuntimeJSONRPCLibrary(registrar interface{ ... })
- func RegisterRuntimeKVLibrary(registrar interface{ ... })
- func RegisterRuntimeKVLibraryWithSecurity(registrar interface{ ... }, allowedPaths []string)
- func RegisterRuntimeLibrary(registrar interface{ ... })
- func RegisterRuntimeLibraryAll(registrar interface{ ... }, allowedPaths []string)
- func RegisterRuntimeMCPLibrary(registrar interface{ ... })
- func RegisterRuntimePluginLibrary(registrar interface{ ... })
- func RegisterRuntimeSandboxLibrary(registrar interface{ ... }, allowedPaths []string)
- func RegisterRuntimeSyncLibrary(registrar interface{ ... })
- func RegisterSecretLibrary(registrar interface{ ... }, registry *secretprovider.Registry)
- func RegisterSecretsLibrary(registrar interface{ ... })
- func RegisterSedLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterShlexLibrary(registrar object.LibraryRegistrar)
- func RegisterShutilLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterSubprocessLibrary(registrar interface{ ... })
- func RegisterSysLibrary(registrar sysRegistrar, argv []string, stdin io.Reader)
- func RegisterTOMLLibrary(registrar interface{ ... })
- func RegisterTarfileLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterTempfileLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func RegisterTemplateHTMLLibrary(registrar interface{ ... })
- func RegisterTemplateTextLibrary(registrar interface{ ... })
- func RegisterWaitForLibrary(registrar interface{ ... }, cfg ...*netsecurity.Config)
- func RegisterWebSocketLibrary(registrar interface{ ... }, cfg ...*netsecurity.Config)
- func RegisterXmlLibrary(registrar object.LibraryRegistrar)
- func RegisterYAMLLibrary(registrar interface{ ... })
- func RegisterZipfileLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
- func ReleaseBackgroundTasks()
- func ResetRuntime()
- func SedReplace(ctx context.Context, old, replacement, path string, opts SedOptions) (int64, error)
- func SedReplacePattern(ctx context.Context, pattern, replacement, path string, opts SedOptions) (int64, error)
- func SetBackgroundFactory(factory SandboxFactory)
- func SetSandboxFactory(factory SandboxFactory)
- func SetTaskErrorLogger(fn func(name string, err error))
- func WaitBackgroundTasks()
- type CompletedProcess
- func (cp *CompletedProcess) AsBool() (bool, object.Object)
- func (cp *CompletedProcess) AsDict() (map[string]object.Object, object.Object)
- func (cp *CompletedProcess) AsFloat() (float64, object.Object)
- func (cp *CompletedProcess) AsInt() (int64, object.Object)
- func (cp *CompletedProcess) AsList() ([]object.Object, object.Object)
- func (cp *CompletedProcess) AsString() (string, object.Object)
- func (cp *CompletedProcess) Inspect() string
- func (cp *CompletedProcess) Type() object.ObjectType
- type ExtractMatch
- type FindEntry
- type FindOptions
- type GlobLibraryInstance
- type GrepMatch
- type GrepOptions
- type PathlibLibraryInstance
- type Promise
- type RouteInfo
- type RuntimeAtomic
- type RuntimeQueue
- type RuntimeShared
- type RuntimeWaitGroup
- type SandboxFactory
- type SandboxInstance
- type SedOptions
- type WebSocketClientConn
- func (c *WebSocketClientConn) Close() error
- func (c *WebSocketClientConn) ClosedChan() <-chan struct{}
- func (c *WebSocketClientConn) IsConnected() bool
- func (c *WebSocketClientConn) ReadWithTimeout(timeout time.Duration) (int, []byte, error)
- func (c *WebSocketClientConn) RemoteAddr() string
- func (c *WebSocketClientConn) WriteMessage(msgType int, data []byte) error
- type WebSocketRouteInfo
- type WebSocketServerConn
- func (c *WebSocketServerConn) Close() error
- func (c *WebSocketServerConn) ClosedChan() <-chan struct{}
- func (c *WebSocketServerConn) ID() string
- func (c *WebSocketServerConn) IsConnected() bool
- func (c *WebSocketServerConn) ReadWithTimeout(timeout time.Duration) (int, []byte, error)
- func (c *WebSocketServerConn) RemoteAddr() string
- func (c *WebSocketServerConn) WriteMessage(msgType int, data []byte) error
Constants ¶
const ( RequestsLibraryName = "requests" SysLibraryName = "sys" SecretsLibraryName = "secrets" SubprocessLibraryName = "subprocess" HTMLParserLibraryName = "html.parser" OSLibraryName = "os" OSPathLibraryName = "os.path" PathlibLibraryName = "pathlib" GlobLibraryName = "glob" TempfileLibraryName = "tempfile" ShutilLibraryName = "shutil" ShlexLibraryName = "shlex" ZipfileLibraryName = "zipfile" TarfileLibraryName = "tarfile" CsvLibraryName = "scriptling.csv" XmlLibraryName = "scriptling.xml" LoggingLibraryName = "logging" WaitForLibraryName = "scriptling.wait_for" RuntimeHTTPLibraryName = "scriptling.runtime.http" RuntimeKVLibraryName = "scriptling.runtime.kv" RuntimeSyncLibraryName = "scriptling.runtime.sync" RuntimeSandboxLibraryName = "scriptling.runtime.sandbox" RuntimeJSONRPCLibraryName = "scriptling.runtime.jsonrpc" RuntimePluginLibraryName = "scriptling.runtime.plugin" AILibraryName = "scriptling.ai" MCPLibraryName = "scriptling.mcp" ToonLibraryName = "scriptling.toon" YAMLLibraryName = "yaml" AgentLibraryName = "scriptling.ai.agent" InteractLibraryName = "scriptling.ai.agent.interact" SimilarityLibraryName = "scriptling.similarity" SecretLibraryName = "scriptling.secret" TOMLLibraryName = "toml" WebSocketLibraryName = "scriptling.net.websocket" MulticastLibraryName = "scriptling.net.multicast" UnicastLibraryName = "scriptling.net.unicast" GossipLibraryName = "scriptling.net.gossip" ResolveLibraryName = "scriptling.net.resolve" FSLibraryName = "fs" GrepLibraryName = "scriptling.grep" FindLibraryName = "scriptling.find" SedLibraryName = "scriptling.sed" ContainerLibraryName = "scriptling.container" NomadLibraryName = "scriptling.nomad" TemplateHTMLLibraryName = "scriptling.template.html" TemplateTextLibraryName = "scriptling.template.text" FileProvisionLibraryName = "scriptling.provision.file" FetchProvisionLibraryName = "scriptling.provision.fetch" MarkdownLibraryName = "scriptling.markdown" )
Library names as constants for easy reference
const ( RuntimeMCPLibraryName = "scriptling.runtime.mcp" // MCPRegistryVar is the environment variable name where the mcp.tool() // decorator records tool registrations. The folder scanner reads this after // evaluating a .py file to discover decorated tools. MCPRegistryVar = "__mcp_registry" )
const RuntimeLibraryName = "scriptling.runtime"
Variables ¶
var CompletedProcessClass = &object.Class{ Name: "CompletedProcess", Methods: map[string]object.Object{ "check_returncode": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } if instance, ok := args[0].(*object.Instance); ok { if returncode, ok := instance.Field("returncode").(*object.Integer); ok { if returncode.IntValue() != 0 { return errors.NewError("Command returned non-zero exit status %d", returncode.IntValue()) } return args[0] } } return errors.NewError("Invalid CompletedProcess instance") }, HelpText: `check_returncode() - Check if the process returned successfully Raises an exception if returncode is non-zero.`, }, }, }
CompletedProcessClass defines the CompletedProcess class
var ErrPathNotAllowed = errors.New("path is outside allowed directories")
ErrPathNotAllowed is returned when a requested path falls outside the AllowedPaths configured on the options struct.
var ErrSearchNotFound = errors.New("search text not found")
ErrSearchNotFound is returned by EditFile when the search text does not appear in the file at all.
var ErrSearchNotUnique = errors.New("search text matched multiple times")
ErrSearchNotUnique is returned by EditFile when the search text appears more than once. The caller should provide more surrounding context to disambiguate.
var HTMLParserLibrary = object.NewLibrary(HTMLParserLibraryName, nil, map[string]object.Object{ "HTMLParser": &object.Class{ Name: "HTMLParser", Methods: htmlParserMethods, }, }, "HTML parser library compatible with Python's html.parser module")
HTMLParserLibrary provides Python-compatible html.parser functionality
var HTTPSubLibrary = object.NewLibrary(RuntimeHTTPLibraryName, map[string]*object.Builtin{ "get": httpVerbEntry("GET"), "post": httpVerbEntry("POST"), "put": httpVerbEntry("PUT"), "patch": httpVerbEntry("PATCH"), "delete": httpVerbEntry("DELETE"), "route": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } path, err := args[0].AsString() if err != nil { return err } var methods []string if m := kwargs.Get("methods"); m != nil { if list, e := m.AsList(); e == nil { for _, item := range list { if method, e := item.AsString(); e == nil { methods = append(methods, strings.ToUpper(method)) } } } } if len(methods) == 0 { methods = []string{"GET", "POST", "PUT", "DELETE"} } if len(args) == 1 { return makeHTTPDecorator(ctx, func(ref string) { for _, m := range methods { registerHTTPRoute(m, path, ref) } }) } handler, err := args[1].AsString() if err != nil { return err } for _, m := range methods { registerHTTPRoute(m, path, handler) } return &object.Null{} }, HelpText: `route(path, handler=None, methods=["GET","POST","PUT","DELETE"]) - Register a route for multiple methods, or use as decorator Decorator form: @http.route("/api", methods=["GET", "POST"]) def handler(request): ... Imperative form: runtime.http.route("/api", "handlers.api", methods=["GET", "POST"])`, }, "middleware": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } if fn, ok := args[0].(*object.Function); ok { env := evaluator.GetEnvFromContext(ctx) ref := resolveModuleRef(env, fn.Name) if ref == "" { return errors.NewError("cannot determine module name for @http.middleware decorator") } RuntimeState.Lock() RuntimeState.Middleware = ref RuntimeState.Unlock() return fn } handler, err := args[0].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.http.middleware registered after start_server() — will not be applied\n") } RuntimeState.Middleware = handler RuntimeState.Unlock() return &object.Null{} }, HelpText: `middleware(handler) - Register middleware for all routes, or use as bare decorator Decorator form: @http.middleware def auth(request): ... Imperative form: runtime.http.middleware("auth.check_request") The middleware receives the request object and should return: - None to continue to the handler - A response dict to short-circuit (block the request)`, }, "static": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 2); err != nil { return err } path, err := args[0].AsString() if err != nil { return err } directory, err := args[1].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.http.static %q registered after start_server() — route will not be served\n", path) } RuntimeState.Routes["GET "+path] = &RouteInfo{ Methods: []string{"GET"}, Static: true, StaticDir: directory, } RuntimeState.Unlock() return &object.Null{} }, HelpText: `static(path, directory) - Register a static file serving route Parameters: path (string): URL path prefix for static files (e.g., "/assets") directory (string): Local directory to serve files from Example: runtime.http.static("/assets", "./public")`, }, "json": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } statusCode := int64(200) var data object.Object = &object.Null{} if len(args) >= 2 { if code, err := args[0].AsInt(); err == nil { statusCode = code } data = args[1] } else { data = args[0] } if c := kwargs.Get("status"); c != nil { if code, e := c.AsInt(); e == nil { statusCode = code } } return httpResponse(statusCode, map[string]string{ "Content-Type": "application/json", }, data) }, HelpText: `json(status_code, data) - Create a JSON response Parameters: status_code (int): HTTP status code (e.g., 200, 404, 500) data: Data to serialize as JSON Returns: dict: Response object for the server Example: return runtime.http.json(200, {"status": "ok"}) return runtime.http.json(404, {"error": "Not found"})`, }, "redirect": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } location, err := args[0].AsString() if err != nil { return err } statusCode := int64(302) if len(args) > 1 { if code, e := args[1].AsInt(); e == nil { statusCode = code } } if c := kwargs.Get("status"); c != nil { if code, e := c.AsInt(); e == nil { statusCode = code } } return httpResponse(statusCode, map[string]string{ "Location": location, }, object.NewString("")) }, HelpText: `redirect(location, status=302) - Create a redirect response Parameters: location (string): URL to redirect to status (int, optional): HTTP status code (default: 302) Returns: dict: Response object for the server Example: return runtime.http.redirect("/new-location") return runtime.http.redirect("/permanent", status=301)`, }, "html": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } statusCode := int64(200) var htmlContent object.Object = object.NewString("") if len(args) >= 2 { if code, err := args[0].AsInt(); err == nil { statusCode = code } htmlContent = args[1] } else { htmlContent = args[0] } if c := kwargs.Get("status"); c != nil { if code, e := c.AsInt(); e == nil { statusCode = code } } return httpResponse(statusCode, map[string]string{ "Content-Type": "text/html; charset=utf-8", }, htmlContent) }, HelpText: `html(status_code, content) - Create an HTML response Parameters: status_code (int): HTTP status code content (string): HTML content to return Returns: dict: Response object for the server Example: return runtime.http.html(200, "<h1>Hello World</h1>")`, }, "text": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } statusCode := int64(200) var textContent object.Object = object.NewString("") if len(args) >= 2 { if code, err := args[0].AsInt(); err == nil { statusCode = code } textContent = args[1] } else { textContent = args[0] } if c := kwargs.Get("status"); c != nil { if code, e := c.AsInt(); e == nil { statusCode = code } } return httpResponse(statusCode, map[string]string{ "Content-Type": "text/plain; charset=utf-8", }, textContent) }, HelpText: `text(status_code, content) - Create a plain text response Parameters: status_code (int): HTTP status code content (string): Text content to return Returns: dict: Response object for the server Example: return runtime.http.text(200, "Hello World")`, }, "parse_query": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } queryString, err := args[0].AsString() if err != nil { return err } values, parseErr := url.ParseQuery(queryString) if parseErr != nil { return errors.NewError("failed to parse query string: %s", parseErr.Error()) } pairs := make(map[string]object.DictPair) for key, vals := range values { keyObj := object.NewString(key) dk := object.DictKey(keyObj) if len(vals) == 1 { pairs[dk] = object.DictPair{ Key: keyObj, Value: object.NewString(vals[0]), } } else { elements := make([]object.Object, len(vals)) for i, v := range vals { elements[i] = object.NewString(v) } pairs[dk] = object.DictPair{ Key: keyObj, Value: &object.List{Elements: elements}, } } } return &object.Dict{Pairs: pairs} }, HelpText: `parse_query(query_string) - Parse a URL query string Parameters: query_string (string): Query string to parse (with or without leading ?) Returns: dict: Parsed key-value pairs Example: params = runtime.http.parse_query("name=John&age=30")`, }, "not_found": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } if fn, ok := args[0].(*object.Function); ok { env := evaluator.GetEnvFromContext(ctx) ref := resolveModuleRef(env, fn.Name) if ref == "" { return errors.NewError("cannot determine module name for @http.not_found decorator") } RuntimeState.Lock() RuntimeState.NotFoundHandler = ref RuntimeState.Unlock() return fn } handler, err := args[0].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.http.not_found registered after start_server() — will not be applied\n") } RuntimeState.NotFoundHandler = handler RuntimeState.Unlock() return &object.Null{} }, HelpText: `not_found(handler) - Register a 404 handler, or use as bare decorator Decorator form: @http.not_found def handle_404(request): ... Imperative form: runtime.http.not_found("handlers.not_found") The handler receives the request object and should return a response. It is called when no route matches the request path, or when a static asset is not found.`, }, "websocket": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } path, err := args[0].AsString() if err != nil { return err } if len(args) == 1 { return makeHTTPDecorator(ctx, func(ref string) { RuntimeState.Lock() RuntimeState.WebSocketRoutes[path] = &WebSocketRouteInfo{Handler: ref} RuntimeState.Unlock() }) } handler, err := args[1].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.http.websocket %q registered after start_server() — route will not be served\n", path) } RuntimeState.WebSocketRoutes[path] = &WebSocketRouteInfo{ Handler: handler, } RuntimeState.Unlock() return &object.Null{} }, HelpText: `websocket(path, handler=None) - Register a WebSocket route, or use as decorator Decorator form: @http.websocket("/ws") def chat_handler(client): client.send("Welcome!") while client.connected(): msg = client.receive(timeout=60) if msg: client.send("Echo: " + msg) Imperative form: runtime.http.websocket("/chat", "handlers.chat_handler") The handler receives a WebSocketClient object and runs for the connection lifetime.`, }, }, map[string]object.Object{ "Request": RequestClass, "WebSocketClient": WebSocketClientClass, }, "HTTP server route registration and response helpers")
var JSONRPCErrorClass = &object.Class{
Name: "JSONRPCError",
}
JSONRPCErrorClass is the class for error objects produced by runtime.jsonrpc.error(). The stdio server recognises instances of this class and emits a JSON-RPC error response instead of a result.
var JSONRPCSubLibrary = object.NewLibrary(RuntimeJSONRPCLibraryName, map[string]*object.Builtin{ "method": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } name, err := args[0].AsString() if err != nil { return err } if len(args) == 1 { return makeJSONRPCDecorator(ctx, func(ref string) { registerJSONRPCMethod(name, ref) }) } handler, err := args[1].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.jsonrpc.method %q registered after start_server() — method will not be served\n", name) } RuntimeState.JSONRPCMethods[name] = handler RuntimeState.Unlock() return &object.Null{} }, HelpText: `method(name, handler=None) - Register a JSON-RPC method handler, or use as decorator Decorator form: import scriptling.jsonrpc as jsonrpc @jsonrpc.method("echo") def echo(params): return params Imperative form: runtime.jsonrpc.method("echo", "handlers.echo") The handler receives the decoded JSON-RPC params as its single argument and returns a JSON-compatible result. Return runtime.jsonrpc.error(...) to produce an error response.`, }, "notification": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } name, err := args[0].AsString() if err != nil { return err } if len(args) == 1 { return makeJSONRPCDecorator(ctx, func(ref string) { registerJSONRPCNotification(name, ref) }) } handler, err := args[1].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.jsonrpc.notification %q registered after start_server() — notification will not be served\n", name) } RuntimeState.JSONRPCNotifications[name] = handler RuntimeState.Unlock() return &object.Null{} }, HelpText: `notification(name, handler=None) - Register a JSON-RPC notification handler, or use as decorator Decorator form: @jsonrpc.notification("updated") def on_updated(params): ... Imperative form: runtime.jsonrpc.notification("updated", "handlers.on_updated") Notifications are JSON-RPC requests without an id. The handler receives the decoded params and no response is written.`, }, "error": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 2); err != nil { return err } code, err := args[0].AsInt() if err != nil { return err } message, err := args[1].AsString() if err != nil { return err } var data object.Object if len(args) >= 3 { data = args[2] } return CreateJSONRPCErrorInstance(code, message, data) }, HelpText: `error(code, message, data=None) - Build a JSON-RPC error response Parameters: code (int): JSON-RPC error code (e.g. -32602 for invalid params) message (str): Human-readable error message data (any, optional): Optional structured data attached to the error Return this from a method handler to emit a JSON-RPC error response with a custom code. If omitted the response uses the given code and message. Example: def divide(params): if params["b"] == 0: return runtime.jsonrpc.error(-32602, "division by zero") return params["a"] / params["b"]`, }, }, map[string]object.Object{ "JSONRPCError": JSONRPCErrorClass, }, "stdio JSON-RPC 2.0 server method and notification registration")
JSONRPCSubLibrary exposes runtime.jsonrpc for registering stdio JSON-RPC 2.0 method and notification handlers. Handlers are referenced by string ("library.function") and run on a fresh evaluator per request, matching the runtime.http / MCP / WebSocket concurrency model.
var MCPSubLibrary = object.NewLibrary(RuntimeMCPLibraryName, map[string]*object.Builtin{ "tool": { Fn: mcpToolDecorator, HelpText: `tool(description, params=None, keywords=None, discoverable=False) - Decorator for MCP tools Decorates a function to register it as an MCP tool. The function's parameters become the tool's input schema; the return value becomes the tool response. Parameters: description (str): Tool description shown to the AI params (dict, optional): Parameter metadata keyed by name. Each value is either a string (the description; type inferred from default or defaults to "string") or a dict with keys "type", "description", and optional "required". keywords (list, optional): Keywords for tool search/discovery discoverable (bool, optional): If True, tool is hidden from tools/list and only available via search (default: False) Returns: A decorator function that registers the tool and returns the original function. Example: import scriptling.runtime.mcp as mcp @mcp.tool( description="Calculate a mathematical expression", params={"expr": "Expression to evaluate (e.g. 2+3*4)"}, ) def calc(expr): return f"{expr} = {eval(expr)}" @mcp.tool(description="Greet someone", params={ "name": "Name of the person", "times": {"type": "int", "description": "Number of greetings"}, }) def greet(name, times=1): return "\n".join(f"Hello, {name}!" for _ in range(times))`, }, }, nil, "MCP tool, resource, and prompt registration via decorators")
MCPSubLibrary is the scriptling.runtime.mcp sub-library. It provides decorator functions for defining MCP tools (and in future, resources and prompts) from script code. Registrations are recorded per-interpreter in __mcp_registry rather than the global RuntimeState.
var MarkdownLibrary = object.NewLibrary(MarkdownLibraryName, map[string]*object.Builtin{ "to_html": { Fn: markdownToHTMLFunc, HelpText: `to_html(markdown_string) - Convert Markdown to HTML Converts a Markdown string to an HTML string using the GitHub Flavored Markdown (GFM) specification. Supports headings, bold, italic, code blocks, fenced code, blockquotes, ordered and unordered lists, tables, strikethrough, task lists, and auto-linked URLs. Args: markdown_string (str): The Markdown source to convert. Returns: str: HTML representation of the Markdown input. Example: import scriptling.markdown as markdown html = markdown.to_html("# Hello\n\nThis is **bold** and _italic_ text.") print(html) # <h1 id="hello">Hello</h1> # <p>This is <strong>bold</strong> and <em>italic</em> text.</p> html = markdown.to_html("- item one\n- item two") print(html) # <ul> # <li>item one</li> # <li>item two</li> # </ul>`, }, }, nil, "Markdown parsing and conversion")
MarkdownLibrary provides Markdown parsing and conversion to HTML.
var PluginSubLibrary = object.NewLibrary(RuntimePluginLibraryName, map[string]*object.Builtin{ "serve": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } name, err := args[0].AsString() if err != nil { return err } version := "" if len(args) >= 2 { v, e := args[1].AsString() if e != nil { return e } version = v } description := "" if len(args) >= 3 { d, e := args[2].AsString() if e != nil { return e } description = d } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.plugin.serve() called after start_server() — plugin identity will not be used\n") } RuntimeState.PluginName = name RuntimeState.PluginVersion = version RuntimeState.PluginDescription = description RuntimeState.Unlock() return &object.Null{} }, HelpText: `serve(name, version="", description="") - Declare this script as a plugin server When runtime.start_server() is called in stdio mode the server serves the full Scriptling plugin protocol (scriptling.handshake, function.call, etc.) instead of the plain JSON-RPC loop. Clients can then load the script as a plugin peer with scriptling=True and get auto-generated proxy libraries. Parameters: name (str): Library name (e.g. "myservice"). Clients import it as plugin.<name>. version (str): Optional version string (e.g. "1.0.0"). description (str): Optional human-readable description. Example: import scriptling.runtime.plugin as plugin_srv plugin_srv.serve("calculator", "1.0", "Basic arithmetic operations") plugin_srv.register_function("add", "handlers.add") import scriptling.runtime as runtime runtime.start_server()`, }, "register_function": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } if fn, ok := args[0].(*object.Function); ok { env := evaluator.GetEnvFromContext(ctx) ref := resolveModuleRef(env, fn.Name) if ref == "" { return errors.NewError("cannot determine module name for @plugin.register_function decorator") } RuntimeState.Lock() RuntimeState.PluginFunctions[fn.Name] = ref RuntimeState.Unlock() return fn } name, err := args[0].AsString() if err != nil { return err } if len(args) == 1 { env := evaluator.GetEnvFromContext(ctx) return &object.Builtin{ Fn: func(_ context.Context, _ object.Kwargs, dArgs ...object.Object) object.Object { if len(dArgs) == 0 { return errors.NewError("decorator requires a function") } fn, ok := dArgs[0].(*object.Function) if !ok { return errors.NewError("decorated value must be a function, got %s", dArgs[0].Type()) } ref := resolveModuleRef(env, fn.Name) if ref == "" { return errors.NewError("cannot determine module name for decorator") } RuntimeState.Lock() RuntimeState.PluginFunctions[name] = ref RuntimeState.Unlock() return fn }, } } handler, err := args[1].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.plugin.register_function %q registered after start_server() — function will not be served\n", name) } RuntimeState.PluginFunctions[name] = handler RuntimeState.Unlock() return &object.Null{} }, HelpText: `register_function(name, handler=None) - Register a function for the plugin server, or use as decorator Decorator form: import scriptling.runtime.plugin as plugin @plugin.register_function("add") def add(a, b): return a + b # Bare form uses the function name as the plugin name @plugin.register_function def greet(name): return "hello " + name Imperative form: plugin.register_function("add", "handlers.add")`, }, "register_constant": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 2); err != nil { return err } name, err := args[0].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.plugin.register_constant %q registered after start_server() — constant will not be served\n", name) } RuntimeState.PluginConstants[name] = args[1] RuntimeState.Unlock() return &object.Null{} }, HelpText: `register_constant(name, value) - Register a constant exported by the plugin server Parameters: name (str): Constant name exposed to plugin clients. value (any): Value — any type that the plugin transport can encode (bool, int, float, string, list, dict, None). Constants are included in the handshake schema so clients can read them directly as attributes of the plugin library. Example: import scriptling.runtime.plugin as plugin_srv plugin_srv.register_constant("VERSION", "1.0.0") plugin_srv.register_constant("MAX_RETRIES", 5)`, }, "register_class": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } if cls, ok := args[0].(*object.Class); ok { env := evaluator.GetEnvFromContext(ctx) ref := resolveModuleRef(env, cls.Name) if ref == "" { return errors.NewError("cannot determine module name for @plugin.register_class decorator") } RuntimeState.Lock() RuntimeState.PluginClasses[cls.Name] = ref RuntimeState.Unlock() return cls } handler, err := args[0].AsString() if err != nil { return err } name := handler for i := len(handler) - 1; i >= 0; i-- { if handler[i] == '.' { name = handler[i+1:] break } } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.plugin.register_class %q registered after start_server() — class will not be served\n", name) } RuntimeState.PluginClasses[name] = handler RuntimeState.Unlock() return &object.Null{} }, HelpText: `register_class(handler) - Register a class exported by the plugin server, or use as bare decorator Decorator form: import scriptling.runtime.plugin as plugin @plugin.register_class class Config: def __init__(self): self.version = "1.0" Imperative form: plugin.register_class("handlers.Config") The exposed class name is derived from the class name (decorator) or the last segment of the handler ref (imperative).`, }, }, nil, "Scriptling plugin server — declare this script as a plugin peer with full handshake support")
PluginSubLibrary exposes runtime.plugin for declaring a script as a Scriptling plugin server. When start_server() is called the CLI replaces the plain JSON-RPC loop with a full plugin.Server that handles the plugin handshake, function.call, and object lifecycle. Available in the agent variant of scriptling only.
var RequestClass = &object.Class{ Name: "Request", Methods: map[string]object.Object{ "path_param": requestFieldGetter("path_params", "path_param", false, `path_param(name, default=None) - Get a path parameter captured from a route wildcard Route patterns like "/api/users/{id}" capture the matching request path segments. Returns the captured value, default, or None. Parameters: name (string): Path parameter name default (any, optional): Value returned when the parameter is absent`), "query_param": requestFieldGetter("query", "query_param", false, `query_param(name, default=None) - Get a query parameter Returns the first value of the query parameter, default, or None. Parameters: name (string): Query parameter name default (any, optional): Value returned when the parameter is absent`), "header": requestFieldGetter("headers", "header", true, `header(name, default=None) - Get a request header Header names are case-insensitive. Returns the header value, default, or None. Parameters: name (string): Header name default (any, optional): Value returned when the header is absent`), "json": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } instance, ok := args[0].(*object.Instance) if !ok { return errors.NewError("json() called on non-Request object") } body, err := instance.Field("body").AsString() if err != nil { return err } if body == "" { return &object.Null{} } return conversion.MustParseJSON(body) }, HelpText: `json() - Parse request body as JSON Returns the parsed JSON as a dict or list, or None if body is empty.`, }, }, }
RequestClass is the class for Request objects passed to handlers
var RequestsLibrary = newRequestsLibrary(nil)
var ResponseClass = &object.Class{ Name: "Response", Methods: map[string]object.Object{ "json": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } if instance, ok := args[0].(*object.Instance); ok { if body, err := instance.Field("body").AsString(); err == nil { return conversion.MustParseJSON(body) } } return errors.NewError("json() called on non-Response object") }, HelpText: `json() - Parses the response body as JSON and returns the parsed object`, }, "raise_for_status": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } if instance, ok := args[0].(*object.Instance); ok { if statusCode, err := instance.Field("status_code").AsInt(); err == nil { if statusCode >= 400 { kind := "Client" if statusCode >= 500 { kind = "Server" } return &object.Exception{ ExceptionType: "HTTPError", Message: fmt.Sprintf("HTTPError: %d %s Error", statusCode, kind), } } return &object.Null{} } } return errors.NewError("raise_for_status() called on non-Response object") }, HelpText: `raise_for_status() - Raises an exception if the status code indicates an error`, }, }, }
ResponseClass defines the Response class with its methods
var RuntimeLibraryCore = object.NewLibrary(RuntimeLibraryName, RuntimeLibraryFunctions, nil, "Runtime library for background tasks")
RuntimeLibraryCore is the runtime library without sub-libraries
var RuntimeLibraryFunctions = map[string]*object.Builtin{ "background": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 2); err != nil { return err } name, err := args[0].AsString() if err != nil { return err } handler, err := args[1].AsString() if err != nil { return err } env := getEnvFromContext(ctx) eval := evaliface.FromContext(ctx) shared := false if v := kwargs.Get("shared"); v != nil { if b, e := v.AsBool(); e == nil { shared = b } } daemon := false if v := kwargs.Get("daemon"); v != nil { if b, e := v.AsBool(); e == nil { daemon = b } } handlerKwargs := make(map[string]object.Object, len(kwargs.Kwargs)) for k, v := range kwargs.Kwargs { if k == "shared" || k == "daemon" { continue } handlerKwargs[k] = v } kwargs = object.NewKwargs(handlerKwargs) promise, claimed := claimTaskName(name) if !claimed { return promiseObject(promise) } handedOff := false defer func() { if !handedOff { releaseTaskName(promise) } }() if shared { handedOff = true liveArgs := make([]object.Object, len(args)-2) copy(liveArgs, args[2:]) return startSharedTask(ctx, handler, liveArgs, kwargs.Kwargs, env, eval, daemon, promise) } for i, a := range args[2:] { if err := object.ValidateTransferable(a); err != nil { return errors.NewError("background arg %d: %s", i, err) } } for k, v := range kwargs.Kwargs { if err := object.ValidateTransferable(v); err != nil { return errors.NewError("background kwarg '%s': %s", k, err) } } fnArgs := make([]object.Object, len(args[2:])) for i, a := range args[2:] { fnArgs[i] = object.CloneObject(a) } fnKwargs := make(map[string]object.Object, len(kwargs.Kwargs)) for k, v := range kwargs.Kwargs { fnKwargs[k] = object.CloneObject(v) } RuntimeState.Lock() backgroundReady := RuntimeState.BackgroundReady factory := RuntimeState.BackgroundFactory if !backgroundReady && factory != nil { RuntimeState.Backgrounds[name] = handler RuntimeState.BackgroundArgs[name] = fnArgs RuntimeState.BackgroundKwargs[name] = fnKwargs RuntimeState.BackgroundEnvs[name] = env RuntimeState.BackgroundEvals[name] = eval RuntimeState.BackgroundCtxs[name] = ctx RuntimeState.BackgroundDaemons[name] = daemon RuntimeState.BackgroundPromises[name] = promise } RuntimeState.Unlock() if backgroundReady || factory == nil { handedOff = true return startBackgroundTask(promise, handler, fnArgs, fnKwargs, env, eval, factory, ctx, daemon) } handedOff = true return promiseObject(promise) }, HelpText: `background(name, handler, *args, **kwargs) - Start a fire-and-forget background task Starts a background task in a goroutine and returns immediately. Returns a promise (call .get() or .wait() to await the result), or an error if the handler is not found. In server mode the task is queued until the server starts; the promise resolves once it runs. Both handler patterns run in isolated environments with no access to the calling script's data. Sibling functions, imported modules, and module-level constants (int, float, bool, str) are copied; other module-level variables (lists, dicts, objects, logger instances) are NOT visible — pass them as arguments or via runtime.sync. A task that fails is reported to stderr with its name. The name identifies the task: calling background() with the name of a task that is already queued or running starts nothing and returns the running task's promise. This keeps a module that registers both a request handler and a task from spawning another copy each time it is re-imported to serve a request. Once a task ends its name is free to reuse. Parameters: name (string): Unique name for the background task handler (string): Function name or "library.function" "func_name" - runs in isolated env with import support and sibling functions "lib.func" - loads library.function in a new Scriptling instance *args: Positional arguments to pass to the function **kwargs: Keyword arguments to pass to the function shared (bool): Run in the caller's environment with live shared state instead of an isolated copy (default False) daemon (bool): Do not wait for this task at script exit (default False) Arguments must be transferable types — only simple values and recursively transferable containers are allowed: - Scalars: None, bool, int, float, str - Containers: list, dict, set, tuple (elements must also be transferable) - Not allowed: instances, classes, functions, builtins, or any other runtime-backed objects Arguments are deep-copied before the task starts so the caller and task cannot race on shared state. Returns: null on success, error if handler validation fails Background tasks are fire-and-forget. For coordination between tasks use runtime.sync primitives (Shared, Atomic, Queue, WaitGroup). Access panels via console.Console().panel("name") from background tasks. The scriptling CLI waits for outstanding background tasks before exiting, so a finishing script does not kill them mid-flight and their output and logging are not lost. Pass daemon=True for long-running tasks that must not hold the process open. `, }, "start_server": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { wait := true if v := kwargs.Get("wait"); v != nil { if b, e := v.AsBool(); e == nil { wait = b } } RuntimeState.Lock() if !RuntimeState.ServerStarted && RuntimeState.ServerStartCh != nil { RuntimeState.ServerStarted = true if RuntimeState.ServerCollect != nil { RuntimeState.ServerCollect() } close(RuntimeState.ServerStartCh) } RuntimeState.Unlock() if wait { object.RunBlocking(ctx, func() { RuntimeState.RLock() ch := RuntimeState.ServerRunningCh RuntimeState.RUnlock() if ch != nil { <-ch } }) } return &object.Null{} }, HelpText: `start_server(wait=True) - Signal the server to start accepting requests Signals the server to collect registered routes/methods and begin listening for requests. Call this after all routes are registered. Parameters: wait (bool, default True): If True, blocks until the server shuts down. If False, returns immediately so the script can continue running (e.g. to maintain gossip state or run a polling loop). When wait=True the call blocks until the server receives a shutdown signal (SIGTERM / Ctrl-C). Use wait=False combined with a server_running() loop to stay alive while performing other work: runtime.start_server(wait=False) while runtime.server_running(): yield_now() Backward compatibility: scripts that exit without calling start_server() continue to work — the server starts automatically after the setup script finishes. `, }, "server_running": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { RuntimeState.RLock() ch := RuntimeState.ServerRunningCh RuntimeState.RUnlock() if ch == nil { return object.NewBoolean(false) } select { case <-ch: return object.NewBoolean(false) default: return object.NewBoolean(true) } }, HelpText: `server_running() - Returns True while the server is running Returns True as long as the server has not received a shutdown signal. Returns False once the server is shutting down or if called outside of server mode. Typical usage with start_server(wait=False): runtime.start_server(wait=False) while runtime.server_running(): yield_now() # release GIL on each iteration `, }, }
RuntimeLibraryFunctions contains the core runtime functions (background)
var RuntimeState = struct { sync.RWMutex // HTTP routes Routes map[string]*RouteInfo Middleware string NotFoundHandler string // JSON-RPC stdio methods and notifications (name -> "library.function") JSONRPCMethods map[string]string JSONRPCNotifications map[string]string // WebSocket routes and connections WebSocketRoutes map[string]*WebSocketRouteInfo WebSocketConnections map[string]*WebSocketServerConn // Background tasks Backgrounds map[string]string // name -> "function_name" BackgroundArgs map[string][]object.Object // name -> args BackgroundKwargs map[string]map[string]object.Object // name -> kwargs BackgroundEnvs map[string]*object.Environment // name -> environment BackgroundEvals map[string]evaliface.Evaluator // name -> evaluator BackgroundFactory SandboxFactory // Factory to create new Scriptling instances BackgroundCtxs map[string]context.Context // name -> context BackgroundReady bool // If true, start tasks immediately BackgroundDaemons map[string]bool // name -> daemon flag (queued tasks) BackgroundPromises map[string]*Promise // name -> promise returned to the setup script (queued tasks) // ActiveTasks holds the promise of every background task that is currently // queued or running, keyed by the task name. A task name identifies one // task, so background() with a name that is already active starts nothing // and hands back the running task's promise. The entry is dropped when the // task ends, so a name can be reused for a later task. // // This matters because a module body is re-executed whenever one of its // functions is imported to serve a request: without this, a setup script // that registers both a route handler and a background task in the same // file would spawn a fresh copy of that task on every request. ActiveTasks map[string]*Promise // name -> promise (queued or running) // TaskWG tracks outstanding non-daemon background tasks. Hosts that run // scripts to completion (the CLI's plain-run modes) wait on it before // exiting so fire-and-forget tasks are not killed mid-flight. It is // deliberately NOT reset by ResetRuntime: a WaitGroup's counter cannot be // reset, and Done() calls from still-running tasks of an earlier run must // land on the same counter or they would panic. TaskWG sync.WaitGroup // KV store KVDB *snapshotkv.DB // Sync primitives WaitGroups map[string]*RuntimeWaitGroup Queues map[string]*RuntimeQueue Atomics map[string]*RuntimeAtomic Shareds map[string]*RuntimeShared // Server lifecycle channels (nil in script mode) ServerStartCh chan struct{} // closed by start_server() to signal server is ready ServerRunningCh chan struct{} // closed by server on shutdown ServerStarted bool // prevents double-close of ServerStartCh ServerCollect func() // set by NewServer; called inside start_server() to snapshot routes atomically // Plugin server registration (set via runtime.plugin, agent variant only) PluginName string PluginVersion string PluginDescription string PluginFunctions map[string]string // function name → "library.function" handler PluginConstants map[string]object.Object // constant name → value PluginClasses map[string]string // exposed class name → "library.ClassName" handler // Cleanup functions registered by libraries cleanupFuncs []func() }{ Routes: make(map[string]*RouteInfo), NotFoundHandler: "", JSONRPCMethods: make(map[string]string), JSONRPCNotifications: make(map[string]string), WebSocketRoutes: make(map[string]*WebSocketRouteInfo), WebSocketConnections: make(map[string]*WebSocketServerConn), Backgrounds: make(map[string]string), BackgroundArgs: make(map[string][]object.Object), BackgroundKwargs: make(map[string]map[string]object.Object), BackgroundEnvs: make(map[string]*object.Environment), BackgroundEvals: make(map[string]evaliface.Evaluator), BackgroundFactory: nil, BackgroundCtxs: make(map[string]context.Context), BackgroundReady: false, BackgroundDaemons: make(map[string]bool), BackgroundPromises: make(map[string]*Promise), ActiveTasks: make(map[string]*Promise), KVDB: nil, WaitGroups: make(map[string]*RuntimeWaitGroup), Queues: make(map[string]*RuntimeQueue), Atomics: make(map[string]*RuntimeAtomic), Shareds: make(map[string]*RuntimeShared), ServerStartCh: nil, ServerRunningCh: nil, ServerStarted: false, PluginFunctions: make(map[string]string), PluginConstants: make(map[string]object.Object), PluginClasses: make(map[string]string), }
RuntimeState holds all runtime state
var SecretsLibrary = object.NewLibrary(SecretsLibraryName, map[string]*object.Builtin{ "token_bytes": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { nbytes := 32 if len(args) > 0 { if intVal, ok := args[0].(*object.Integer); ok { nbytes = int(intVal.IntValue()) } } if nbytes < 1 { return errors.NewError("token_bytes requires a positive number of bytes") } bytes := make([]byte, nbytes) _, err := rand.Read(bytes) if err != nil { return errors.NewError("failed to generate random bytes: %s", err.Error()) } elements := make([]object.Object, nbytes) for i, b := range bytes { elements[i] = object.NewInteger(int64(b)) } return &object.List{Elements: elements} }, HelpText: `token_bytes([nbytes]) - Generate nbytes random bytes Parameters: nbytes - Number of bytes to generate (default 32) Returns: List of integers representing bytes Example: import secrets bytes = secrets.token_bytes(16)`, }, "token_hex": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { nbytes := 32 if len(args) > 0 { if intVal, ok := args[0].(*object.Integer); ok { nbytes = int(intVal.IntValue()) } } if nbytes < 1 { return errors.NewError("token_hex requires a positive number of bytes") } bytes := make([]byte, nbytes) _, err := rand.Read(bytes) if err != nil { return errors.NewError("failed to generate random bytes: %s", err.Error()) } return object.NewString(hex.EncodeToString(bytes)) }, HelpText: `token_hex([nbytes]) - Generate random text in hexadecimal Parameters: nbytes - Number of random bytes (string will be 2x this length) (default 32) Returns: Hex string Example: import secrets token = secrets.token_hex(16) # 32 character hex string`, }, "token_urlsafe": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { nbytes := 32 if len(args) > 0 { if intVal, ok := args[0].(*object.Integer); ok { nbytes = int(intVal.IntValue()) } } if nbytes < 1 { return errors.NewError("token_urlsafe requires a positive number of bytes") } bytes := make([]byte, nbytes) _, err := rand.Read(bytes) if err != nil { return errors.NewError("failed to generate random bytes: %s", err.Error()) } encoded := base64.URLEncoding.EncodeToString(bytes) encoded = strings.TrimRight(encoded, "=") return object.NewString(encoded) }, HelpText: `token_urlsafe([nbytes]) - Generate URL-safe random text Parameters: nbytes - Number of random bytes (default 32) Returns: URL-safe base64 encoded string Example: import secrets token = secrets.token_urlsafe(16)`, }, "randbelow": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) != 1 { return errors.NewError("randbelow() requires exactly 1 argument") } n, ok := args[0].(*object.Integer) if !ok { return errors.NewTypeError("INTEGER", args[0].Type().String()) } if n.IntValue() <= 0 { return errors.NewError("randbelow requires a positive upper bound") } result, err := rand.Int(rand.Reader, big.NewInt(n.IntValue())) if err != nil { return errors.NewError("failed to generate random number: %s", err.Error()) } return object.NewInteger(result.Int64()) }, HelpText: `randbelow(n) - Generate a random integer in range [0, n) Parameters: n - Exclusive upper bound (must be positive) Returns: Random integer from 0 to n-1 Example: import secrets dice = secrets.randbelow(6) + 1 # 1-6`, }, "randbits": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) != 1 { return errors.NewError("randbits() requires exactly 1 argument") } k, ok := args[0].(*object.Integer) if !ok { return errors.NewTypeError("INTEGER", args[0].Type().String()) } if k.IntValue() < 1 { return errors.NewError("randbits requires a positive number of bits") } result, err := rand.Int(rand.Reader, big.NewInt(0).Lsh(big.NewInt(1), uint(k.IntValue()))) if err != nil { return errors.NewError("failed to generate random bits: %s", err.Error()) } return object.NewInteger(result.Int64()) }, HelpText: `randbits(k) - Generate a random integer with k random bits Parameters: k - Number of random bits (must be positive) Returns: Random integer with k bits Example: import secrets random_int = secrets.randbits(8) # 0-255`, }, "choice": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) != 1 { return errors.NewError("choice() requires exactly 1 argument") } if str, ok := args[0].(*object.String); ok { if len(str.StringValue()) == 0 { return errors.NewError("cannot choose from empty sequence") } idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(str.StringValue())))) if err != nil { return errors.NewError("failed to generate random index: %s", err.Error()) } return object.NewString(string(str.StringValue()[idx.Int64()])) } list, ok := args[0].(*object.List) if !ok { return errors.NewTypeError("LIST or STRING", args[0].Type().String()) } if len(list.Elements) == 0 { return errors.NewError("cannot choose from empty sequence") } idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(list.Elements)))) if err != nil { return errors.NewError("failed to generate random index: %s", err.Error()) } return list.Elements[idx.Int64()] }, HelpText: `choice(sequence) - Return a random element from sequence Parameters: sequence - Non-empty list or string to choose from Returns: Random element from the sequence Example: import secrets item = secrets.choice(["apple", "banana", "cherry"]) char = secrets.choice("abcdef")`, }, "compare_digest": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) != 2 { return errors.NewError("compare_digest() requires exactly 2 arguments") } a, okA := args[0].(*object.String) b, okB := args[1].(*object.String) if !okA || !okB { return errors.NewError("compare_digest() requires two string arguments") } return object.NewBoolean(stdlib.CompareDigest(a.StringValue(), b.StringValue())) }, HelpText: `compare_digest(a, b) - Compare two strings using constant-time comparison This function is designed to prevent timing attacks when comparing secret values. Parameters: a - First string b - Second string Returns: True if strings are equal, False otherwise Example: import secrets secrets.compare_digest(user_token, stored_token)`, }, }, nil, "Cryptographically strong random number generation (extended library)")
SecretsLibrary provides cryptographically strong random number generation NOTE: This is an extended library and not enabled by default
var SubprocessLibrary = object.NewLibrary(SubprocessLibraryName, map[string]*object.Builtin{ "run": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } // Parse args - can be string or list var cmdArgs []string var cmdStr string if args[0].Type() == object.STRING_OBJ { cmdStr, _ = args[0].AsString() shell := false if sh, exists := kwargs.Kwargs["shell"]; exists { if b, ok := sh.(*object.Boolean); ok { shell = b.BoolValue() } } if shell { cmdArgs = []string{cmdStr} } else { cmdArgs = strings.Fields(cmdStr) } } else if args[0].Type() == object.LIST_OBJ { list, _ := args[0].AsList() cmdArgs = make([]string, len(list)) for i, arg := range list { if str, err := arg.AsString(); err == nil { cmdArgs[i] = str } else { return errors.NewTypeError("STRING", arg.Type().String()) } } } else { return errors.NewTypeError("STRING or LIST", args[0].Type().String()) } captureOutput := false shell := false cwd := "" timeout := 0.0 check := false text := false encoding := "utf-8" inputData := "" env := make(map[string]string) if capture, exists := kwargs.Kwargs["capture_output"]; exists { if b, ok := capture.(*object.Boolean); ok { captureOutput = b.BoolValue() } } if sh, exists := kwargs.Kwargs["shell"]; exists { if b, ok := sh.(*object.Boolean); ok { shell = b.BoolValue() } } if wd, exists := kwargs.Kwargs["cwd"]; exists { if s, ok := wd.(*object.String); ok { cwd = s.StringValue() } } if to, exists := kwargs.Kwargs["timeout"]; exists { if f, ok := to.(*object.Float); ok { timeout = f.FloatValue() } else if i, ok := to.(*object.Integer); ok { timeout = float64(i.IntValue()) } } if ch, exists := kwargs.Kwargs["check"]; exists { if b, ok := ch.(*object.Boolean); ok { check = b.BoolValue() } } if txt, exists := kwargs.Kwargs["text"]; exists { if b, ok := txt.(*object.Boolean); ok { text = b.BoolValue() } } if enc, exists := kwargs.Kwargs["encoding"]; exists { if s, ok := enc.(*object.String); ok { encoding = s.StringValue() } } if inp, exists := kwargs.Kwargs["input"]; exists { if s, ok := inp.(*object.String); ok { inputData = s.StringValue() } } if envDict, exists := kwargs.Kwargs["env"]; exists { if d, ok := envDict.(*object.Dict); ok { for _, pair := range d.Pairs { if valStr, ok := pair.Value.(*object.String); ok { env[pair.StringKey()] = valStr.StringValue() } } } } if shell && args[0].Type() == object.STRING_OBJ { cmdArgs = []string{"sh", "-c", cmdStr} } // Execute command var cmd *exec.Cmd if shell && args[0].Type() == object.STRING_OBJ { cmd = exec.Command(cmdArgs[0], cmdArgs[1:]...) } else { cmd = exec.Command(cmdArgs[0], cmdArgs[1:]...) } if cwd != "" { cmd.Dir = cwd } if len(env) > 0 { cmd.Env = make([]string, 0, len(env)) for k, v := range env { cmd.Env = append(cmd.Env, k+"="+v) } } if inputData != "" { cmd.Stdin = strings.NewReader(inputData) } if timeout > 0 { ctx, cancel := context.WithTimeout(ctx, time.Duration(timeout*float64(time.Second))) defer cancel() cmd = exec.CommandContext(ctx, cmd.Path, cmd.Args[1:]...) cmd.Dir = cwd if len(env) > 0 { cmd.Env = make([]string, 0, len(env)) for k, v := range env { cmd.Env = append(cmd.Env, k+"="+v) } } if inputData != "" { cmd.Stdin = strings.NewReader(inputData) } } var stdout, stderr []byte var err error object.RunBlocking(ctx, func() { if captureOutput { stdout, err = cmd.Output() if exitErr, ok := err.(*exec.ExitError); ok { stderr = exitErr.Stderr } } else { err = cmd.Run() } }) returncode := 0 if err != nil { if exitErr, ok := err.(*exec.ExitError); ok { returncode = exitErr.ExitCode() } else { return errors.NewError("Command execution failed: %v", err) } } // Convert output based on text/encoding settings var stdoutStr, stderrStr string if text { _ = encoding stdoutStr = string(stdout) stderrStr = string(stderr) } else { stdoutStr = string(stdout) stderrStr = string(stderr) } instance := object.NewInstanceWithFields(CompletedProcessClass, map[string]object.Object{ "args": &object.List{Elements: make([]object.Object, len(cmdArgs))}, "returncode": object.NewInteger(int64(returncode)), "stdout": object.NewString(stdoutStr), "stderr": object.NewString(stderrStr), }) for i, arg := range cmdArgs { instance.Field("args").(*object.List).Elements[i] = object.NewString(arg) } if check && returncode != 0 { return errors.NewError("Command returned non-zero exit status %d", returncode) } return instance }, HelpText: `run(args, options={}) - Run a command Runs a command and returns a CompletedProcess instance. Parameters: args (string or list): Command to run. If string, split on spaces. If list, each element is an argument. options (dict, optional): Options - capture_output (bool): Capture stdout and stderr (default: false) - shell (bool): Run command through shell (default: false) - cwd (string): Working directory for command - timeout (int): Timeout in seconds - check (bool): Raise exception if returncode is non-zero Returns: CompletedProcess instance with args, returncode, stdout, stderr`, }, }, map[string]object.Object{}, "Subprocess library for running external commands")
var SyncSubLibrary = object.NewLibrary(RuntimeSyncLibraryName, map[string]*object.Builtin{ "WaitGroup": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } name, err := args[0].AsString() if err != nil { return err } RuntimeState.Lock() wg, exists := RuntimeState.WaitGroups[name] if !exists { wg = &RuntimeWaitGroup{} RuntimeState.WaitGroups[name] = wg } RuntimeState.Unlock() return &object.Builtin{ Attributes: map[string]object.Object{ "add": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { delta := int64(1) if len(args) > 0 { if d, err := args[0].AsInt(); err == nil { delta = d } } wg.wg.Add(int(delta)) return &object.Null{} }, HelpText: "add(delta=1) - Add to the wait group counter", }, "done": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { wg.wg.Done() return &object.Null{} }, HelpText: "done() - Decrement the wait group counter", }, "wait": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { object.RunBlocking(ctx, func() { wg.wg.Wait() }) return &object.Null{} }, HelpText: "wait() - Block until counter reaches zero", }, }, HelpText: "WaitGroup - Go-style synchronization primitive", } }, HelpText: `WaitGroup(name) - Get or create a named wait group Parameters: name (string): Unique name for the wait group (shared across environments) Example: wg = runtime.sync.WaitGroup("tasks") def worker(id): print(f"Worker {id}") wg.done() for i in range(10): wg.add(1) runtime.run(worker, i) wg.wait()`, }, "Queue": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } name, err := args[0].AsString() if err != nil { return err } maxsize := 0 if len(args) > 1 { if m, err := args[1].AsInt(); err == nil { maxsize = int(m) } } if m, ok := kwargs.Kwargs["maxsize"]; ok { if mInt, err := m.AsInt(); err == nil { maxsize = int(mInt) } } RuntimeState.Lock() queue, exists := RuntimeState.Queues[name] if !exists { queue = newRuntimeQueue(maxsize) RuntimeState.Queues[name] = queue } RuntimeState.Unlock() return &object.Builtin{ Attributes: map[string]object.Object{ "put": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } if err := queue.put(ctx, args[0]); err != nil { return errors.NewError("queue error: %v", err) } return &object.Null{} }, HelpText: "put(item) - Add item to queue (blocks if full, respects context timeout)", }, "get": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { item, err := queue.get(ctx) if err != nil { return errors.NewError("queue error: %v", err) } return item }, HelpText: "get() - Remove and return item from queue (blocks if empty, respects context timeout)", }, "size": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return object.NewInteger(int64(queue.size())) }, HelpText: "size() - Return number of items in queue", }, "close": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { queue.close() return &object.Null{} }, HelpText: "close() - Close the queue", }, }, HelpText: "Queue - Thread-safe queue for producer-consumer patterns", } }, HelpText: `Queue(name, maxsize=0) - Get or create a named queue Parameters: name (string): Unique name for the queue (shared across environments) maxsize (int): Maximum queue size (0 = unbounded) Example: queue = runtime.sync.Queue("jobs", maxsize=100) def producer(): for i in range(10): queue.put(i) def consumer(): for i in range(10): item = queue.get() print(item) runtime.run(producer) runtime.run(consumer)`, }, "Atomic": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } name, err := args[0].AsString() if err != nil { return err } initial := int64(0) if len(args) > 1 { if i, err := args[1].AsInt(); err == nil { initial = i } } if i := kwargs.Get("initial"); i != nil { if iVal, err := i.AsInt(); err == nil { initial = iVal } } RuntimeState.Lock() atomic, exists := RuntimeState.Atomics[name] if !exists { atomic = &RuntimeAtomic{value: initial} RuntimeState.Atomics[name] = atomic } RuntimeState.Unlock() return &object.Builtin{ Attributes: map[string]object.Object{ "add": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { delta := int64(1) if len(args) > 0 { if d, err := args[0].AsInt(); err == nil { delta = d } else { return errors.NewTypeError("INTEGER", args[0].Type().String()) } } newVal := atomic.add(delta) return object.NewInteger(newVal) }, HelpText: "add(delta=1) - Atomically add delta and return new value", }, "get": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return object.NewInteger(atomic.get()) }, HelpText: "get() - Atomically read the value", }, "set": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } if val, err := args[0].AsInt(); err == nil { atomic.set(val) return &object.Null{} } return errors.NewTypeError("INTEGER", args[0].Type().String()) }, HelpText: "set(value) - Atomically set the value", }, }, HelpText: "Atomic integer - lock-free operations", } }, HelpText: `Atomic(name, initial=0) - Get or create a named atomic counter Parameters: name (string): Unique name for the counter (shared across environments) initial (int): Initial value (only used if creating new counter) Example: counter = runtime.sync.Atomic("requests", initial=0) counter.add(1) # Atomic increment counter.add(-5) # Atomic add counter.set(100) # Atomic set value = counter.get() # Atomic read`, }, "Shared": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } name, err := args[0].AsString() if err != nil { return err } var initial object.Object = &object.Null{} if len(args) > 1 { initial = args[1] } if i := kwargs.Get("initial"); i != nil { initial = i } RuntimeState.Lock() shared, exists := RuntimeState.Shareds[name] if !exists { shared = &RuntimeShared{value: initial} RuntimeState.Shareds[name] = shared } RuntimeState.Unlock() return &object.Builtin{ Attributes: map[string]object.Object{ "get": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return shared.get() }, HelpText: "get() - Get the current value (thread-safe read)", }, "set": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } shared.set(args[0]) return &object.Null{} }, HelpText: "set(value) - Set the value (thread-safe write)", }, "update": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } fn := args[0] result := shared.update(func(current object.Object) object.Object { eval := evaliface.FromContext(ctx) if eval == nil { return current } env := getEnvFromContext(ctx) return eval.CallObjectFunction(ctx, fn, []object.Object{current}, nil, env) }) return result }, HelpText: "update(fn) - Atomically read-modify-write: fn receives current value, returns new value", }, }, HelpText: "Shared variable - thread-safe access with get()/set()/update()", } }, HelpText: `Shared(name, initial) - Get or create a named shared variable Parameters: name (string): Unique name for the variable (shared across environments) initial: Initial value (only used if creating new variable) Note: Values should be treated as immutable. Use set() to replace, or update() for atomic read-modify-write operations. Example: counter = runtime.sync.Shared("counter", 0) def increment(current): return current + 1 # Atomic increment using update() counter.update(increment) # Simple get/set for immutable values counter.set(42) value = counter.get()`, }, }, nil, "Cross-environment named concurrency primitives")
var TOMLLibrary = object.NewLibrary(TOMLLibraryName, map[string]*object.Builtin{ "loads": { Fn: tomlLoadsFunc, HelpText: `loads(toml_string) - Parse TOML string Parses a TOML string and returns the corresponding Scriptling object. This function is compatible with Python's tomllib.loads() from Python 3.11+. Example: import toml data = toml.loads("[database]\nhost = \"localhost\"\nport = 5432") print(data["database"]["host"])`, }, "dumps": { Fn: tomlDumpsFunc, HelpText: `dumps(obj) - Convert Scriptling object to TOML string Converts a Scriptling object to a TOML formatted string. Note: Python's tomllib does not include a write function. This follows the convention of the tomli-w library which provides dumps(). Example: import toml data = {"database": {"host": "localhost", "port": 5432}} toml_str = toml.dumps(data) print(toml_str)`, }, }, nil, "TOML parsing and generation")
TOMLLibrary provides TOML parsing and generation functionality
var TemplateHTMLLibrary = object.NewLibrary(TemplateHTMLLibraryName, map[string]*object.Builtin{ "Set": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } left, errObj := kwargs.GetString("left", "") if errObj != nil { return errObj } right, errObj := kwargs.GetString("right", "") if errObj != nil { return errObj } t := htmltemplate.New("").Delims(left, right) return newSetInstance(&parsedTemplateSet{html: t}) }, HelpText: `Set(left="", right="") - Create a new HTML template set (uses html/template with auto-escaping) Parameters: left (str, optional): Left action delimiter, default "{{" right (str, optional): Right action delimiter, default "}}" Returns: Set: A template set with add(source) and render([name,] data) methods Example: import scriptling.template.html as html # Simple template tmpl = html.Set() tmpl.add("Hello, {{.Name}}!") print(tmpl.render({"Name": "Alice"})) # With partials tmpl = html.Set() tmpl.add('{{define "header"}}<h1>{{.Title}}</h1>{{end}}') tmpl.add('{{define "page"}}{{template "header" .}}<p>{{.Body}}</p>{{end}}') print(tmpl.render("page", {"Title": "Home", "Body": "Welcome"})) # Custom delimiters, e.g. {% %} to avoid clashing with literal {{ }} tmpl = html.Set(left="{%", right="%}") tmpl.add("<p>{%.Name%}</p>")`, }, }, map[string]object.Object{}, "Go html/template rendering with automatic HTML escaping")
TemplateHTMLLibrary provides html/template rendering with automatic HTML escaping
var TemplateSetClass = &object.Class{ Name: "Set", Methods: map[string]object.Object{ "add": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } self, ok := args[0].(*object.Instance) if !ok { return errors.NewError("add() called on non-Set object") } src, err := args[1].AsString() if err != nil { return err } pt := getTemplateSet(self) if pt == nil { return errors.NewError("Set not initialised") } if pt.html != nil { if _, parseErr := pt.html.Parse(src); parseErr != nil { return errors.NewError("template parse error: %s", parseErr.Error()) } } else { if _, parseErr := pt.text.Parse(src); parseErr != nil { return errors.NewError("template parse error: %s", parseErr.Error()) } } return &object.Null{} }, HelpText: `add(source) - Add a template source to the set Parameters: source (string): Template source, may contain {{define "name"}}...{{end}} blocks Example: tmpl = html.Set() tmpl.add('{{define "header"}}<h1>{{.Title}}</h1>{{end}}') tmpl.add('{{define "page"}}{{template "header" .}}<p>{{.Body}}</p>{{end}}')`, }, "render": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 3); err != nil { return err } self, ok := args[0].(*object.Instance) if !ok { return errors.NewError("render() called on non-Set object") } // render(data) or render(name, data) var name string var data object.Object = &object.Null{} switch len(args) { case 2: data = args[1] case 3: n, err := args[1].AsString() if err != nil { return err } name = n data = args[2] } pt := getTemplateSet(self) if pt == nil { return errors.NewError("Set not initialised") } result, execErr := renderTemplateSet(pt, name, conversion.ToGo(data)) if execErr != nil { return errors.NewError("template render error: %s", execErr.Error()) } return object.NewString(result) }, HelpText: `render(data) or render(name, data) - Render a template from the set Parameters: name (string, optional): Name of the template to render (from {{define "name"}}) data (dict): Template data Returns: string: Rendered output Example: # Anonymous / single template tmpl.render({"Name": "Alice"}) # Named template tmpl.render("page", {"Title": "Home", "Body": "Welcome"})`, }, }, }
TemplateSetClass is the class for Set objects
var TemplateTextLibrary = object.NewLibrary(TemplateTextLibraryName, map[string]*object.Builtin{ "Set": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } left, errObj := kwargs.GetString("left", "") if errObj != nil { return errObj } right, errObj := kwargs.GetString("right", "") if errObj != nil { return errObj } t := texttemplate.New("").Delims(left, right) return newSetInstance(&parsedTemplateSet{text: t}) }, HelpText: `Set(left="", right="") - Create a new text template set (uses text/template, no HTML escaping) Parameters: left (str, optional): Left action delimiter, default "{{" right (str, optional): Right action delimiter, default "}}" Returns: Set: A template set with add(source) and render([name,] data) methods Example: import scriptling.template.text as text # Simple template tmpl = text.Set() tmpl.add("Hello, {{.Name}}!") print(tmpl.render({"Name": "Alice"})) # With partials tmpl = text.Set() tmpl.add('{{define "greeting"}}Hello, {{.Name}}!{{end}}') tmpl.add('{{define "email"}}{{template "greeting" .}}\n\nYour order is ready.{{end}}') print(tmpl.render("email", {"Name": "Alice"})) # Custom delimiters, e.g. {% %} to avoid clashing with literal {{ }} tmpl = text.Set(left="{%", right="%}") tmpl.add("Hello, {%.Name%}!")`, }, }, map[string]object.Object{}, "Go text/template rendering with no escaping")
TemplateTextLibrary provides text/template rendering with no escaping
var WaitForLibrary = newWaitForLibrary(nil)
WaitForLibrary is the wait_for library (no network policy)
var WebSocketClientClass = &object.Class{ Name: "WebSocketClient", Methods: map[string]object.Object{ "connected": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } instance, ok := args[0].(*object.Instance) if !ok { return errors.NewError("connected() called on non-WebSocketClient object") } conn := getWSConnFromInstance(instance) if conn == nil { return object.NewBoolean(false) } return object.NewBoolean(conn.IsConnected()) }, HelpText: `connected() - Check if the WebSocket connection is still open Returns True if connected, False otherwise.`, }, "receive": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } instance, ok := args[0].(*object.Instance) if !ok { return errors.NewError("receive() called on non-WebSocketClient object") } timeout := 30.0 if t := kwargs.Get("timeout"); t != nil { if timeoutFloat, e := t.AsFloat(); e == nil { timeout = timeoutFloat } } conn := getWSConnFromInstance(instance) if conn == nil { return &object.Null{} } msgType, data, err := conn.ReadWithTimeout(time.Duration(timeout * float64(time.Second))) if err != nil || data == nil { return &object.Null{} } if msgType == websocket.TextMessage { return object.NewString(string(data)) } elements := make([]object.Object, len(data)) for i, b := range data { elements[i] = object.NewInteger(int64(b)) } return &object.List{Elements: elements} }, HelpText: `receive(timeout=30) - Receive a message from the WebSocket Parameters: timeout (number, optional): Timeout in seconds (default: 30) Returns: string for text messages, list of bytes for binary, or None on timeout/disconnect`, }, "send": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 2); err != nil { return err } instance, ok := args[0].(*object.Instance) if !ok { return errors.NewError("send() called on non-WebSocketClient object") } msg := args[1] var data []byte if dict, ok := msg.(*object.Dict); ok { jsonData, jsonErr := json.Marshal(conversion.ToGo(dict)) if jsonErr != nil { return errors.NewError("failed to encode JSON: %s", jsonErr.Error()) } data = jsonData } else if str, ok := msg.(*object.String); ok { data = []byte(str.StringValue()) } else { strVal, coerceErr := object.CoerceWireString(msg) if coerceErr != nil { return errors.NewError("message must be string or dict") } data = []byte(strVal) } conn := getWSConnFromInstance(instance) if conn == nil { return errors.NewError("connection closed") } if writeErr := conn.WriteMessage(websocket.TextMessage, data); writeErr != nil { return errors.NewError("send failed: %s", writeErr.Error()) } return &object.Null{} }, HelpText: `send(message) - Send a message to the WebSocket client Parameters: message (string or dict): Message to send. Dicts are automatically JSON encoded.`, }, "send_binary": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 2); err != nil { return err } instance, ok := args[0].(*object.Instance) if !ok { return errors.NewError("send_binary() called on non-WebSocketClient object") } list, ok := args[1].(*object.List) if !ok { return errors.NewError("send_binary requires a list of bytes") } data := make([]byte, len(list.Elements)) for i, elem := range list.Elements { b, e := elem.AsInt() if e != nil || b < 0 || b > 255 { return errors.NewError("send_binary requires list of bytes (0-255)") } data[i] = byte(b) } conn := getWSConnFromInstance(instance) if conn == nil { return errors.NewError("connection closed") } if writeErr := conn.WriteMessage(websocket.BinaryMessage, data); writeErr != nil { return errors.NewError("send_binary failed: %s", writeErr.Error()) } return &object.Null{} }, HelpText: `send_binary(data) - Send binary data to the WebSocket client Parameters: data (list): List of byte values (0-255)`, }, "close": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } instance, ok := args[0].(*object.Instance) if !ok { return errors.NewError("close() called on non-WebSocketClient object") } conn := getWSConnFromInstance(instance) if conn != nil { conn.Close() } return &object.Null{} }, HelpText: `close() - Close the WebSocket connection`, }, }, }
WebSocketClientClass is the class for WebSocket client objects passed to handlers
var WebSocketLibrary = newWebSocketLibrary(nil)
WebSocketLibrary is the WebSocket client library (no network policy)
var YAMLLibrary = object.NewLibrary(YAMLLibraryName, map[string]*object.Builtin{ "load": { Fn: yamlLoadFunc, HelpText: `load(yaml_string) - Parse YAML string (deprecated, use safe_load) Parses a YAML string and returns the corresponding Scriptling object. Alias for safe_load(). Both functions are identical and safe in Scriptling. Note: In PyYAML, load() is deprecated. Use safe_load() instead. Example: import yaml data = yaml.safe_load("name: John\nage: 30") print(data["name"])`, }, "safe_load": { Fn: yamlLoadFunc, HelpText: `safe_load(yaml_string) - Safely parse YAML string Safely parses a YAML string and returns the corresponding Scriptling object. Example: import yaml data = yaml.safe_load("name: John\nage: 30") print(data["name"])`, }, "dump": { Fn: yamlDumpFunc, HelpText: `dump(obj) - Convert Scriptling object to YAML string (use safe_dump) Converts a Scriptling object to a YAML string. Alias for safe_dump(). Both functions are identical in Scriptling. Example: import yaml data = {"name": "John", "age": 30} yaml_str = yaml.safe_dump(data) print(yaml_str)`, }, "safe_dump": { Fn: yamlDumpFunc, HelpText: `safe_dump(obj) - Safely convert Scriptling object to YAML string Safely converts a Scriptling object to a YAML string. Example: import yaml data = {"name": "John", "age": 30} yaml_str = yaml.safe_dump(data) print(yaml_str)`, }, }, nil, "YAML parsing and generation")
YAMLLibrary provides YAML parsing and generation functionality
Functions ¶
func CloseKVStore ¶ added in v0.2.22
func CloseKVStore()
CloseKVStore closes the system-wide default KV store.
func CreateJSONRPCErrorInstance ¶ added in v0.12.0
CreateJSONRPCErrorInstance creates a JSON-RPC error object from Go code.
func CreateRequestInstance ¶
func CreateRequestInstance(method, path, body string, headers, query, pathParams map[string]string, remoteAddr string) *object.Instance
CreateRequestInstance creates a new Request instance with the given data. pathParams holds values captured from route wildcards ("{id}" and "{path...}") and remoteAddr the client address; both are exposed as request fields (path_params, remote_addr).
func CreateWebSocketClientInstance ¶ added in v0.4.1
func CreateWebSocketClientInstance(conn *WebSocketServerConn) *object.Instance
CreateWebSocketClientInstance creates a new WebSocketClient instance
func EditFile ¶ added in v0.17.1
EditFile performs a targeted search-and-replace on a single file: it finds the exact `search` text, verifies it appears exactly once, and replaces it with `replace`. The modification is written atomically (temp file + rename), matching sed's in-place edit semantics.
Unlike SedReplace (which replaces every occurrence line-by-line), EditFile operates on the full file content and requires the match to be unique — the gold standard for coding-agent edits where "replace all" is dangerous.
search and replace may span multiple lines. Returns the number of bytes written.
func Find ¶ added in v0.17.1
Find returns the paths under root that match the given filters. It uses the same concurrent walker as scriptling.find. Paths are returned in arbitrary order. The root itself is never included in the result.
func InitKVStore ¶ added in v0.2.22
InitKVStore initializes the system-wide default KV store. If path is empty, the store operates in memory-only mode.
func IsJSONRPCError ¶ added in v0.12.0
IsJSONRPCError reports whether obj is a JSONRPCError instance.
func KVStoreDB ¶ added in v0.2.23
func KVStoreDB(store object.Object) *snapshotkv.DB
KVStoreDB returns the underlying snapshotkv.DB for a kv store object, or nil if the object is not a kv store.
func NewCsvLibrary ¶ added in v0.17.0
func NewFindLibrary ¶ added in v0.17.0
func NewFindLibrary(config fssecurity.Config) *object.Library
NewFindLibrary creates a new scriptling.find library with the given configuration.
func NewGlobLibrary ¶
func NewGlobLibrary(config fssecurity.Config) *object.Library
NewGlobLibrary creates a new Glob library with the given configuration.
func NewGrepLibrary ¶ added in v0.5.7
func NewGrepLibrary(config fssecurity.Config) *object.Library
NewGrepLibrary creates a new scriptling.grep library with the given configuration.
func NewInputBuiltin ¶ added in v0.2.6
NewInputBuiltin returns an input() builtin backed by the given reader. Callers that manage their own Scriptling instance can use this to inject input() directly via SetObjectVar when the reader is known at a different point than RegisterSysLibrary.
func NewKVSubLibrary ¶ added in v0.2.22
NewKVSubLibrary builds the kv sub-library with no path restrictions. Must be called after InitKVStore so RuntimeState.KVDB is set.
func NewKVSubLibraryWithSecurity ¶ added in v0.2.23
NewKVSubLibraryWithSecurity builds the kv sub-library restricted to allowedPaths. In-memory stores are always permitted. If allowedPaths is nil, all paths are allowed. If allowedPaths is an empty slice, all filesystem paths are denied. Must be called after InitKVStore so RuntimeState.KVDB is set.
func NewOSLibrary ¶
NewOSLibrary creates a new OS library with the given configuration. The returned libraries are for "os" and "os.path". Prefer using RegisterOSLibrary which handles registration automatically.
func NewPathlibLibrary ¶
func NewPathlibLibrary(config fssecurity.Config) *object.Library
NewPathlibLibrary creates a new Pathlib library with the given configuration.
func NewSandboxLibrary ¶ added in v0.1.1
NewSandboxLibrary creates a new sandbox library with the given allowed paths. If allowedPaths is nil, all paths are allowed (no restrictions). If allowedPaths is empty slice, no paths are allowed (deny all).
func NewSecretLibrary ¶ added in v0.5.6
func NewSecretLibrary(registry *secretprovider.Registry) *object.Library
NewSecretLibrary creates the scriptling.secret library.
func NewSedLibrary ¶ added in v0.5.8
func NewSedLibrary(config fssecurity.Config) *object.Library
NewSedLibrary creates a new scriptling.sed library with the given configuration.
func NewShlexLibrary ¶ added in v0.17.0
NewShlexLibrary creates a new shlex library.
func NewShutilLibrary ¶ added in v0.17.0
func NewShutilLibrary(config fssecurity.Config) *object.Library
NewShutilLibrary creates a new shutil library with the given configuration.
func NewSysLibrary ¶
NewSysLibrary creates a new sys library with the given argv and optional stdin reader.
func NewTarfileLibrary ¶ added in v0.17.0
func NewTarfileLibrary(config fssecurity.Config) *object.Library
func NewTempfileLibrary ¶ added in v0.17.0
func NewTempfileLibrary(config fssecurity.Config) *object.Library
NewTempfileLibrary creates a new tempfile library with the given configuration.
func NewXmlLibrary ¶ added in v0.17.0
func NewZipfileLibrary ¶ added in v0.17.0
func NewZipfileLibrary(config fssecurity.Config) *object.Library
func RegisterCleanup ¶ added in v0.2.22
func RegisterCleanup(fn func())
RegisterCleanup registers a function to be called during ResetRuntime. Libraries use this to clean up their own state without creating dependencies between packages.
func RegisterCsvLibrary ¶ added in v0.17.0
func RegisterCsvLibrary(registrar object.LibraryRegistrar)
RegisterCsvLibrary registers the scriptling.csv library.
func RegisterFSLibrary ¶ added in v0.6.2
func RegisterFSLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
func RegisterFindLibrary ¶ added in v0.17.0
func RegisterFindLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
RegisterFindLibrary registers the scriptling.find library with a Scriptling instance. If allowedPaths is nil, all paths are allowed. If non-nil, all find operations are restricted to those directories (same semantics as RegisterGrepLibrary).
func RegisterGlobLibrary ¶
func RegisterGlobLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
RegisterGlobLibrary registers the glob library with a Scriptling instance. If allowedPaths is empty or nil, all paths are allowed (no restrictions). If allowedPaths contains paths, all glob operations are restricted to those directories.
SECURITY: When running untrusted scripts, ALWAYS provide allowedPaths to restrict file system access. The security checks prevent: - Reading files outside allowed directories - Path traversal attacks (../../../etc/passwd) - Symlink attacks (symlinks pointing outside allowed dirs)
Example:
No restrictions - full filesystem access (DANGEROUS for untrusted code)
extlibs.RegisterGlobLibrary(s, nil)
Restricted to specific directories (SECURE)
extlibs.RegisterGlobLibrary(s, []string{"/tmp/sandbox", "/home/user/data"})
func RegisterGrepLibrary ¶ added in v0.5.7
func RegisterGrepLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
RegisterGrepLibrary registers the scriptling.grep library with a Scriptling instance. If allowedPaths is nil, all paths are allowed. If non-nil, operations are restricted to those directories (same semantics as RegisterOSLibrary).
func RegisterLoggingLibrary ¶
func RegisterLoggingLibrary(registrar interface{ RegisterLibrary(*object.Library) }, loggerInstance logger.Logger)
RegisterLoggingLibrary registers the logging library with the given registrar and optional logger Each environment gets its own logger instance
func RegisterLoggingLibraryDefault ¶
RegisterLoggingLibraryDefault registers the logging library with default configuration
func RegisterMarkdownLibrary ¶ added in v0.11.3
func RegisterOSLibrary ¶
func RegisterOSLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
RegisterOSLibrary registers the os and os.path libraries with a Scriptling instance. If allowedPaths is empty or nil, all paths are allowed (no restrictions). If allowedPaths contains paths, all file operations are restricted to those directories.
SECURITY: When running untrusted scripts, ALWAYS provide allowedPaths to restrict file system access. The security checks prevent: - Reading/writing files outside allowed directories - Path traversal attacks (../../../etc/passwd) - Symlink attacks (symlinks pointing outside allowed dirs)
Example:
No restrictions - full filesystem access (DANGEROUS for untrusted code)
extlibs.RegisterOSLibrary(s, nil)
Restricted to specific directories (SECURE)
extlibs.RegisterOSLibrary(s, []string{"/tmp/sandbox", "/home/user/data"})
func RegisterPathlibLibrary ¶
func RegisterPathlibLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
RegisterPathlibLibrary registers the pathlib library with a Scriptling instance.
func RegisterRequestsLibrary ¶
func RegisterRequestsLibrary(registrar interface{ RegisterLibrary(*object.Library) }, cfg ...*netsecurity.Config)
func RegisterRuntimeJSONRPCLibrary ¶ added in v0.12.0
RegisterRuntimeJSONRPCLibrary registers only the jsonrpc sub-library.
func RegisterRuntimeKVLibraryWithSecurity ¶ added in v0.2.23
func RegisterRuntimeKVLibraryWithSecurity(registrar interface{ RegisterLibrary(*object.Library) }, allowedPaths []string)
RegisterRuntimeKVLibraryWithSecurity registers the kv library restricted to allowedPaths. In-memory stores are always permitted regardless of allowedPaths. If allowedPaths is nil, all paths are allowed. If empty slice, all filesystem paths are denied.
func RegisterRuntimeLibrary ¶
RegisterRuntimeLibrary registers only the core runtime library (background function). Sub-libraries (http, kv, sync) must be registered separately if needed.
func RegisterRuntimeLibraryAll ¶
func RegisterRuntimeLibraryAll(registrar interface{ RegisterLibrary(*object.Library) }, allowedPaths []string)
RegisterRuntimeLibraryAll registers the runtime library with all sub-libraries, including sandbox with the specified allowed paths for exec_file restrictions. If allowedPaths is nil, all paths are allowed (no restrictions). If allowedPaths is empty slice, no paths are allowed (deny all).
func RegisterRuntimeMCPLibrary ¶ added in v0.18.0
RegisterRuntimeMCPLibrary registers only the runtime.mcp sub-library.
func RegisterRuntimePluginLibrary ¶ added in v0.15.0
RegisterRuntimePluginLibrary registers the plugin sub-library and exposes it as runtime.plugin on the parent library so that `import scriptling.runtime as rt; rt.plugin.serve(...)` works. Call this AFTER RegisterRuntimeLibraryAll. Intentionally not included in RegisterRuntimeLibraryAll — available only for the agent variant.
func RegisterRuntimeSandboxLibrary ¶
func RegisterRuntimeSandboxLibrary(registrar interface{ RegisterLibrary(*object.Library) }, allowedPaths []string)
RegisterRuntimeSandboxLibrary registers the sandbox library with the specified allowed paths. If allowedPaths is nil, all paths are allowed (no restrictions). If allowedPaths is empty slice, no paths are allowed (deny all).
func RegisterSecretLibrary ¶ added in v0.5.6
func RegisterSecretLibrary(registrar interface{ RegisterLibrary(*object.Library) }, registry *secretprovider.Registry)
RegisterSecretLibrary registers the provider-agnostic secret access library.
func RegisterSecretsLibrary ¶
func RegisterSedLibrary ¶ added in v0.5.8
func RegisterSedLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
RegisterSedLibrary registers the scriptling.sed library with a Scriptling instance. If allowedPaths is nil, all paths are allowed. If non-nil, operations are restricted to those directories (same semantics as RegisterOSLibrary).
func RegisterShlexLibrary ¶ added in v0.17.0
func RegisterShlexLibrary(registrar object.LibraryRegistrar)
RegisterShlexLibrary registers the shlex library with a Scriptling instance.
func RegisterShutilLibrary ¶ added in v0.17.0
func RegisterShutilLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
RegisterShutilLibrary registers the shutil library with a Scriptling instance.
func RegisterSysLibrary ¶
func RegisterTOMLLibrary ¶
func RegisterTarfileLibrary ¶ added in v0.17.0
func RegisterTarfileLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
func RegisterTempfileLibrary ¶ added in v0.17.0
func RegisterTempfileLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
RegisterTempfileLibrary registers the tempfile library with a Scriptling instance.
func RegisterTemplateHTMLLibrary ¶ added in v0.6.0
func RegisterTemplateTextLibrary ¶ added in v0.6.0
func RegisterWaitForLibrary ¶
func RegisterWaitForLibrary(registrar interface{ RegisterLibrary(*object.Library) }, cfg ...*netsecurity.Config)
func RegisterWebSocketLibrary ¶ added in v0.4.1
func RegisterWebSocketLibrary(registrar interface{ RegisterLibrary(*object.Library) }, cfg ...*netsecurity.Config)
RegisterWebSocketLibrary registers the WebSocket client library. An optional network policy guards the outbound dial.
func RegisterXmlLibrary ¶ added in v0.17.0
func RegisterXmlLibrary(registrar object.LibraryRegistrar)
RegisterXmlLibrary registers the scriptling.xml library.
func RegisterYAMLLibrary ¶
func RegisterZipfileLibrary ¶ added in v0.17.0
func RegisterZipfileLibrary(registrar object.LibraryRegistrar, allowedPaths []string)
func ReleaseBackgroundTasks ¶
func ReleaseBackgroundTasks()
ReleaseBackgroundTasks sets BackgroundReady=true and starts all queued tasks
func ResetRuntime ¶
func ResetRuntime()
ResetRuntime clears all runtime state (for testing or re-initialization)
func SedReplace ¶ added in v0.17.1
SedReplace replaces every occurrence of old with replacement in the file (or every matching file under the directory) at path. old is matched literally, not as a regular expression. Files are edited in place using an atomic temp-file + rename. The return value is the number of files modified.
func SedReplacePattern ¶ added in v0.17.1
func SedReplacePattern(ctx context.Context, pattern, replacement, path string, opts SedOptions) (int64, error)
SedReplacePattern is SedReplace with old interpreted as a regular expression. Capture groups may be referenced in replacement as ${1}, ${2}, or ${name}.
func SetBackgroundFactory ¶
func SetBackgroundFactory(factory SandboxFactory)
SetBackgroundFactory sets the factory function for creating Scriptling instances in background tasks. Deprecated: Use SetSandboxFactory instead, which sets the factory for both sandbox and background use.
func SetSandboxFactory ¶
func SetSandboxFactory(factory SandboxFactory)
SetSandboxFactory sets the factory function for creating sandbox instances. Must be called before sandbox.create() is used in scripts.
Example:
extlibs.SetSandboxFactory(func() extlibs.SandboxInstance {
p := scriptling.New()
setupMyLibraries(p)
return p
})
func SetTaskErrorLogger ¶ added in v0.21.5
SetTaskErrorLogger replaces the handler invoked when a background task fails (its promise resolves with an error). Pass nil to restore the default stderr reporter. The handler must be safe to call from task goroutines.
func WaitBackgroundTasks ¶ added in v0.21.4
func WaitBackgroundTasks()
WaitBackgroundTasks blocks until every outstanding non-daemon background task has finished. Hosts that run scripts to completion (the CLI's plain-run modes) call it before exiting so fire-and-forget tasks are not killed mid-flight; long-running hosts (servers) do not.
Types ¶
type CompletedProcess ¶
CompletedProcess represents the result of a subprocess.run call
func (*CompletedProcess) AsList ¶
func (cp *CompletedProcess) AsList() ([]object.Object, object.Object)
func (*CompletedProcess) Inspect ¶
func (cp *CompletedProcess) Inspect() string
func (*CompletedProcess) Type ¶
func (cp *CompletedProcess) Type() object.ObjectType
type ExtractMatch ¶ added in v0.17.1
ExtractMatch is a single regex match with its capture groups, returned by SedExtract.
func SedExtract ¶ added in v0.17.1
func SedExtract(ctx context.Context, pattern, path string, opts SedOptions) ([]ExtractMatch, error)
SedExtract returns every match of pattern (a regular expression with capture groups) found in the file or directory at path. The result includes the captured groups for each match.
type FindEntry ¶ added in v0.17.7
type FindEntry struct {
Path string
Size int64
Mtime time.Time
IsDir bool
Hash uint64 // crc64 of file content when FindOptions.IncludeHash is set; 0 otherwise
LinkTarget string // symlink target when the entry is a symlink; empty for regular files/dirs
FilePerm int // file permission bits; populated when FindOptions.IncludeMetadata is set
}
FindEntry is a single matching entry returned by FindEntries, carrying the metadata required to decide whether the entry has changed without re-reading it. Callers comparing two trees (e.g. a sync tool diffing local and remote) can rely on Size+Mtime alone for the common case.
func FindEntries ¶ added in v0.17.7
FindEntries is like Find but returns FindEntry records with size, mtime, and type per match. Every matching entry is stat'd so the caller can compare trees without re-reading the bytes. Use Find instead when only the path strings are needed — Find skips the stat in the no-filter common case.
Like Find, the root itself is never included in the result, and paths are returned in arbitrary order.
type FindOptions ¶ added in v0.17.1
type FindOptions struct {
Recursive *bool
Type string
Name string
MtimeMin *float64
MtimeMax *float64
SizeMin *int64
SizeMax *int64
IncludeHidden bool
FollowLinks bool
MaxDepth int
AllowedPaths []string
IncludeHash bool // when true, every entry's file content is crc64-hashed
IncludeSymlinks bool // when true, symlink entries are yielded with their target in LinkTarget
IncludeMetadata bool // when true, file_perm is populated
}
FindOptions controls a find search.
Recursive is a pointer so that the zero value (nil) preserves scriptling's default of descending into subdirectories. Pass a pointer to false to keep the search non-recursive.
Type selects "any" (the zero value), "file", or "dir".
MtimeMin/MtimeMax and SizeMin/SizeMax are pointers so that a zero value is not confused with the valid bound 0; nil means the filter is inactive.
MaxDepth of 0 means unlimited.
type GlobLibraryInstance ¶
type GlobLibraryInstance struct {
// contains filtered or unexported fields
}
GlobLibraryInstance holds the configured Glob library instance
type GrepMatch ¶ added in v0.17.1
GrepMatch is a single matching line.
func Grep ¶ added in v0.17.1
Grep searches path for needle. When path is a directory the search runs concurrently over its files using the same bounded worker pool as scriptling.grep, respecting opts.Recursive / opts.Glob / opts.MaxSize.
needle is interpreted as a regular expression unless opts.Literal is true. Matches are returned in arbitrary order.
type GrepOptions ¶ added in v0.17.1
type GrepOptions struct {
Literal bool
Recursive bool
IgnoreCase bool
FollowLinks bool
Glob string
MaxSize int64
AllowedPaths []string
}
GrepOptions controls a Grep search.
Literal selects literal-string matching (scriptling.grep.string) when true, versus regular-expression matching (scriptling.grep.pattern) when false.
MaxSize skips files larger than this many bytes. The zero value applies the scriptling default of 1 MiB; a negative value disables the limit.
AllowedPaths, when non-nil, restricts every searched path to the listed absolute directories (nil = no restriction, matching the interpreter default).
type PathlibLibraryInstance ¶
type PathlibLibraryInstance struct {
PathClass *object.Class
// contains filtered or unexported fields
}
PathlibLibraryInstance holds the configured Pathlib library instance
type Promise ¶
type Promise struct {
// contains filtered or unexported fields
}
Promise represents an async operation result
type RuntimeAtomic ¶
type RuntimeAtomic struct {
// contains filtered or unexported fields
}
RuntimeAtomic is a named atomic counter
func (*RuntimeAtomic) Value ¶ added in v0.21.5
func (a *RuntimeAtomic) Value() int64
Value returns the current counter value. Exported for hosts and tests that need to observe a sync.Atomic created by script code.
type RuntimeQueue ¶
type RuntimeQueue struct {
// contains filtered or unexported fields
}
RuntimeQueue is a named thread-safe queue
type RuntimeShared ¶
type RuntimeShared struct {
// contains filtered or unexported fields
}
RuntimeShared is a named shared value. Values stored should be treated as immutable. Use set() to replace. For atomic read-modify-write, use update() with a callback.
type RuntimeWaitGroup ¶
type RuntimeWaitGroup struct {
// contains filtered or unexported fields
}
RuntimeWaitGroup is a named wait group
type SandboxFactory ¶
type SandboxFactory func() SandboxInstance
SandboxFactory creates new Scriptling instances for sandbox execution. Must be set by the host application before sandbox.create() can be used. The factory should return a fully configured instance with all required libraries registered and import paths configured.
func GetSandboxFactory ¶ added in v0.1.1
func GetSandboxFactory() SandboxFactory
GetSandboxFactory returns the currently configured sandbox factory. Returns nil if no factory has been set.
type SandboxInstance ¶
type SandboxInstance interface {
SetObjectVar(name string, obj object.Object) error
GetVarAsObject(name string) (object.Object, error)
EvalWithContext(ctx context.Context, input string) (object.Object, error)
SetSourceFile(name string)
LoadLibraryIntoEnv(name string, env *object.Environment) error
SetOutputWriter(w io.Writer)
}
SandboxInstance is the minimal interface a sandbox environment needs. This matches the Scriptling public API without importing the scriptling package. It is also used by the background task factory in scriptling.runtime.
type SedOptions ¶ added in v0.17.1
type SedOptions struct {
Recursive bool
IgnoreCase bool
FollowLinks bool
Glob string
MaxSize int64
AllowedPaths []string
}
SedOptions controls a sed replace or extract operation. See GrepOptions for the meaning of MaxSize and AllowedPaths; the semantics are identical.
type WebSocketClientConn ¶ added in v0.4.1
type WebSocketClientConn struct {
// contains filtered or unexported fields
}
WebSocketClientConn wraps a websocket.Conn with thread-safe access
func NewWebSocketClientConn ¶ added in v0.4.1
func NewWebSocketClientConn(conn *websocket.Conn) *WebSocketClientConn
NewWebSocketClientConn creates a new wrapped WebSocket connection
func (*WebSocketClientConn) Close ¶ added in v0.4.1
func (c *WebSocketClientConn) Close() error
Close closes the connection
func (*WebSocketClientConn) ClosedChan ¶ added in v0.4.1
func (c *WebSocketClientConn) ClosedChan() <-chan struct{}
ClosedChan returns a channel that closes when the connection closes
func (*WebSocketClientConn) IsConnected ¶ added in v0.4.1
func (c *WebSocketClientConn) IsConnected() bool
IsConnected returns whether the connection is still open
func (*WebSocketClientConn) ReadWithTimeout ¶ added in v0.4.1
ReadWithTimeout reads a message with a timeout Returns messageType, data, error. On timeout, returns 0, nil, nil
func (*WebSocketClientConn) RemoteAddr ¶ added in v0.4.1
func (c *WebSocketClientConn) RemoteAddr() string
RemoteAddr returns the remote address
func (*WebSocketClientConn) WriteMessage ¶ added in v0.4.1
func (c *WebSocketClientConn) WriteMessage(msgType int, data []byte) error
WriteMessage sends a message
type WebSocketRouteInfo ¶ added in v0.4.1
type WebSocketRouteInfo struct {
Handler string // "library.function" to call for each connection
}
WebSocketRouteInfo stores information about a registered WebSocket route
type WebSocketServerConn ¶ added in v0.4.1
type WebSocketServerConn struct {
// contains filtered or unexported fields
}
WebSocketServerConn wraps a server-side WebSocket connection
func NewWebSocketServerConn ¶ added in v0.4.1
func NewWebSocketServerConn(conn *websocket.Conn, id string) *WebSocketServerConn
NewWebSocketServerConn creates a new server WebSocket connection wrapper
func (*WebSocketServerConn) Close ¶ added in v0.4.1
func (c *WebSocketServerConn) Close() error
Close closes the connection
func (*WebSocketServerConn) ClosedChan ¶ added in v0.4.1
func (c *WebSocketServerConn) ClosedChan() <-chan struct{}
ClosedChan returns a channel that closes when the connection closes
func (*WebSocketServerConn) ID ¶ added in v0.4.1
func (c *WebSocketServerConn) ID() string
ID returns the connection ID
func (*WebSocketServerConn) IsConnected ¶ added in v0.4.1
func (c *WebSocketServerConn) IsConnected() bool
IsConnected returns whether the connection is still open
func (*WebSocketServerConn) ReadWithTimeout ¶ added in v0.4.1
ReadWithTimeout reads a message with timeout. Returns messageType, data, error. On timeout, returns 0, nil, nil
func (*WebSocketServerConn) RemoteAddr ¶ added in v0.4.1
func (c *WebSocketServerConn) RemoteAddr() string
RemoteAddr returns the remote address
func (*WebSocketServerConn) WriteMessage ¶ added in v0.4.1
func (c *WebSocketServerConn) WriteMessage(msgType int, data []byte) error
WriteMessage sends a message
Source Files
¶
- csv.go
- exported.go
- extlibs.go
- file_io.go
- fileops.go
- find.go
- fs.go
- glob.go
- grep.go
- htmlparser.go
- logging.go
- markdown.go
- os.go
- pathlib.go
- requests.go
- runtime.go
- runtime_http.go
- runtime_jsonrpc.go
- runtime_kv.go
- runtime_mcp.go
- runtime_plugin.go
- runtime_sync.go
- sandbox.go
- secret.go
- secrets.go
- sed.go
- shlex.go
- shutil.go
- shutil_disk_unix.go
- subprocess.go
- sys.go
- tarfile.go
- tempfile.go
- template.go
- toml.go
- wait_for.go
- websocket.go
- xml.go
- yaml.go
- zipfile.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Example demonstrating the module-level console API and how background tasks can access the shared TUI.
|
Example demonstrating the module-level console API and how background tasks can access the shared TUI. |
|
messaging
|
|
|
net
|
|
|
Package netsecurity restricts outbound network access for script-facing libraries (requests, wait_for, websocket).
|
Package netsecurity restricts outbound network access for script-facing libraries (requests, wait_for, websocket). |
|
Package nomad implements the scriptling.nomad extended library: a thin client over the HashiCorp Nomad HTTP API covering CSI volumes and jobs.
|
Package nomad implements the scriptling.nomad extended library: a thin client over the HashiCorp Nomad HTTP API covering CSI volumes and jobs. |
|
provision
|
|