Files
WeKnora/pluginsdk/pluginapi/options.go
wizardchen d46fa25e41 feat(plugin): dynamic form options, OAuth connections and resource limits
Plugin config forms can now ask the plugin while they are filled in:

- x-options {name, dependsOn, search} loads a field's choices from
  /v1/options/{name}. POST /api/v1/plugins/:id/options merges the form's
  values with the stored secrets (system, tenant or instance scope) so a
  redacted token still works. Go and Python SDKs, OpenAPI and conformance
  cover the endpoint.
- x-oauth turns a string field into an account connection. WeKnora runs
  the authorization code flow (PKCE, client credentials from the plugin's
  system config), keeps the tokens sealed in plugin_oauth_connections and
  the field holds "oauth:<id>". The invoker swaps references for fresh
  access tokens at call time, refreshing near expiry. Callback:
  /api/v1/plugin-oauth/callback, one-time states in Redis or memory.
- SchemaForm gains an options widget (dependsOn reload, remote search)
  and an OAuth connect widget; connector, web search, workspace and
  platform config forms provide the plugin behind them.

Host plugins are capped per runtime.resources on Linux: RLIMIT_DATA for
memory (WEKNORA_PLUGIN_MEMORY_DEFAULT when undeclared) and, with a
delegated cgroup v2 (WEKNORA_PLUGIN_CGROUP), memory.max and cpu.max.
2026-09-26 11:51:35 +08:00

41 lines
1.4 KiB
Go

package pluginapi
// OptionsPath answers the choices of a form field whose schema names
// x-options {name}: projects of the account the form is being filled for,
// channels of a workspace, and so on. The envelope's config carries what
// the form holds right now (stored secrets filled in for fields the user did
// not change), in the scope the form edits.
func OptionsPath(name string) string { return "/v1/options/" + name }
// Option scopes: which configuration the form edits.
const (
OptionsScopeSystem = "system"
OptionsScopeTenant = "tenant"
OptionsScopeInstance = "instance"
)
// OptionsInput asks for one field's choices.
type OptionsInput struct {
// Field is the dotted path of the field ("settings.project").
Field string `json:"field"`
// Scope is system, tenant or instance.
Scope string `json:"scope"`
// Contribution is "<point>/<id>" of the instance's contribution
// ("connectors/jira") when Scope is instance.
Contribution string `json:"contribution,omitempty"`
// Query is what the user typed, for fields that search (x-options.search).
Query string `json:"query,omitempty"`
}
// Option is one choice.
type Option struct {
Value any `json:"value"`
Label string `json:"label"`
Description string `json:"description,omitempty"`
}
// OptionsOutput lists the choices, in display order.
type OptionsOutput struct {
Options []Option `json:"options"`
}