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 map[string]string, query 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{ ... })
- 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 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{ ... })
- func RegisterWebSocketLibrary(registrar interface{ ... })
- 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)
- 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 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": { 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 } handler, err := args[1].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.http.get %q registered after start_server() — route will not be served\n", path) } RuntimeState.Routes["GET "+path] = &RouteInfo{ Methods: []string{"GET"}, Handler: handler, } RuntimeState.Unlock() return &object.Null{} }, HelpText: `get(path, handler) - Register a GET route Parameters: path (string): URL path for the route (e.g., "/api/users") handler (string): Handler function as "library.function" string Example: runtime.http.get("/health", "handlers.health_check")`, }, "post": { 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 } handler, err := args[1].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.http.post %q registered after start_server() — route will not be served\n", path) } RuntimeState.Routes["POST "+path] = &RouteInfo{ Methods: []string{"POST"}, Handler: handler, } RuntimeState.Unlock() return &object.Null{} }, HelpText: `post(path, handler) - Register a POST route Parameters: path (string): URL path for the route handler (string): Handler function as "library.function" string Example: runtime.http.post("/webhook", "handlers.webhook")`, }, "put": { 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 } handler, err := args[1].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.http.put %q registered after start_server() — route will not be served\n", path) } RuntimeState.Routes["PUT "+path] = &RouteInfo{ Methods: []string{"PUT"}, Handler: handler, } RuntimeState.Unlock() return &object.Null{} }, HelpText: `put(path, handler) - Register a PUT route Parameters: path (string): URL path for the route handler (string): Handler function as "library.function" string Example: runtime.http.put("/resource", "handlers.update_resource")`, }, "delete": { 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 } handler, err := args[1].AsString() if err != nil { return err } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.http.delete %q registered after start_server() — route will not be served\n", path) } RuntimeState.Routes["DELETE "+path] = &RouteInfo{ Methods: []string{"DELETE"}, Handler: handler, } RuntimeState.Unlock() return &object.Null{} }, HelpText: `delete(path, handler) - Register a DELETE route Parameters: path (string): URL path for the route handler (string): Handler function as "library.function" string Example: runtime.http.delete("/resource", "handlers.delete_resource")`, }, "route": { 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 } handler, err := args[1].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"} } RuntimeState.Lock() if RuntimeState.ServerStarted { fmt.Fprintf(os.Stderr, "warning: runtime.http.route %q registered after start_server() — route will not be served\n", path) } for _, method := range methods { RuntimeState.Routes[method+" "+path] = &RouteInfo{ Methods: []string{method}, Handler: handler, } } RuntimeState.Unlock() return &object.Null{} }, HelpText: `route(path, handler, methods=["GET", "POST", "PUT", "DELETE"]) - Register a route for multiple methods Parameters: path (string): URL path for the route handler (string): Handler function as "library.function" string methods (list): List of HTTP methods to accept Example: 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 } 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 Parameters: handler (string): Middleware function as "library.function" string The middleware receives the request object and should return: - None to continue to the handler - A response dict to short-circuit (block the request) Example: runtime.http.middleware("auth.check_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 } 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 handler for 404 Not Found responses Parameters: handler (string): Handler function as "library.function" string 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. Example: runtime.http.not_found("handlers.not_found")`, }, "websocket": { 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 } 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) - Register a WebSocket route Parameters: path (string): URL path for the WebSocket endpoint (e.g., "/ws") handler (string): Handler function as "library.function" string The handler receives a WebSocketClient object and runs for the connection lifetime. The handler should loop while client.connected() and use client.receive()/client.send(). Example: runtime.http.websocket("/chat", "handlers.chat_handler") # In handlers.py: def chat_handler(client): client.send("Welcome!") while client.connected(): msg = client.receive(timeout=60) if msg: client.send(f"Echo: {msg}")`, }, }, 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, 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 } 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) - Register a JSON-RPC method handler Parameters: name (str): JSON-RPC method name handler (str): Handler function as "library.function" string The handler receives the decoded JSON-RPC params as its single argument and returns a JSON-compatible result. Raise an exception or return runtime.jsonrpc.error(...) to produce an error response. Example: import scriptling.runtime as runtime runtime.jsonrpc.method("echo", "handlers.echo")`, }, "notification": { 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 } 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) - Register a JSON-RPC notification handler Parameters: name (str): JSON-RPC notification name handler (str): Handler function as "library.function" string Notifications are JSON-RPC requests without an id. The handler receives the decoded params and no response is written. Return values are ignored. Example: import scriptling.runtime as runtime runtime.jsonrpc.notification("updated", "handlers.on_updated")`, }, "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 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, 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 } 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) - Register a function for the plugin server Parameters: name (str): Function name exposed to plugin clients. handler (str): Handler as "library.function" string. The handler receives the positional arguments decoded from the plugin transport. Return any JSON-serialisable value. Raise an exception to produce an error response on the client side. Example: import scriptling.runtime.plugin as plugin_srv plugin_srv.register_function("greet", "handlers.greet")`, }, "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 } 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 Parameters: handler (str): Class as "library.ClassName" string. The exposed class name is taken from the last segment (e.g. "mymodule.Config" → "Config"). The class must be a normal scriptling class. The server handles object creation (object.new), method calls (object.call_method), and destruction (object.destroy) for every instance. Each instance is held server-side; clients receive a remote handle that behaves like a local object. Example: import scriptling.runtime.plugin as plugin_srv plugin_srv.register_class("handlers.Config")`, }, }, 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{ "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 = object.NewLibrary(RequestsLibraryName, map[string]*object.Builtin{ "exceptions": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return exceptionsNamespace }, }, "get": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { rawURL, _, options, err := extractRequestArgs(kwargs, args, false) if err != nil { return err } timeout, headers, params, user, pass := parseRequestOptions(options) fullURL := buildURLWithParams(rawURL, params) return httpRequestWithContext(ctx, "GET", fullURL, "", timeout, headers, user, pass) }, HelpText: `get(url, **kwargs) - Send a GET request Sends an HTTP GET request to the specified URL. Parameters: url (string): The URL to send the request to **kwargs: Optional arguments - timeout (int): Request timeout in seconds (default: 5) - headers (dict): HTTP headers as key-value pairs - params (dict): Query parameters to append to URL - auth (tuple/list): Basic authentication as (username, password) Returns: Response object with status_code, text, headers, body, url, and json() method`, }, "post": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { url, data, options, err := extractRequestArgs(kwargs, args, true) if err != nil { return err } timeout, headers, params, user, pass := parseRequestOptions(options) fullURL := buildURLWithParams(url, params) return httpRequestWithContext(ctx, "POST", fullURL, data, timeout, headers, user, pass) }, HelpText: `post(url, data=None, json=None, **kwargs) - Send a POST request Sends an HTTP POST request to the specified URL with the given data. Parameters: url (string): The URL to send the request to data (string, optional): The request body data as a string json (dict/list, optional): Data to be JSON-encoded and sent (sets Content-Type to application/json) **kwargs: Optional arguments - timeout (int): Request timeout in seconds (default: 5) - headers (dict): HTTP headers as key-value pairs - auth (tuple/list): Basic authentication as (username, password) Note: Use either 'data' or 'json', not both. Returns: Response object with status_code, text, headers, body, url, and json() method`, }, "put": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { url, data, options, err := extractRequestArgs(kwargs, args, true) if err != nil { return err } timeout, headers, params, user, pass := parseRequestOptions(options) fullURL := buildURLWithParams(url, params) return httpRequestWithContext(ctx, "PUT", fullURL, data, timeout, headers, user, pass) }, HelpText: `put(url, data=None, json=None, **kwargs) - Send a PUT request Sends an HTTP PUT request to the specified URL with the given data. Parameters: url (string): The URL to send the request to data (string, optional): The request body data as a string json (dict/list, optional): Data to be JSON-encoded and sent (sets Content-Type to application/json) **kwargs: Optional arguments - timeout (int): Request timeout in seconds (default: 5) - headers (dict): HTTP headers as key-value pairs - auth (tuple/list): Basic authentication as (username, password) Note: Use either 'data' or 'json', not both. Returns: Response object with status_code, text, headers, body, url, and json() method`, }, "delete": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { rawURL, _, options, err := extractRequestArgs(kwargs, args, false) if err != nil { return err } timeout, headers, params, user, pass := parseRequestOptions(options) fullURL := buildURLWithParams(rawURL, params) return httpRequestWithContext(ctx, "DELETE", fullURL, "", timeout, headers, user, pass) }, HelpText: `delete(url, **kwargs) - Send a DELETE request Sends an HTTP DELETE request to the specified URL. Parameters: url (string): The URL to send the request to **kwargs: Optional arguments - timeout (int): Request timeout in seconds (default: 5) - headers (dict): HTTP headers as key-value pairs - auth (tuple/list): Basic authentication as (username, password) Returns: Response object with status_code, text, headers, body, url, and json() method`, }, "patch": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { url, data, options, err := extractRequestArgs(kwargs, args, true) if err != nil { return err } timeout, headers, params, user, pass := parseRequestOptions(options) fullURL := buildURLWithParams(url, params) return httpRequestWithContext(ctx, "PATCH", fullURL, data, timeout, headers, user, pass) }, HelpText: `patch(url, data=None, json=None, **kwargs) - Send a PATCH request Sends an HTTP PATCH request to the specified URL with the given data. Parameters: url (string): The URL to send the request to data (string, optional): The request body data as a string json (dict/list, optional): Data to be JSON-encoded and sent (sets Content-Type to application/json) **kwargs: Optional arguments - timeout (int): Request timeout in seconds (default: 5) - headers (dict): HTTP headers as key-value pairs - auth (tuple/list): Basic authentication as (username, password) Note: Use either 'data' or 'json', not both. Returns: Response object with status_code, text, headers, body, url, and json() method`, }, "parallel": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 1 { return errors.NewArgumentError(1, 0) } requestList, err := args[0].AsList() if err != nil { return errors.NewTypeError("LIST", args[0].Type().String()) } if len(requestList) == 0 { return &object.List{Elements: []object.Object{}} } maxParallel := int64(4) if mp, mpErr := kwargs.GetInt("max_parallel", 4); mpErr == nil { maxParallel = mp } if maxParallel < 1 { maxParallel = 1 } results := make([]object.Object, len(requestList)) sem := make(chan struct{}, maxParallel) var wg sync.WaitGroup workerCtx := context.WithValue(ctx, "scriptling-env", (*object.Environment)(nil)) object.RunBlocking(ctx, func() { for i, reqObj := range requestList { reqDict, dictErr := reqObj.AsDict() if dictErr != nil { results[i] = errors.NewTypeError("DICT", reqObj.Type().String()) continue } wg.Add(1) go func(idx int, rd map[string]object.Object) { defer wg.Done() sem <- struct{}{} defer func() { <-sem }() results[idx] = executeParallelRequest(workerCtx, rd) }(i, reqDict) } wg.Wait() }) return &object.List{Elements: results} }, HelpText: `parallel(requests, max_parallel=4) - Execute multiple HTTP requests in parallel Sends multiple HTTP requests concurrently with a configurable concurrency limit. Results are returned in the same order as the input requests. Parameters: requests (list): List of request dicts, each with: - method (string): HTTP method ("GET", "POST", "PUT", "DELETE", "PATCH") - url (string): The URL to send the request to - data (string, optional): Request body as string - json (dict/list, optional): Data to JSON-encode as body - headers (dict, optional): HTTP headers - params (dict, optional): Query parameters - auth (list/tuple, optional): [username, password] for basic auth - timeout (int, optional): Timeout in seconds (default: 30) max_parallel (int): Maximum concurrent requests (default: 4) Returns: list: List of Response objects in the same order as input requests. Failed requests return a Response with status_code=0 and the error in body. Example: results = requests.parallel([ {"method": "GET", "url": "https://api.example.com/item/1"}, {"method": "POST", "url": "https://api.example.com/item/2", "json": {"key": "val"}}, ], max_parallel=4) for resp in results: if resp.status_code == 200: data = resp.json()`, }, }, map[string]object.Object{ "RequestException": requestExceptionType, "HTTPError": httpErrorType, "Response": ResponseClass, }, "HTTP requests library")
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 } } if shared { sharedKwargs := make(map[string]object.Object, len(kwargs.Kwargs)) for k, v := range kwargs.Kwargs { if k == "shared" { continue } sharedKwargs[k] = v } return startSharedTask(ctx, handler, args[2:], sharedKwargs, env, eval) } 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 { 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.Unlock() if backgroundReady { return startBackgroundTask(handler, fnArgs, fnKwargs, env, eval, factory, ctx) } return &object.Null{} }, HelpText: `background(name, handler, *args, **kwargs) - Start a fire-and-forget background task Starts a background task in a goroutine and returns immediately. Returns null on success, or an error if the handler is not found. Both handler patterns run in isolated environments with no access to the calling script's data. Only sibling functions are copied; data must be passed via args or runtime.sync. 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 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. `, }, "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 // 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, 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 = object.NewLibrary(WaitForLibraryName, map[string]*object.Builtin{ "file": { 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 } timeout, pollRate, err := parseWaitOptions(args, kwargs.Kwargs) if err != nil { return err } deadline := time.Now().Add(time.Duration(timeout) * time.Second) pollInterval := time.Duration(pollRate * float64(time.Second)) timer := time.NewTimer(pollInterval) defer stopTimer(timer) for time.Now().Before(deadline) { if _, err := os.Stat(path); err == nil { return object.NewBoolean(true) } if !waitForNextPoll(ctx, timer, pollInterval) { return object.NewBoolean(false) } } if _, err := os.Stat(path); err == nil { return object.NewBoolean(true) } return object.NewBoolean(false) }, HelpText: `file(path, timeout=30, poll_rate=1) - Wait for a file to exist Waits for the specified file to become available. Parameters: path (string): Path to the file to wait for timeout (int): Maximum time to wait in seconds (default: 30) poll_rate (float): Time between checks in seconds (default: 1) Returns: bool: True if file exists, False if timeout exceeded`, }, "dir": { 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 } timeout, pollRate, err := parseWaitOptions(args, kwargs.Kwargs) if err != nil { return err } deadline := time.Now().Add(time.Duration(timeout) * time.Second) pollInterval := time.Duration(pollRate * float64(time.Second)) timer := time.NewTimer(pollInterval) defer stopTimer(timer) for time.Now().Before(deadline) { if info, err := os.Stat(path); err == nil { if info.IsDir() { return object.NewBoolean(true) } } if !waitForNextPoll(ctx, timer, pollInterval) { return object.NewBoolean(false) } } if info, err := os.Stat(path); err == nil { if info.IsDir() { return object.NewBoolean(true) } } return object.NewBoolean(false) }, HelpText: `dir(path, timeout=30, poll_rate=1) - Wait for a directory to exist Waits for the specified directory to become available. Parameters: path (string): Path to the directory to wait for timeout (int): Maximum time to wait in seconds (default: 30) poll_rate (float): Time between checks in seconds (default: 1) Returns: bool: True if directory exists, False if timeout exceeded`, }, "port": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 2); err != nil { return err } host, err := args[0].AsString() if err != nil { return err } var port int switch v := args[1].(type) { case *object.Integer: port = int(v.IntValue()) case *object.String: p, err := strconv.Atoi(v.StringValue()) if err != nil { return errors.NewError("invalid port number: %s", v.StringValue()) } port = p default: return errors.NewTypeError("INT|STRING", args[1].Type().String()) } timeout, pollRate, err := parseWaitOptionsKwargsOnly(30, 1.0, kwargs.Kwargs) if err != nil { return err } deadline := time.Now().Add(time.Duration(timeout) * time.Second) pollInterval := time.Duration(pollRate * float64(time.Second)) address := net.JoinHostPort(host, strconv.Itoa(port)) timer := time.NewTimer(pollInterval) defer stopTimer(timer) for time.Now().Before(deadline) { conn, err := net.DialTimeout("tcp", address, time.Second) if err == nil { conn.Close() return object.NewBoolean(true) } if !waitForNextPoll(ctx, timer, pollInterval) { return object.NewBoolean(false) } } if conn, err := net.DialTimeout("tcp", address, time.Second); err == nil { conn.Close() return object.NewBoolean(true) } return object.NewBoolean(false) }, HelpText: `port(host, port, timeout=30, poll_rate=1) - Wait for a TCP port to be open Waits for the specified TCP port to accept connections. Parameters: host (string): Hostname or IP address port (int|string): Port number timeout (int): Maximum time to wait in seconds (default: 30) poll_rate (float): Time between checks in seconds (default: 1) Returns: bool: True if port is open, False if timeout exceeded`, }, "http": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } url, err := args[0].AsString() if err != nil { return err } timeout := 30 pollRate := 1.0 expectedStatus := int64(200) if len(args) > 1 { if t, err := args[1].AsInt(); err == nil { timeout = int(t) } else { return err } } for k, v := range kwargs.Kwargs { switch k { case "timeout": if t, err := v.AsInt(); err == nil { timeout = int(t) } else { return err } case "poll_rate": if f, err := v.AsFloat(); err == nil { pollRate = f } else if i, err := v.AsInt(); err == nil { pollRate = float64(i) } else { return errors.NewTypeError("FLOAT", v.Type().String()) } case "status_code": if s, err := v.AsInt(); err == nil { expectedStatus = s } else { return err } } } deadline := time.Now().Add(time.Duration(timeout) * time.Second) pollInterval := time.Duration(pollRate * float64(time.Second)) timer := time.NewTimer(pollInterval) defer stopTimer(timer) client := pool.GetHTTPClient() for time.Now().Before(deadline) { req, httpErr := http.NewRequestWithContext(ctx, "GET", url, nil) if httpErr != nil { return errors.NewError("http request error: %s", httpErr.Error()) } var resp *http.Response object.RunBlocking(ctx, func() { resp, httpErr = client.Do(req) }) if httpErr == nil { statusMatch := int64(resp.StatusCode) == expectedStatus resp.Body.Close() if statusMatch { return object.NewBoolean(true) } } if !waitForNextPoll(ctx, timer, pollInterval) { return object.NewBoolean(false) } } req, httpErr := http.NewRequestWithContext(ctx, "GET", url, nil) if httpErr != nil { return object.NewBoolean(false) } var resp *http.Response object.RunBlocking(ctx, func() { resp, httpErr = client.Do(req) }) if httpErr == nil { statusMatch := int64(resp.StatusCode) == expectedStatus resp.Body.Close() if statusMatch { return object.NewBoolean(true) } } return object.NewBoolean(false) }, HelpText: `http(url, timeout=30, poll_rate=1, status_code=200) - Wait for HTTP endpoint Waits for the specified HTTP endpoint to respond with the expected status code. Parameters: url (string): URL to check timeout (int): Maximum time to wait in seconds (default: 30) poll_rate (float): Time between checks in seconds (default: 1) status_code (int): Expected HTTP status code (default: 200) Returns: bool: True if endpoint responds with expected status, False if timeout exceeded`, }, "file_content": { 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 } content, err := args[1].AsString() if err != nil { return err } timeout, pollRate, err := parseWaitOptionsKwargsOnly(30, 1.0, kwargs.Kwargs) if err != nil { return err } deadline := time.Now().Add(time.Duration(timeout) * time.Second) pollInterval := time.Duration(pollRate * float64(time.Second)) needle := []byte(content) timer := time.NewTimer(pollInterval) defer stopTimer(timer) for time.Now().Before(deadline) { if data, err := os.ReadFile(path); err == nil { if bytes.Contains(data, needle) { return object.NewBoolean(true) } } if !waitForNextPoll(ctx, timer, pollInterval) { return object.NewBoolean(false) } } if data, err := os.ReadFile(path); err == nil { if bytes.Contains(data, needle) { return object.NewBoolean(true) } } return object.NewBoolean(false) }, HelpText: `file_content(path, content, timeout=30, poll_rate=1) - Wait for file to contain content Waits for the specified file to exist and contain the given content. Parameters: path (string): Path to the file to check content (string): Content to search for in the file timeout (int): Maximum time to wait in seconds (default: 30) poll_rate (float): Time between checks in seconds (default: 1) Returns: bool: True if file contains the content, False if timeout exceeded`, }, "process_name": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } processName, err := args[0].AsString() if err != nil { return err } timeout, pollRate, err := parseWaitOptions(args, kwargs.Kwargs) if err != nil { return err } deadline := time.Now().Add(time.Duration(timeout) * time.Second) pollInterval := time.Duration(pollRate * float64(time.Second)) timer := time.NewTimer(pollInterval) defer stopTimer(timer) for time.Now().Before(deadline) { if processRunning(processName) { return object.NewBoolean(true) } if !waitForNextPoll(ctx, timer, pollInterval) { return object.NewBoolean(false) } } if processRunning(processName) { return object.NewBoolean(true) } return object.NewBoolean(false) }, HelpText: `process_name(name, timeout=30, poll_rate=1) - Wait for a process to be running Waits for a process with the specified name to be running. Parameters: name (string): Process name to search for timeout (int): Maximum time to wait in seconds (default: 30) poll_rate (float): Time between checks in seconds (default: 1) Returns: bool: True if process is running, False if timeout exceeded`, }, }, nil, "Wait for resources to become available", )
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 := msg.CoerceString() 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 = object.NewLibrary(WebSocketLibraryName, map[string]*object.Builtin{ "connect": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } url, err := args[0].AsString() if err != nil { return err } timeout := 10 if t := kwargs.Get("timeout"); t != nil { if timeoutInt, e := t.AsInt(); e == nil { timeout = int(timeoutInt) } } headers := make(map[string]string) if h := kwargs.Get("headers"); h != nil { if hDict, e := h.AsDict(); e == nil { for key, val := range hDict { if strVal, e := val.AsString(); e == nil { headers[key] = strVal } } } } header := make(map[string][]string) for k, v := range headers { header[k] = []string{v} } dialCtx, cancel := context.WithTimeout(ctx, time.Duration(timeout)*time.Second) defer cancel() var conn *websocket.Conn var dialErr error object.RunBlocking(ctx, func() { conn, _, dialErr = websocket.DefaultDialer.DialContext(dialCtx, url, header) }) if dialErr != nil { return errors.NewError("websocket connect failed: %s", dialErr.Error()) } wsConn := NewWebSocketClientConn(conn) return &object.Builtin{ Attributes: map[string]object.Object{ "send": &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 } msg := args[0] var data []byte var msgType = websocket.TextMessage if dict, ok := msg.(*object.Dict); ok { jsonData, err := json.Marshal(conversion.ToGo(dict)) if err != nil { return errors.NewError("failed to encode JSON: %s", err.Error()) } data = jsonData } else if str, ok := msg.(*object.String); ok { data = []byte(str.StringValue()) } else { strVal, err := msg.CoerceString() if err != nil { return errors.NewError("message must be string or dict") } data = []byte(strVal) } var sendErr error object.RunBlocking(ctx, func() { sendErr = wsConn.WriteMessage(msgType, data) }) if sendErr != nil { return errors.NewError("send failed: %s", sendErr.Error()) } return &object.Null{} }, HelpText: `send(message) - Send a text message 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, 1); err != nil { return err } var data []byte if list, ok := args[0].(*object.List); ok { data = make([]byte, len(list.Elements)) for i, elem := range list.Elements { if b, e := elem.AsInt(); e == nil && b >= 0 && b <= 255 { data[i] = byte(b) } else { return errors.NewError("send_binary requires list of bytes (0-255)") } } } else { return errors.NewError("send_binary requires a list of bytes") } var sendErr error object.RunBlocking(ctx, func() { sendErr = wsConn.WriteMessage(websocket.BinaryMessage, data) }) if sendErr != nil { return errors.NewError("send_binary failed: %s", sendErr.Error()) } return &object.Null{} }, HelpText: `send_binary(data) - Send binary data Parameters: data (list): List of byte values (0-255)`, }, "receive": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { timeout := 30 if t := kwargs.Get("timeout"); t != nil { if timeoutInt, e := t.AsInt(); e == nil { timeout = int(timeoutInt) } } var msgType int var data []byte var err error object.RunBlocking(ctx, func() { msgType, data, err = wsConn.ReadWithTimeout(time.Duration(timeout) * time.Second) }) if err != nil { return &object.Null{} } if 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 Parameters: timeout (int, optional): Timeout in seconds (default: 30) Returns: string for text messages, list of bytes for binary, or None on timeout/disconnect`, }, "connected": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return object.NewBoolean(wsConn.IsConnected()) }, HelpText: `connected() - Check if connection is still open Returns: True if connected, False otherwise`, }, "close": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := wsConn.Close(); err != nil { return errors.NewError("close failed: %s", err.Error()) } return &object.Null{} }, HelpText: `close() - Close the WebSocket connection`, }, "remote_addr": object.NewString(wsConn.RemoteAddr()), }, HelpText: "WebSocket connection object", } }, HelpText: `connect(url, timeout=10, headers={}) - Connect to a WebSocket server Parameters: url (string): WebSocket URL (ws:// or wss://) timeout (int, optional): Connection timeout in seconds (default: 10) headers (dict, optional): HTTP headers to send with the handshake Returns: Connection object with methods: send(), send_binary(), receive(), connected(), close() Example: import scriptling.net.websocket as ws conn = ws.connect("wss://echo.websocket.org") conn.send("Hello") msg = conn.receive(timeout=5) conn.close()`, }, "send": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return errors.NewError("send() requires a connection object - use ws.connect() first, then conn.send()") }, HelpText: `send(conn, message) - Send a message on a connection Note: This is a module-level function. Prefer using conn.send(message) instead.`, }, "send_binary": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return errors.NewError("send_binary() requires a connection object - use ws.connect() first, then conn.send_binary()") }, HelpText: `send_binary(conn, data) - Send binary data on a connection Note: This is a module-level function. Prefer using conn.send_binary(data) instead.`, }, "receive": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return errors.NewError("receive() requires a connection object - use ws.connect() first, then conn.receive()") }, HelpText: `receive(conn, timeout=30) - Receive a message from a connection Note: This is a module-level function. Prefer using conn.receive(timeout) instead.`, }, "connected": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return errors.NewError("connected() requires a connection object - use ws.connect() first, then conn.connected()") }, HelpText: `connected(conn) - Check if a connection is still open Note: This is a module-level function. Prefer using conn.connected() instead.`, }, "close": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return errors.NewError("close() requires a connection object - use ws.connect() first, then conn.close()") }, HelpText: `close(conn) - Close a connection Note: This is a module-level function. Prefer using conn.close() instead.`, }, "is_text": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } _, ok := args[0].(*object.String) return object.NewBoolean(ok) }, HelpText: `is_text(message) - Check if a received message is a text message Parameters: message: A message returned from receive() Returns: True if the message is a text message (string), False otherwise Example: msg = conn.receive() if ws.is_text(msg): print(f"Text: {msg}")`, }, "is_binary": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } _, ok := args[0].(*object.List) return object.NewBoolean(ok) }, HelpText: `is_binary(message) - Check if a received message is a binary message Parameters: message: A message returned from receive() Returns: True if the message is binary (list of bytes), False otherwise Example: msg = conn.receive() if ws.is_binary(msg): print(f"Binary: {len(msg)} bytes")`, }, }, nil, "WebSocket client library")
WebSocketLibrary is the WebSocket client library
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 map[string]string, query map[string]string) *object.Instance
CreateRequestInstance creates a new Request instance with the given data
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 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 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 RegisterWebSocketLibrary ¶ added in v0.4.1
RegisterWebSocketLibrary registers the WebSocket client library
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
})
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
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_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 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
|
|