diff --git a/docs/index.md b/docs/index.md index 5f58d066c..daffd8315 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,9 +32,13 @@ Use `$path` expressions in templates for dynamic data extraction, and leverage b - **Enhanced Parameter Validation**: Comprehensive `_$()` validation chains with type conversion, regex matching, and range checking - **Job Input/Output Validation**: Declarative `check.in` and `check.out` sections with fluent validation syntax - **AI/LLM Integration**: Multi-provider LLM support (OpenAI, Anthropic, Gemini, Ollama) with function calling and image processing +- **MCP Client**: `$mcp()` for Model Context Protocol communication with stdio, HTTP, and SSE transports; OAuth2 (client credentials & authorization_code); tool blacklist (see `openaf-advanced.md` §19) +- **FTP/FTPS Client**: `$ftp()` shortcut for plain and TLS-secured FTP file transfers (see `openaf.md`) +- **HTTP Path Prefix**: Deploy the embedded HTTP server under a configurable subpath via `HTTPD_PREFIX` flag (see `openaf-advanced.md` §18, `openaf-flags.md`) - **Telemetry & Metrics**: Built-in metrics collection, OpenMetrics format support, and integration with monitoring systems - **Security Enhancements**: File integrity checking, authorized domains, and comprehensive audit trails -- **Async Promises**: `$do` / `$doV` helpers build on `oPromise` for threaded or virtual-thread asynchronous execution with familiar `.then` / `.catch` chaining.【F:js/openaf.js†L13130-L13157】【F:js/openaf.js†L12145-L12163】【F:js/openaf.js†L12208-L12251】 +- **Async Promises**: `$do` / `$doV` helpers build on `oPromise` for threaded or virtual-thread asynchronous execution with familiar `.then` / `.catch` chaining. +- **Inline Argument Token Interpolation**: oJob args support `"${key}"` and `"prefix-${key}-suffix"` patterns with default values and backslash escaping (see `ojob.md`) --- This index is intentionally minimal—open individual docs for full tables of contents. diff --git a/docs/ojob.md b/docs/ojob.md index 284edde28..8a1841ffb 100644 --- a/docs/ojob.md +++ b/docs/ojob.md @@ -392,7 +392,9 @@ todo: - **Inheritance**: These processed arguments are passed to the target job - **Template integration**: Works seamlessly with oJob's template processing - **Nested support**: Supports dot notation for nested object properties -- **Type preservation**: Values are processed as strings but maintain their intended types +- **Type preservation**: When a string value is exactly `"${key}"` the resolved value keeps its original type (number, boolean, array, object, etc.). When a token appears inside a longer string (e.g. `"prefix-${key}-suffix"`) the result is always a string. +- **Inline interpolation**: Tokens can appear anywhere inside a string value and multiple tokens may be combined — e.g. `"${host:-localhost}:${port:-8080}"`. Note that when multiple tokens are interpolated into a string context, each resolved value is converted via `String()` before concatenation. +- **Escaping**: Prefix a token with `\` to prevent resolution — `\${key}` is left as the literal string `${key}`. Use `\\${key}` to produce `\${key}` in the output (odd number of backslashes escapes, even number does not). **Usage examples:** ```yaml diff --git a/docs/openaf-advanced.md b/docs/openaf-advanced.md index df8fb47f6..41124aad7 100644 --- a/docs/openaf-advanced.md +++ b/docs/openaf-advanced.md @@ -516,5 +516,146 @@ function robustLLMCall(prompt, maxRetries = 3) { ## 17. Safe Dynamic Includes Combine integrity hashes + authorized domains + change auditing flags. For local development disable with environment variable toggles but keep production strict. +## 18. HTTP Path Prefix Support (ow.server.httpd) + +OpenAF's embedded HTTP server supports serving all resources and routes under a configurable path prefix. This is useful when deploying behind a reverse proxy that forwards requests under a subpath (e.g. `/app`). + +### Setting a prefix + +Set the `HTTPD_PREFIX` flag before starting any server. Key `"0"` is the global default for all ports; use a specific port number string to override per port: + +```javascript +// Set global prefix /app for all HTTP servers +__flags.HTTPD_PREFIX = { "0": "/app" }; + +// Or different prefixes per port +__flags.HTTPD_PREFIX = { "0": "", "8080": "/api", "9090": "/gui" }; +``` + +Via oJob YAML: +```yaml +ojob: + flags: + HTTPD_PREFIX: + "0": /app +``` + +### Prefix utility functions + +`ow.server.httpd` provides helper functions to work with prefixes: + +| Function | Description | +|----------|-------------| +| `ow.server.httpd.getPrefix(httpdOrPort)` | Returns the normalized prefix for the given server or port | +| `ow.server.httpd.withPrefix(httpdOrPort, uri)` | Prepend the prefix to `uri` (skips absolute URLs) | +| `ow.server.httpd.stripPrefix(httpdOrPort, uri)` | Remove the prefix from a URI for internal routing | +| `ow.server.httpd.normalizePrefix(aPrefix)` | Normalize a raw prefix string (ensures leading `/`, no trailing `/`) | + +```javascript +ow.loadServer(); +__flags.HTTPD_PREFIX = { "0": "/app" }; + +var httpd = ow.server.httpd.start(8080); + +// Build a prefixed URL +var link = ow.server.httpd.withPrefix(httpd, "/about"); // "/app/about" + +// Strip prefix for internal route matching +ow.server.httpd.route(httpd, ow.server.httpd.mapWithExistingFn(httpd, { + "/about": function(req) { + return httpd.replyOKText("About page"); + } +}), function(req) { + var internalURI = ow.server.httpd.stripPrefix(httpd, req.uri); + return httpd.replyNotFound(); +}); +``` + +All built-in GUI pages and static-file handlers automatically respect the configured prefix. + +## 19. MCP Client ($mcp) + +`$mcp(aOptions)` creates a Model Context Protocol (MCP) client for communicating with LLM tool servers. + +### Connection types + +| `type` | Description | +|--------|-------------| +| `stdio` (default) | Spawn a local process; communicate over stdin/stdout | +| `remote` / `http` | HTTP JSON-RPC endpoint | +| `sse` | HTTP endpoint with Server-Sent Events responses | +| `ojob` | In-process oJob jobs exposed as MCP tools | +| `dummy` | Local in-memory stub for testing | + +```javascript +// stdio MCP server +var client = $mcp({ type: "stdio", cmd: "my-mcp-server" }); +client.initialize(); +var tools = client.listTools(); +var result = client.callTool("myTool", { param: "value" }); + +// Remote HTTP MCP server +var remote = $mcp({ type: "remote", url: "https://mcp.example.com/mcp" }); +remote.initialize(); +``` + +### Authentication (`auth` option) + +For `remote`/`http`/`sse` connections: + +```javascript +// Static bearer token +var client = $mcp({ + type: "remote", + url: "https://mcp.example.com/mcp", + auth: { type: "bearer", token: "my-token" } +}); + +// OAuth2 client credentials +var client = $mcp({ + type: "remote", + url: "https://mcp.example.com/mcp", + auth: { + type: "oauth2", + tokenURL: "https://auth.example.com/oauth/token", + clientId: "my-client", + clientSecret: "my-secret", + scope: "mcp:read mcp:write" + } +}); + +// OAuth2 authorization_code (opens browser) +var client = $mcp({ + type: "remote", + url: "https://mcp.example.com/mcp", + auth: { + type: "oauth2", + grantType: "authorization_code", + authURL: "https://auth.example.com/authorize", + tokenURL: "https://auth.example.com/oauth/token", + clientId: "my-client", + redirectURI: "http://localhost:8080/callback", + disableOpenBrowser: false // set true to suppress browser launch + } +}); +``` + +OAuth2 token URLs can also be auto-discovered from the MCP server's OAuth 2.0 Protected Resource Metadata when `tokenURL`/`authURL` are omitted. + +### Tool blacklist + +Prevent specific tools from appearing in `listTools()` or being called via `callTool()`: + +```javascript +var client = $mcp({ + type: "stdio", + cmd: "my-mcp-server", + blacklist: ["dangerousTool", "internalTool"] +}); +client.initialize(); +// listTools() will not include blacklisted tools +// callTool("dangerousTool", {}) throws an error +``` + --- See also: `ojob-security.md`, `openaf-flags.md`, and main references. diff --git a/docs/openaf-flags.md b/docs/openaf-flags.md index 43313a03b..72ae59061 100644 --- a/docs/openaf-flags.md +++ b/docs/openaf-flags.md @@ -40,9 +40,16 @@ Central list of noteworthy runtime flags and environment variables. ## Server Management -| Env | Default | Purpose | -|-----|---------|---------| +| Env / Flag | Default | Purpose | +|------------|---------|---------| | OAF_PIDFILE | (unset) | Override PID file path in ow.server.checkIn | +| HTTPD_PREFIX *(flag)* | `{}` | Runtime flag (set via `__flags.HTTPD_PREFIX` or `ojob.flags`). Map of port-to-path-prefix entries for the embedded HTTP server. Key `"0"` is the global default. E.g. `{ "0": "/app", "8080": "/api" }`. Must be set before calling `ow.server.httpd.start()`. | + +## Template / Markdown + +| Flag | Default | Purpose | +|------|---------|---------| +| MD_RENDER_SVG | false | When true, ` ```svg ` fenced blocks in markdown are extracted and injected as inline SVG in HTML output instead of being treated as code blocks | ## Misc Performance / Behavior diff --git a/docs/openaf.md b/docs/openaf.md index 110d732d3..1a42f5ba6 100644 --- a/docs/openaf.md +++ b/docs/openaf.md @@ -759,6 +759,65 @@ Convenience getters: - `getJsSlon(i)` parse SLON - `getYaml(i)` parse YAML +### $ftp - FTP / FTPS Client + +`$ftp(aMap)` creates an FTP or FTPS client connection. `aMap` can be a URL string or a configuration map. + +**URL format:** +``` +ftp://user:pass@host:port/?timeout=5000&passive=true&binary=true +ftps://user:pass@host:port/?timeout=5000&passive=true&implicit=false&protocol=TLS +``` + +**Map format:** + +| Key | Default | Description | +|-----|---------|-------------| +| `host` | – | Remote hostname or IP | +| `port` | `21` / `990` | Port (990 for implicit FTPS) | +| `login` | – | Username | +| `pass` | – | Password | +| `secure` | `false` | Enable FTPS (explicit TLS) | +| `implicit` | `false` | Use implicit TLS mode (port 990) | +| `protocol` | `"TLS"` | TLS protocol name | +| `passive` | `true` | Use passive mode | +| `binary` | `true` | Binary transfer mode | +| `timeout` | – | Connection timeout in ms | + +**Available methods (all return `$ftp` for chaining except where noted):** + +| Method | Description | +|--------|-------------| +| `cd(aPath)` | Change remote working directory | +| `pwd()` | Return current remote working directory (String) | +| `mkdir(aDir)` | Create remote directory | +| `getFile(src, dst)` | Download remote `src` to local `dst` | +| `putFile(src, dst)` | Upload local `src` (path or stream) to remote `dst` | +| `rename(src, dst)` | Rename / move remote file | +| `listFiles(aPath)` | Return array of file-info maps for `aPath` | +| `rm(aFilePath)` | Delete remote file | +| `rmdir(aFilePath)` | Delete remote directory | +| `passive(bool)` | Toggle passive mode | +| `binary(bool)` | Toggle binary transfer mode | +| `timeout(ms)` | Set connection timeout | +| `close()` | Close the connection | + +**Examples:** +```javascript +// Plain FTP via URL +var ftp = $ftp("ftp://user:secret@ftp.example.com/"); +ftp.cd("/uploads") + .putFile("/local/report.csv", "report.csv") + .close(); + +// FTPS via map +var ftps = $ftp({ host: "ftp.example.com", secure: true, + login: "user", pass: "secret" }); +var files = ftps.listFiles("/data"); +ftps.getFile("/data/report.csv", "/tmp/report.csv"); +ftps.close(); +``` + ### $tb - Thread Box (Timeout / Stop Controller) Run a function with enforced timeout or stop condition: diff --git a/hbs/index.hbs b/hbs/index.hbs index 42735a047..472d8e513 100644 --- a/hbs/index.hbs +++ b/hbs/index.hbs @@ -5,8 +5,8 @@