Files
Plexum-fabric-channel-plugin/cmd/plexum-fabric-register/main.go
hzhang f8d43ae70e feat: Phase F-1 — Plexum-fabric-channel-plugin foundation
Ports the foundation of Fabric.OpenclawPlugin to a native Plexum
channel plugin (Go). F-2+ phases (socket.io inbound, wakeup gate,
tools, presence, etc.) follow.

Layout:
  internal/identity/      — fabric-identity.json registry (agent → API key)
  internal/fabric/        — REST client (Center auth + Guild messaging)
  internal/config/        — channels/<name>.json fabric extension parser
  cmd/plexum-fabric-register/      — agent registration CLI
  cmd/plexum-fabric-channel-plugin/— Plexum SDK plugin entry
  scripts/install.sh      — build + install + manifest generator

Plugin behavior (F-1):
- Reads <profile>/channels/*.json, filters plugin=plexum-fabric-channel,
  builds (plexum-channel-name → fabric channel-id) index
- Validates each bound agent's API key against Center at init
  (warmSessions); logs warning but doesn't refuse init on bad keys
- `send` MCP tool: POST plain text to the bound Fabric channel as the
  agent user; selects guild endpoint+token from cached session
- Manifest channels[] is generated by install.sh from current
  channels/*.json — re-run with --reset-manifest after adding bindings
- Plugin-private config at
  <profile>/plugins/plexum-fabric-channel/config.json
  (center_api_base, default http://localhost:7001/api)

Live smoke verified:
- plexum-fabric-register against running Fabric Center (port 7001):
  validated fak_..., wrote identity file with user_id + email captured

Tests: identity (5) + config (6) = 11 unit tests.

F-2 will hook socket.io for inbound + wakeup gating + token refresh.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 15:13:34 +01:00

92 lines
2.8 KiB
Go

// Command plexum-fabric-register binds a Plexum agent to a Fabric
// Center API key. Equivalent of openclaw's `fabric-register` script.
//
// Usage:
//
// plexum-fabric-register --api-key fak_...
// # agent id from $AGENT_ID env (set by exec MCP tool)
// plexum-fabric-register --agent-id alice --api-key fak_...
//
// Flags:
// --api-key Required. The Center-issued agent API key.
// --agent-id Optional when $AGENT_ID is set.
// --center Optional. Defaults to ${FABRIC_CENTER_API_BASE} or
// http://localhost:7001/api.
// --identity-file Optional path to fabric-identity.json (default
// ${PLEXUM_PROFILE_ROOT}/fabric-identity.json).
//
// Writes the (agent, api key, user id, email) tuple to identity file
// after validating the key against Center via /auth/agent/login.
package main
import (
"context"
"flag"
"fmt"
"os"
"time"
"git.hangman-lab.top/hzhang/Plexum-fabric-channel-plugin/internal/fabric"
"git.hangman-lab.top/hzhang/Plexum-fabric-channel-plugin/internal/identity"
)
func main() {
apiKey := flag.String("api-key", "", "Fabric Center API key (fak_...; required)")
agentID := flag.String("agent-id", "", "agent id (defaults to $AGENT_ID)")
centerBase := flag.String("center", "", "Center API base URL (default $FABRIC_CENTER_API_BASE or http://localhost:7001/api)")
identityFile := flag.String("identity-file", "", "identity registry path (default $PLEXUM_PROFILE_ROOT/fabric-identity.json)")
flag.Parse()
if *apiKey == "" {
fatalf("--api-key is required")
}
if *agentID == "" {
*agentID = os.Getenv("AGENT_ID")
}
if *agentID == "" {
fatalf("--agent-id required (or set AGENT_ID env)")
}
if *centerBase == "" {
*centerBase = os.Getenv("FABRIC_CENTER_API_BASE")
}
if *centerBase == "" {
*centerBase = "http://localhost:7001/api"
}
if *identityFile == "" {
*identityFile = identity.DefaultPath()
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
client := fabric.New(*centerBase)
sess, err := client.AgentLogin(ctx, *apiKey)
if err != nil {
fatalf("validate key against %s: %v", *centerBase, err)
}
reg, err := identity.Open(*identityFile)
if err != nil {
fatalf("open identity: %v", err)
}
reg.Set(*agentID, &identity.Entry{
FabricAPIKey: *apiKey,
FabricUserID: sess.User.ID,
FabricEmail: sess.User.Email,
Enabled: true,
})
if err := reg.Save(); err != nil {
fatalf("save identity: %v", err)
}
fmt.Printf("registered agent %s as fabric user %s (%s); %d guilds\n",
*agentID, sess.User.Email, sess.User.ID, len(sess.Guilds))
fmt.Printf("identity file: %s\n", *identityFile)
fmt.Println("restart the plexum gateway to pick up the new identity")
}
func fatalf(format string, args ...any) {
fmt.Fprintf(os.Stderr, "plexum-fabric-register: "+format+"\n", args...)
os.Exit(1)
}