agent/pkg/agentic/platform.go
Virgil 85f3a02361 feat(agentic): add fleet platform actions and commands
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-31 11:02:11 +00:00

784 lines
25 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"context"
core "dappco.re/go/core"
)
// node := agentic.FleetNode{AgentID: "charon", Platform: "linux", Status: "online"}
type FleetNode struct {
ID int `json:"id"`
AgentID string `json:"agent_id"`
Platform string `json:"platform"`
Models []string `json:"models,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
Status string `json:"status"`
ComputeBudget map[string]any `json:"compute_budget,omitempty"`
CurrentTaskID int `json:"current_task_id,omitempty"`
LastHeartbeatAt string `json:"last_heartbeat_at,omitempty"`
RegisteredAt string `json:"registered_at,omitempty"`
}
// task := agentic.FleetTask{ID: 7, Repo: "go-io", Task: "Fix tests", Status: "assigned"}
type FleetTask struct {
ID int `json:"id"`
Repo string `json:"repo"`
Branch string `json:"branch,omitempty"`
Task string `json:"task"`
Template string `json:"template,omitempty"`
AgentModel string `json:"agent_model,omitempty"`
Status string `json:"status"`
Result map[string]any `json:"result,omitempty"`
Findings []map[string]any `json:"findings,omitempty"`
Changes map[string]any `json:"changes,omitempty"`
Report map[string]any `json:"report,omitempty"`
StartedAt string `json:"started_at,omitempty"`
CompletedAt string `json:"completed_at,omitempty"`
}
// out := agentic.FleetNodesOutput{Total: 2, Nodes: []agentic.FleetNode{{AgentID: "charon"}}}
type FleetNodesOutput struct {
Total int `json:"total"`
Nodes []FleetNode `json:"nodes"`
}
// stats := agentic.FleetStats{NodesOnline: 2, TasksToday: 5}
type FleetStats struct {
NodesOnline int `json:"nodes_online"`
TasksToday int `json:"tasks_today"`
TasksWeek int `json:"tasks_week"`
ReposTouched int `json:"repos_touched"`
FindingsTotal int `json:"findings_total"`
ComputeHours int `json:"compute_hours"`
}
// status := agentic.SyncStatusOutput{AgentID: "charon", Status: "online"}
type SyncStatusOutput struct {
AgentID string `json:"agent_id"`
Status string `json:"status"`
LastPushAt string `json:"last_push_at,omitempty"`
LastPullAt string `json:"last_pull_at,omitempty"`
Queued int `json:"queued"`
ContextCount int `json:"context_count"`
RemoteError string `json:"remote_error,omitempty"`
}
// balance := agentic.CreditBalance{AgentID: "charon", Balance: 12}
type CreditBalance struct {
AgentID string `json:"agent_id"`
Balance int `json:"balance"`
Entries int `json:"entries"`
}
// entry := agentic.CreditEntry{ID: 4, TaskType: "fleet-task", Amount: 2}
type CreditEntry struct {
ID int `json:"id"`
TaskType string `json:"task_type"`
Amount int `json:"amount"`
BalanceAfter int `json:"balance_after"`
Description string `json:"description,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
}
// out := agentic.CreditsHistoryOutput{Total: 1, Entries: []agentic.CreditEntry{{ID: 1}}}
type CreditsHistoryOutput struct {
Total int `json:"total"`
Entries []CreditEntry `json:"entries"`
}
// caps := agentic.SubscriptionCapabilities{Available: []string{"claude", "openai"}}
type SubscriptionCapabilities struct {
Providers map[string]bool `json:"providers,omitempty"`
Available []string `json:"available,omitempty"`
}
// result := c.Action("agent.sync.status").Run(ctx, core.NewOptions())
func (s *PrepSubsystem) handleSyncStatus(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
agentID = AgentName()
}
output := SyncStatusOutput{
AgentID: agentID,
Status: "offline",
Queued: len(readSyncQueue()),
ContextCount: len(readSyncContext()),
}
if s.syncToken() == "" {
return core.Result{Value: output, OK: true}
}
path := appendQueryParam("/v1/agent/status", "agent_id", agentID)
result := s.platformPayload(ctx, "agent.sync.status", "GET", path, nil)
if !result.OK {
err, _ := result.Value.(error)
if err != nil {
output.RemoteError = err.Error()
}
return core.Result{Value: output, OK: true}
}
data := payloadDataMap(result.Value.(map[string]any))
if len(data) == 0 {
return core.Result{Value: output, OK: true}
}
output.Status = stringValue(data["status"])
output.LastPushAt = stringValue(data["last_push_at"])
output.LastPullAt = stringValue(data["last_pull_at"])
if output.Status == "" {
output.Status = "online"
}
return core.Result{Value: output, OK: true}
}
// result := c.Action("agent.fleet.register").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetRegister(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agent.fleet.register", "agent_id is required", nil), OK: false}
}
platform := optionStringValue(options, "platform")
if platform == "" {
platform = "unknown"
}
body := map[string]any{
"agent_id": agentID,
"platform": platform,
}
if models := optionStringSliceValue(options, "models"); len(models) > 0 {
body["models"] = models
}
if capabilities := optionStringSliceValue(options, "capabilities"); len(capabilities) > 0 {
body["capabilities"] = capabilities
}
result := s.platformPayload(ctx, "agent.fleet.register", "POST", "/v1/fleet/register", body)
if !result.OK {
return result
}
return core.Result{Value: parseFleetNode(payloadDataMap(result.Value.(map[string]any))), OK: true}
}
// result := c.Action("agent.fleet.heartbeat").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetHeartbeat(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
status := optionStringValue(options, "status")
if agentID == "" || status == "" {
return core.Result{Value: core.E("agent.fleet.heartbeat", "agent_id and status are required", nil), OK: false}
}
body := map[string]any{
"agent_id": agentID,
"status": status,
}
if budget := optionAnyMapValue(options, "compute_budget", "compute-budget"); len(budget) > 0 {
body["compute_budget"] = budget
}
result := s.platformPayload(ctx, "agent.fleet.heartbeat", "POST", "/v1/fleet/heartbeat", body)
if !result.OK {
return result
}
return core.Result{Value: parseFleetNode(payloadDataMap(result.Value.(map[string]any))), OK: true}
}
// result := c.Action("agent.fleet.deregister").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetDeregister(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agent.fleet.deregister", "agent_id is required", nil), OK: false}
}
result := s.platformPayload(ctx, "agent.fleet.deregister", "POST", "/v1/fleet/deregister", map[string]any{
"agent_id": agentID,
})
if !result.OK {
return result
}
return core.Result{Value: map[string]any{
"agent_id": agentID,
"deregistered": true,
}, OK: true}
}
// result := c.Action("agent.fleet.nodes").Run(ctx, core.NewOptions())
func (s *PrepSubsystem) handleFleetNodes(ctx context.Context, options core.Options) core.Result {
path := "/v1/fleet/nodes"
path = appendQueryParam(path, "status", optionStringValue(options, "status"))
path = appendQueryParam(path, "platform", optionStringValue(options, "platform"))
result := s.platformPayload(ctx, "agent.fleet.nodes", "GET", path, nil)
if !result.OK {
return result
}
return core.Result{Value: parseFleetNodesOutput(result.Value.(map[string]any)), OK: true}
}
// result := c.Action("agent.fleet.task.assign").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetAssignTask(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
repo := optionStringValue(options, "repo")
task := optionStringValue(options, "task")
if agentID == "" || repo == "" || task == "" {
return core.Result{Value: core.E("agent.fleet.task.assign", "agent_id, repo, and task are required", nil), OK: false}
}
body := map[string]any{
"agent_id": agentID,
"repo": repo,
"task": task,
}
if branch := optionStringValue(options, "branch"); branch != "" {
body["branch"] = branch
}
if template := optionStringValue(options, "template"); template != "" {
body["template"] = template
}
if agentModel := optionStringValue(options, "agent_model", "agent-model"); agentModel != "" {
body["agent_model"] = agentModel
}
result := s.platformPayload(ctx, "agent.fleet.task.assign", "POST", "/v1/fleet/task/assign", body)
if !result.OK {
return result
}
return core.Result{Value: parseFleetTask(payloadDataMap(result.Value.(map[string]any))), OK: true}
}
// result := c.Action("agent.fleet.task.complete").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetCompleteTask(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
taskID := optionIntValue(options, "task_id", "task-id")
if agentID == "" || taskID == 0 {
return core.Result{Value: core.E("agent.fleet.task.complete", "agent_id and task_id are required", nil), OK: false}
}
body := map[string]any{
"agent_id": agentID,
"task_id": taskID,
}
if resultMap := optionAnyMapValue(options, "result"); len(resultMap) > 0 {
body["result"] = resultMap
}
if findings := optionAnyMapSliceValue(options, "findings"); len(findings) > 0 {
body["findings"] = findings
}
if changes := optionAnyMapValue(options, "changes"); len(changes) > 0 {
body["changes"] = changes
}
if report := optionAnyMapValue(options, "report"); len(report) > 0 {
body["report"] = report
}
result := s.platformPayload(ctx, "agent.fleet.task.complete", "POST", "/v1/fleet/task/complete", body)
if !result.OK {
return result
}
return core.Result{Value: parseFleetTask(payloadDataMap(result.Value.(map[string]any))), OK: true}
}
// result := c.Action("agent.fleet.task.next").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleFleetNextTask(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agent.fleet.task.next", "agent_id is required", nil), OK: false}
}
path := appendQueryParam("/v1/fleet/task/next", "agent_id", agentID)
path = appendQuerySlice(path, "capabilities[]", optionStringSliceValue(options, "capabilities"))
result := s.platformPayload(ctx, "agent.fleet.task.next", "GET", path, nil)
if !result.OK {
return result
}
data := payloadDataMap(result.Value.(map[string]any))
if len(data) == 0 {
var task *FleetTask
return core.Result{Value: task, OK: true}
}
task := parseFleetTask(data)
return core.Result{Value: &task, OK: true}
}
// result := c.Action("agent.fleet.stats").Run(ctx, core.NewOptions())
func (s *PrepSubsystem) handleFleetStats(ctx context.Context, options core.Options) core.Result {
result := s.platformPayload(ctx, "agent.fleet.stats", "GET", "/v1/fleet/stats", nil)
if !result.OK {
return result
}
return core.Result{Value: parseFleetStats(payloadDataMap(result.Value.(map[string]any))), OK: true}
}
// result := c.Action("agent.credits.award").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleCreditsAward(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
taskType := optionStringValue(options, "task_type", "task-type")
amount := optionIntValue(options, "amount")
if agentID == "" || taskType == "" || amount == 0 {
return core.Result{Value: core.E("agent.credits.award", "agent_id, task_type, and amount are required", nil), OK: false}
}
body := map[string]any{
"agent_id": agentID,
"task_type": taskType,
"amount": amount,
}
if fleetNodeID := optionIntValue(options, "fleet_node_id", "fleet-node-id"); fleetNodeID > 0 {
body["fleet_node_id"] = fleetNodeID
}
if description := optionStringValue(options, "description"); description != "" {
body["description"] = description
}
result := s.platformPayload(ctx, "agent.credits.award", "POST", "/v1/credits/award", body)
if !result.OK {
return result
}
return core.Result{Value: parseCreditEntry(payloadDataMap(result.Value.(map[string]any))), OK: true}
}
// result := c.Action("agent.credits.balance").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleCreditsBalance(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agent.credits.balance", "agent_id is required", nil), OK: false}
}
path := core.Concat("/v1/credits/balance/", agentID)
result := s.platformPayload(ctx, "agent.credits.balance", "GET", path, nil)
if !result.OK {
return result
}
return core.Result{Value: parseCreditBalance(payloadDataMap(result.Value.(map[string]any))), OK: true}
}
// result := c.Action("agent.credits.history").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleCreditsHistory(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agent.credits.history", "agent_id is required", nil), OK: false}
}
path := core.Concat("/v1/credits/history/", agentID)
if limit := optionIntValue(options, "limit"); limit > 0 {
path = appendQueryParam(path, "limit", core.Sprint(limit))
}
result := s.platformPayload(ctx, "agent.credits.history", "GET", path, nil)
if !result.OK {
return result
}
return core.Result{Value: parseCreditsHistoryOutput(result.Value.(map[string]any)), OK: true}
}
// result := c.Action("agent.subscription.detect").Run(ctx, core.NewOptions())
func (s *PrepSubsystem) handleSubscriptionDetect(ctx context.Context, options core.Options) core.Result {
body := map[string]any{}
if apiKeys := optionStringMapValue(options, "api_keys", "api-keys"); len(apiKeys) > 0 {
body["api_keys"] = apiKeys
}
result := s.platformPayload(ctx, "agent.subscription.detect", "POST", "/v1/subscription/detect", body)
if !result.OK {
return result
}
return core.Result{Value: parseSubscriptionCapabilities(payloadDataMap(result.Value.(map[string]any))), OK: true}
}
// result := c.Action("agent.subscription.budget").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleSubscriptionBudget(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
if agentID == "" {
return core.Result{Value: core.E("agent.subscription.budget", "agent_id is required", nil), OK: false}
}
path := core.Concat("/v1/subscription/budget/", agentID)
result := s.platformPayload(ctx, "agent.subscription.budget", "GET", path, nil)
if !result.OK {
return result
}
return core.Result{Value: payloadDataMap(result.Value.(map[string]any)), OK: true}
}
// result := c.Action("agent.subscription.budget.update").Run(ctx, core.NewOptions(core.Option{Key: "agent_id", Value: "charon"}))
func (s *PrepSubsystem) handleSubscriptionBudgetUpdate(ctx context.Context, options core.Options) core.Result {
agentID := optionStringValue(options, "agent_id", "agent-id", "_arg")
limits := optionAnyMapValue(options, "limits")
if agentID == "" || len(limits) == 0 {
return core.Result{Value: core.E("agent.subscription.budget.update", "agent_id and limits are required", nil), OK: false}
}
path := core.Concat("/v1/subscription/budget/", agentID)
result := s.platformPayload(ctx, "agent.subscription.budget.update", "PUT", path, map[string]any{
"limits": limits,
})
if !result.OK {
return result
}
return core.Result{Value: payloadDataMap(result.Value.(map[string]any)), OK: true}
}
func (s *PrepSubsystem) platformPayload(ctx context.Context, action, method, path string, body any) core.Result {
token := s.syncToken()
if token == "" {
return core.Result{Value: core.E(action, "no platform API key configured", nil), OK: false}
}
bodyString := ""
if body != nil {
bodyString = core.JSONMarshalString(body)
}
requestResult := HTTPDo(ctx, method, core.Concat(s.syncAPIURL(), path), bodyString, token, "Bearer")
if !requestResult.OK {
return core.Result{Value: platformResultError(action, requestResult), OK: false}
}
var payload map[string]any
parseResult := core.JSONUnmarshalString(requestResult.Value.(string), &payload)
if !parseResult.OK {
err, _ := parseResult.Value.(error)
return core.Result{Value: core.E(action, "failed to parse platform response", err), OK: false}
}
return core.Result{Value: payload, OK: true}
}
func platformResultError(action string, result core.Result) error {
if err, ok := result.Value.(error); ok && err != nil {
return core.E(action, "platform request failed", err)
}
body := core.Trim(stringValue(result.Value))
if body == "" {
return core.E(action, "platform request failed", nil)
}
var payload map[string]any
if parseResult := core.JSONUnmarshalString(body, &payload); parseResult.OK {
if message := stringValue(payload["error"]); message != "" {
return core.E(action, message, nil)
}
}
return core.E(action, body, nil)
}
func payloadDataMap(payload map[string]any) map[string]any {
return anyMapValue(payload["data"])
}
func payloadDataSlice(payload map[string]any) []map[string]any {
return anyMapSliceValue(payload["data"])
}
func parseFleetNode(values map[string]any) FleetNode {
return FleetNode{
ID: intValue(values["id"]),
AgentID: stringValue(values["agent_id"]),
Platform: stringValue(values["platform"]),
Models: listValue(values["models"]),
Capabilities: listValue(values["capabilities"]),
Status: stringValue(values["status"]),
ComputeBudget: anyMapValue(values["compute_budget"]),
CurrentTaskID: intValue(values["current_task_id"]),
LastHeartbeatAt: stringValue(values["last_heartbeat_at"]),
RegisteredAt: stringValue(values["registered_at"]),
}
}
func parseFleetTask(values map[string]any) FleetTask {
return FleetTask{
ID: intValue(values["id"]),
Repo: stringValue(values["repo"]),
Branch: stringValue(values["branch"]),
Task: stringValue(values["task"]),
Template: stringValue(values["template"]),
AgentModel: stringValue(values["agent_model"]),
Status: stringValue(values["status"]),
Result: anyMapValue(values["result"]),
Findings: anyMapSliceValue(values["findings"]),
Changes: anyMapValue(values["changes"]),
Report: anyMapValue(values["report"]),
StartedAt: stringValue(values["started_at"]),
CompletedAt: stringValue(values["completed_at"]),
}
}
func parseFleetNodesOutput(payload map[string]any) FleetNodesOutput {
nodesData := payloadDataSlice(payload)
nodes := make([]FleetNode, 0, len(nodesData))
for _, values := range nodesData {
nodes = append(nodes, parseFleetNode(values))
}
total := intValue(payload["total"])
if total == 0 {
total = len(nodes)
}
return FleetNodesOutput{
Total: total,
Nodes: nodes,
}
}
func parseFleetStats(values map[string]any) FleetStats {
return FleetStats{
NodesOnline: intValue(values["nodes_online"]),
TasksToday: intValue(values["tasks_today"]),
TasksWeek: intValue(values["tasks_week"]),
ReposTouched: intValue(values["repos_touched"]),
FindingsTotal: intValue(values["findings_total"]),
ComputeHours: intValue(values["compute_hours"]),
}
}
func parseCreditEntry(values map[string]any) CreditEntry {
return CreditEntry{
ID: intValue(values["id"]),
TaskType: stringValue(values["task_type"]),
Amount: intValue(values["amount"]),
BalanceAfter: intValue(values["balance_after"]),
Description: stringValue(values["description"]),
CreatedAt: stringValue(values["created_at"]),
}
}
func parseCreditBalance(values map[string]any) CreditBalance {
return CreditBalance{
AgentID: stringValue(values["agent_id"]),
Balance: intValue(values["balance"]),
Entries: intValue(values["entries"]),
}
}
func parseCreditsHistoryOutput(payload map[string]any) CreditsHistoryOutput {
entriesData := payloadDataSlice(payload)
entries := make([]CreditEntry, 0, len(entriesData))
for _, values := range entriesData {
entries = append(entries, parseCreditEntry(values))
}
total := intValue(payload["total"])
if total == 0 {
total = len(entries)
}
return CreditsHistoryOutput{
Total: total,
Entries: entries,
}
}
func parseSubscriptionCapabilities(values map[string]any) SubscriptionCapabilities {
return SubscriptionCapabilities{
Providers: boolMapValue(values["providers"]),
Available: listValue(values["available"]),
}
}
func appendQueryParam(path, key, value string) string {
value = core.Trim(value)
if value == "" {
return path
}
separator := "?"
if core.Contains(path, "?") {
separator = "&"
}
return core.Concat(path, separator, key, "=", value)
}
func appendQuerySlice(path, key string, values []string) string {
for _, value := range values {
path = appendQueryParam(path, key, value)
}
return path
}
func optionAnyMapValue(options core.Options, keys ...string) map[string]any {
for _, key := range keys {
result := options.Get(key)
if !result.OK {
continue
}
values := anyMapValue(result.Value)
if len(values) > 0 {
return values
}
}
return nil
}
func optionAnyMapSliceValue(options core.Options, keys ...string) []map[string]any {
for _, key := range keys {
result := options.Get(key)
if !result.OK {
continue
}
values := anyMapSliceValue(result.Value)
if len(values) > 0 {
return values
}
}
return nil
}
func anyMapValue(value any) map[string]any {
switch typed := value.(type) {
case map[string]any:
return typed
case map[string]string:
values := make(map[string]any, len(typed))
for key, item := range typed {
values[key] = item
}
return values
case string:
trimmed := core.Trim(typed)
if trimmed == "" {
return nil
}
if core.HasPrefix(trimmed, "{") {
var values map[string]any
if result := core.JSONUnmarshalString(trimmed, &values); result.OK {
return values
}
var strings map[string]string
if result := core.JSONUnmarshalString(trimmed, &strings); result.OK {
return anyMapValue(strings)
}
}
values := stringMapValue(trimmed)
if len(values) > 0 {
return anyMapValue(values)
}
}
return nil
}
func anyMapSliceValue(value any) []map[string]any {
switch typed := value.(type) {
case []map[string]any:
return typed
case []any:
values := make([]map[string]any, 0, len(typed))
for _, item := range typed {
if mapValue := anyMapValue(item); len(mapValue) > 0 {
values = append(values, mapValue)
}
}
return values
case string:
trimmed := core.Trim(typed)
if trimmed == "" {
return nil
}
if core.HasPrefix(trimmed, "[") {
var values []map[string]any
if result := core.JSONUnmarshalString(trimmed, &values); result.OK {
return values
}
var generic []any
if result := core.JSONUnmarshalString(trimmed, &generic); result.OK {
return anyMapSliceValue(generic)
}
}
}
return nil
}
func boolMapValue(value any) map[string]bool {
switch typed := value.(type) {
case map[string]bool:
return typed
case map[string]any:
values := make(map[string]bool, len(typed))
for key, item := range typed {
switch resolved := item.(type) {
case bool:
values[key] = resolved
case string:
values[key] = core.Lower(core.Trim(resolved)) == "true"
default:
values[key] = intValue(resolved) > 0
}
}
return values
case string:
trimmed := core.Trim(typed)
if trimmed == "" {
return nil
}
if core.HasPrefix(trimmed, "{") {
var values map[string]bool
if result := core.JSONUnmarshalString(trimmed, &values); result.OK {
return values
}
var generic map[string]any
if result := core.JSONUnmarshalString(trimmed, &generic); result.OK {
return boolMapValue(generic)
}
}
}
return nil
}
func listValue(value any) []string {
switch typed := value.(type) {
case map[string]any:
values := make([]string, 0, len(typed))
for key, item := range typed {
if item == true || core.Trim(stringValue(item)) != "" {
values = append(values, key)
}
}
return cleanStrings(values)
default:
return stringSliceValue(value)
}
}
func intValue(value any) int {
switch typed := value.(type) {
case int:
return typed
case int64:
return int(typed)
case float64:
return int(typed)
case string:
parsed := parseInt(typed)
if parsed != 0 || core.Trim(typed) == "0" {
return parsed
}
}
return 0
}