initial drop: Dialectic.PlexumPlugin v0.1
Port of Dialectic.OpenclawPlugin to the Plexum SDK. 8 dialectic_*
tools wired to Dialectic.Backend over HTTP:
list_topics, topic_detail, list_arguments, propose_topic,
signup, post_argument, submit_verdict, view_verdict
Differences from the OpenClaw port worth noting:
- Per-agent API key storage: OpenClaw used secret-mgr (one entry
per agent's keyspace). Plexum has no secret-mgr; v1 stores
keys directly in plugin config (apiKey + agentKeys map).
- Agent identity at tool dispatch: OpenClaw framework surfaces
ctx.agentId; Plexum SDK doesn't yet plumb the calling agent
through ToolPlugin.CallTool. v1 falls back to
config.defaultAgentID — same stop-gap HarborForge.PlexumPlugin
is on. Tracked as upstream SDK work.
- HF on_call coverage pre-check on signup: stub that always
returns "skipped", matching OpenClaw v1's behavior (HarborForge
never shipped the cross-plugin coverage query). pre_validated
is sent as false so the backend records audit honestly.
DIALECTIC_PLUGIN_BYPASS_HF=1 env retains parity with OpenClaw.
- Activation: lazy (no background services, unlike HarborForge's
eager-spawn for the calendar scheduler + monitor bridge).
Backend client follows the bearer-auth contract OpenClaw's
backend-client.ts established; endpoint shapes are unchanged.
This commit is contained in:
99
internal/config/config.go
Normal file
99
internal/config/config.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// Package config loads the Dialectic plugin's per-profile config from
|
||||
// <profile>/plugins/dialectic/config.json. Mirrors the resolved shape
|
||||
// of Dialectic.OpenclawPlugin (backendUrl + per-agent api key), adapted
|
||||
// for Plexum's profile layout.
|
||||
//
|
||||
// OpenClaw stored per-agent api keys in secret-mgr (one read per
|
||||
// agent's keyspace). Plexum has no secret-mgr; v1 stores keys directly
|
||||
// in this config and falls back to a single shared apiKey when the
|
||||
// caller's agent id has no override entry.
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
BackendURL string `json:"backendUrl,omitempty"`
|
||||
|
||||
// APIKey is the default bearer token used when AgentKeys has no
|
||||
// entry for the calling agent. Equivalent to a claw-level api key.
|
||||
APIKey string `json:"apiKey,omitempty"`
|
||||
|
||||
// AgentKeys maps agent_id → bearer token. Overrides APIKey when
|
||||
// the caller's agent id matches an entry.
|
||||
AgentKeys map[string]string `json:"agentKeys,omitempty"`
|
||||
|
||||
// DefaultAgentID is the agent_id the plugin reports when the host
|
||||
// hasn't surfaced the calling agent via ctx. Set this on single-
|
||||
// agent claws; multi-agent setups should leave it empty and rely
|
||||
// on the tool dispatcher's best-effort resolution.
|
||||
DefaultAgentID string `json:"defaultAgentID,omitempty"`
|
||||
}
|
||||
|
||||
type Resolved struct {
|
||||
BackendURL string
|
||||
APIKey string
|
||||
AgentKeys map[string]string
|
||||
DefaultAgentID string
|
||||
}
|
||||
|
||||
const DefaultBackendURL = "https://dialectic-api.hangman-lab.top"
|
||||
|
||||
func PluginConfigDir(profileRoot string) string {
|
||||
return filepath.Join(profileRoot, "plugins", "dialectic")
|
||||
}
|
||||
|
||||
func PluginConfigPath(profileRoot string) string {
|
||||
return filepath.Join(PluginConfigDir(profileRoot), "config.json")
|
||||
}
|
||||
|
||||
func Load(profileRoot string) (Config, error) {
|
||||
path := PluginConfigPath(profileRoot)
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return Config{}, nil
|
||||
}
|
||||
return Config{}, fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
if len(raw) == 0 {
|
||||
return Config{}, nil
|
||||
}
|
||||
var c Config
|
||||
if err := json.Unmarshal(raw, &c); err != nil {
|
||||
return Config{}, fmt.Errorf("parse %s: %w", path, err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func Resolve(c Config) Resolved {
|
||||
out := Resolved{
|
||||
BackendURL: DefaultBackendURL,
|
||||
APIKey: c.APIKey,
|
||||
AgentKeys: c.AgentKeys,
|
||||
DefaultAgentID: c.DefaultAgentID,
|
||||
}
|
||||
if c.BackendURL != "" {
|
||||
out.BackendURL = c.BackendURL
|
||||
}
|
||||
if env := os.Getenv("DIALECTIC_BACKEND_URL"); env != "" {
|
||||
out.BackendURL = env
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ResolveAPIKey picks the bearer token for the calling agent. Returns
|
||||
// "" iff neither an agent-specific key nor a default apiKey is set.
|
||||
func (r Resolved) ResolveAPIKey(agentID string) string {
|
||||
if agentID != "" {
|
||||
if k, ok := r.AgentKeys[agentID]; ok && k != "" {
|
||||
return k
|
||||
}
|
||||
}
|
||||
return r.APIKey
|
||||
}
|
||||
Reference in New Issue
Block a user