Files
HarborForge.Cli/internal/help/surface.go

249 lines
11 KiB
Go

package help
import (
"encoding/json"
"os"
"git.hangman-lab.top/zhi/HarborForge.Cli/internal/client"
"git.hangman-lab.top/zhi/HarborForge.Cli/internal/config"
"git.hangman-lab.top/zhi/HarborForge.Cli/internal/mode"
"git.hangman-lab.top/zhi/HarborForge.Cli/internal/passmgr"
)
type permissionState struct {
Known bool
Permissions map[string]struct{}
}
type permissionIntrospectionResponse struct {
Username string `json:"username"`
RoleName *string `json:"role_name"`
IsAdmin bool `json:"is_admin"`
Permissions []string `json:"permissions"`
}
func CommandSurface() []Group {
perms := detectPermissionState()
groups := []Group{
{Name: "version", Description: "Show CLI version", Permitted: true},
{Name: "health", Description: "Check API health", Permitted: true},
{Name: "config", Description: "View and manage CLI configuration", Permitted: true},
{
Name: "user",
Description: "Manage users",
SubCommands: []Command{
{Name: "create", Description: "Create a user account (uses account-manager token flow)", Permitted: true},
{Name: "list", Description: "List users", Permitted: has(perms, "user.manage")},
{Name: "get", Description: "Show a user by username", Permitted: has(perms, "user.manage")},
{Name: "update", Description: "Update a user", Permitted: has(perms, "user.manage")},
{Name: "activate", Description: "Activate a user", Permitted: has(perms, "user.manage")},
{Name: "deactivate", Description: "Deactivate a user", Permitted: has(perms, "user.manage")},
{Name: "delete", Description: "Delete a user", Permitted: has(perms, "user.manage")},
},
},
{
Name: "role",
Description: "Manage roles and permissions",
SubCommands: []Command{
{Name: "list", Description: "List roles", Permitted: has(perms, "role.manage")},
{Name: "get", Description: "Show a role by name", Permitted: has(perms, "role.manage")},
{Name: "create", Description: "Create a role", Permitted: has(perms, "role.manage")},
{Name: "update", Description: "Update a role", Permitted: has(perms, "role.manage")},
{Name: "delete", Description: "Delete a role", Permitted: has(perms, "role.manage")},
{Name: "set-permissions", Description: "Replace role permissions", Permitted: has(perms, "role.manage")},
{Name: "add-permissions", Description: "Add permissions to a role", Permitted: has(perms, "role.manage")},
{Name: "remove-permissions", Description: "Remove permissions from a role", Permitted: has(perms, "role.manage")},
},
},
{
Name: "permission",
Description: "List permissions",
SubCommands: []Command{{Name: "list", Description: "List permissions", Permitted: has(perms, "role.manage")}},
},
{
Name: "project",
Description: "Manage projects",
SubCommands: []Command{
{Name: "list", Description: "List projects", Permitted: has(perms, "project.read")},
{Name: "get", Description: "Show a project by code", Permitted: has(perms, "project.read")},
{Name: "create", Description: "Create a project", Permitted: has(perms, "project.write")},
{Name: "update", Description: "Update a project", Permitted: has(perms, "project.write")},
{Name: "delete", Description: "Delete a project", Permitted: has(perms, "project.delete")},
{Name: "members", Description: "List project members", Permitted: has(perms, "project.read")},
{Name: "add-member", Description: "Add a project member", Permitted: has(perms, "project.manage_members")},
{Name: "remove-member", Description: "Remove a project member", Permitted: has(perms, "project.manage_members")},
},
},
{
Name: "milestone",
Description: "Manage milestones",
SubCommands: []Command{
{Name: "list", Description: "List milestones", Permitted: has(perms, "milestone.read")},
{Name: "get", Description: "Show a milestone by code", Permitted: has(perms, "milestone.read")},
{Name: "create", Description: "Create a milestone", Permitted: has(perms, "milestone.create")},
{Name: "update", Description: "Update a milestone", Permitted: has(perms, "milestone.write")},
{Name: "delete", Description: "Delete a milestone", Permitted: has(perms, "milestone.delete")},
{Name: "progress", Description: "Show milestone progress", Permitted: has(perms, "milestone.read")},
},
},
{
Name: "task",
Description: "Manage tasks",
SubCommands: []Command{
{Name: "list", Description: "List tasks", Permitted: has(perms, "task.read")},
{Name: "get", Description: "Show a task by code", Permitted: has(perms, "task.read")},
{Name: "create", Description: "Create a task", Permitted: has(perms, "task.create")},
{Name: "update", Description: "Update a task", Permitted: has(perms, "task.write")},
{Name: "transition", Description: "Transition a task to a new status", Permitted: has(perms, "task.write")},
{Name: "take", Description: "Assign a task to the current user", Permitted: has(perms, "task.write")},
{Name: "delete", Description: "Delete a task", Permitted: has(perms, "task.delete")},
{Name: "search", Description: "Search tasks", Permitted: has(perms, "task.read")},
},
},
{
Name: "meeting",
Description: "Manage meetings",
SubCommands: []Command{
{Name: "list", Description: "List meetings", Permitted: has(perms, "task.read")},
{Name: "get", Description: "Show a meeting by code", Permitted: has(perms, "task.read")},
{Name: "create", Description: "Create a meeting", Permitted: has(perms, "task.create")},
{Name: "update", Description: "Update a meeting", Permitted: has(perms, "task.write")},
{Name: "attend", Description: "Attend a meeting", Permitted: has(perms, "task.write")},
{Name: "delete", Description: "Delete a meeting", Permitted: has(perms, "task.delete")},
},
},
{
Name: "support",
Description: "Manage support tickets",
SubCommands: []Command{
{Name: "list", Description: "List support tickets", Permitted: has(perms, "task.read")},
{Name: "get", Description: "Show a support ticket by code", Permitted: has(perms, "task.read")},
{Name: "create", Description: "Create a support ticket", Permitted: has(perms, "task.create")},
{Name: "update", Description: "Update a support ticket", Permitted: has(perms, "task.write")},
{Name: "take", Description: "Assign a support ticket to the current user", Permitted: has(perms, "task.write")},
{Name: "transition", Description: "Transition a support ticket to a new status", Permitted: has(perms, "task.write")},
{Name: "delete", Description: "Delete a support ticket", Permitted: has(perms, "task.delete")},
},
},
{
Name: "proposal",
Description: "Manage proposals",
SubCommands: []Command{
{Name: "list", Description: "List proposals", Permitted: has(perms, "project.read")},
{Name: "get", Description: "Show a proposal by code", Permitted: has(perms, "project.read")},
{Name: "create", Description: "Create a proposal", Permitted: has(perms, "task.create")},
{Name: "update", Description: "Update a proposal", Permitted: has(perms, "task.write")},
{Name: "accept", Description: "Accept a proposal and generate story tasks", Permitted: has(perms, "propose.accept")},
{Name: "reject", Description: "Reject a proposal", Permitted: has(perms, "propose.reject")},
{Name: "reopen", Description: "Reopen a proposal", Permitted: has(perms, "propose.reopen")},
{Name: "essential", Description: "Manage proposal essentials", Permitted: has(perms, "task.create")},
},
},
{
Name: "calendar",
Description: "Manage calendar slots and plans",
SubCommands: []Command{
{Name: "schedule", Description: "Create a one-off slot", Permitted: has(perms, "task.create")},
{Name: "show", Description: "Show slots for a day", Permitted: has(perms, "task.read")},
{Name: "edit", Description: "Edit a slot", Permitted: has(perms, "task.write")},
{Name: "cancel", Description: "Cancel a slot", Permitted: has(perms, "task.write")},
{Name: "date-list", Description: "List dates with materialized slots", Permitted: has(perms, "task.read")},
{Name: "plan-schedule", Description: "Create a recurring plan", Permitted: has(perms, "task.create")},
{Name: "plan-list", Description: "List plans", Permitted: has(perms, "task.read")},
{Name: "plan-edit", Description: "Edit a plan", Permitted: has(perms, "task.write")},
{Name: "plan-cancel", Description: "Cancel a plan", Permitted: has(perms, "task.write")},
},
},
{
Name: "comment",
Description: "Manage task comments",
SubCommands: []Command{
{Name: "add", Description: "Add a comment to a task", Permitted: has(perms, "task.read")},
{Name: "list", Description: "List comments for a task", Permitted: has(perms, "task.read")},
},
},
{
Name: "worklog",
Description: "Manage work logs",
SubCommands: []Command{
{Name: "add", Description: "Add a work log entry", Permitted: has(perms, "task.read")},
{Name: "list", Description: "List work logs by task or user", Permitted: has(perms, "task.read")},
},
},
{
Name: "monitor",
Description: "Monitor servers and API keys",
SubCommands: []Command{
{Name: "overview", Description: "Show monitor overview", Permitted: has(perms, "monitor.read")},
{Name: "server", Description: "Manage monitor servers", Permitted: has(perms, "monitor.manage") || has(perms, "monitor.read")},
{Name: "api-key", Description: "Manage monitor API keys", Permitted: has(perms, "monitor.manage")},
},
},
}
for i := range groups {
groups[i].Permitted = groupPermitted(groups[i])
}
return groups
}
func detectPermissionState() permissionState {
if mode.IsPaddedCell() {
token, err := passmgr.GetToken()
if err != nil || token == "" {
return permissionState{Known: false, Permissions: map[string]struct{}{}}
}
return loadPermissionState(token)
}
return permissionState{Known: false, Permissions: map[string]struct{}{}}
}
func loadPermissionState(token string) permissionState {
cfg, err := config.Load()
if err != nil || cfg.BaseURL == "" {
return permissionState{Known: false, Permissions: map[string]struct{}{}}
}
c := client.New(cfg.BaseURL, token)
data, err := c.Get("/auth/me/permissions")
if err != nil {
return permissionState{Known: false, Permissions: map[string]struct{}{}}
}
var resp permissionIntrospectionResponse
if err := json.Unmarshal(data, &resp); err != nil {
return permissionState{Known: false, Permissions: map[string]struct{}{}}
}
perms := make(map[string]struct{}, len(resp.Permissions))
for _, perm := range resp.Permissions {
perms[perm] = struct{}{}
}
return permissionState{Known: true, Permissions: perms}
}
func has(state permissionState, perm string) bool {
// Test/development bypass: HF_TEST_MODE=1 grants all permissions
if os.Getenv("HF_TEST_MODE") == "1" {
return true
}
if !state.Known {
return false
}
_, ok := state.Permissions[perm]
return ok
}
func groupPermitted(group Group) bool {
if len(group.SubCommands) == 0 {
return group.Permitted
}
for _, cmd := range group.SubCommands {
if cmd.Permitted {
return true
}
}
return false
}