Merge remote-tracking branch 'origin/main' into forrestchang/import-skill

This commit is contained in:
Jiayuan 2026-03-26 18:23:59 +08:00
commit eeb4fee1b6
148 changed files with 3040 additions and 6528 deletions

View file

@ -87,11 +87,7 @@ func resolveWorkspaceID(cmd *cobra.Command) string {
return val
}
cfg, _ := cli.LoadCLIConfig()
if cfg.WorkspaceID != "" {
return cfg.WorkspaceID
}
// Fallback: try daemon.json for workspace_id
return cli.LoadWorkspaceIDFromDaemonConfig()
return cfg.WorkspaceID
}
func runAgentList(cmd *cobra.Command, _ []string) error {

View file

@ -22,8 +22,6 @@ var daemonCmd = &cobra.Command{
func init() {
f := daemonCmd.Flags()
f.String("repos-root", "", "Base directory for task repositories (env: MULTICA_REPOS_ROOT)")
f.String("config-path", "", "Path to daemon config file (env: MULTICA_DAEMON_CONFIG)")
f.String("daemon-id", "", "Unique daemon identifier (env: MULTICA_DAEMON_ID)")
f.String("device-name", "", "Human-readable device name (env: MULTICA_DAEMON_DEVICE_NAME)")
f.String("runtime-name", "", "Runtime display name (env: MULTICA_AGENT_RUNTIME_NAME)")
@ -35,9 +33,6 @@ func init() {
func runDaemon(cmd *cobra.Command, _ []string) error {
overrides := daemon.Overrides{
ServerURL: cli.FlagOrEnv(cmd, "server-url", "MULTICA_SERVER_URL", ""),
WorkspaceID: cli.FlagOrEnv(cmd, "workspace-id", "MULTICA_WORKSPACE_ID", ""),
ReposRoot: flagString(cmd, "repos-root"),
ConfigPath: flagString(cmd, "config-path"),
DaemonID: flagString(cmd, "daemon-id"),
DeviceName: flagString(cmd, "device-name"),
RuntimeName: flagString(cmd, "runtime-name"),
@ -73,4 +68,3 @@ func flagString(cmd *cobra.Command, name string) string {
val, _ := cmd.Flags().GetString(name)
return val
}

View file

@ -0,0 +1,178 @@
package main
import (
"context"
"fmt"
"os"
"text/tabwriter"
"time"
"github.com/spf13/cobra"
"github.com/multica-ai/multica/server/internal/cli"
)
var workspaceCmd = &cobra.Command{
Use: "workspace",
Short: "Manage workspaces",
}
var workspaceListCmd = &cobra.Command{
Use: "list",
Short: "List all workspaces you belong to",
RunE: runWorkspaceList,
}
var watchCmd = &cobra.Command{
Use: "watch <workspace-id>",
Short: "Add a workspace to the daemon watch list",
Args: cobra.ExactArgs(1),
RunE: runWatch,
}
var unwatchCmd = &cobra.Command{
Use: "unwatch <workspace-id>",
Short: "Remove a workspace from the daemon watch list",
Args: cobra.ExactArgs(1),
RunE: runUnwatch,
}
var watchesCmd = &cobra.Command{
Use: "watches",
Short: "List workspaces the daemon is watching",
RunE: runWatches,
}
func init() {
workspaceCmd.AddCommand(workspaceListCmd)
}
func runWorkspaceList(cmd *cobra.Command, _ []string) error {
serverURL := resolveServerURL(cmd)
token := resolveToken()
if token == "" {
return fmt.Errorf("not authenticated: run 'multica auth login' first")
}
client := cli.NewAPIClient(serverURL, "", token)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
var workspaces []struct {
ID string `json:"id"`
Name string `json:"name"`
}
if err := client.GetJSON(ctx, "/api/workspaces", &workspaces); err != nil {
return fmt.Errorf("list workspaces: %w", err)
}
if len(workspaces) == 0 {
fmt.Fprintln(os.Stderr, "No workspaces found.")
return nil
}
// Load watched set for marking.
cfg, _ := cli.LoadCLIConfig()
watched := make(map[string]bool)
for _, w := range cfg.WatchedWorkspaces {
watched[w.ID] = true
}
w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
fmt.Fprintln(w, "ID\tNAME\tWATCHING")
for _, ws := range workspaces {
mark := ""
if watched[ws.ID] {
mark = "*"
}
fmt.Fprintf(w, "%s\t%s\t%s\n", ws.ID, ws.Name, mark)
}
return w.Flush()
}
func runWatch(cmd *cobra.Command, args []string) error {
workspaceID := args[0]
serverURL := resolveServerURL(cmd)
token := resolveToken()
if token == "" {
return fmt.Errorf("not authenticated: run 'multica auth login' first")
}
client := cli.NewAPIClient(serverURL, "", token)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
var ws struct {
ID string `json:"id"`
Name string `json:"name"`
}
if err := client.GetJSON(ctx, "/api/workspaces/"+workspaceID, &ws); err != nil {
return fmt.Errorf("workspace not found: %w", err)
}
cfg, err := cli.LoadCLIConfig()
if err != nil {
return err
}
if !cfg.AddWatchedWorkspace(ws.ID, ws.Name) {
fmt.Fprintf(os.Stderr, "Already watching workspace %s (%s)\n", ws.ID, ws.Name)
return nil
}
if cfg.WorkspaceID == "" {
cfg.WorkspaceID = ws.ID
fmt.Fprintf(os.Stderr, "Set default workspace to %s (%s)\n", ws.ID, ws.Name)
}
if err := cli.SaveCLIConfig(cfg); err != nil {
return err
}
fmt.Fprintf(os.Stderr, "Watching workspace %s (%s)\n", ws.ID, ws.Name)
return nil
}
func runUnwatch(_ *cobra.Command, args []string) error {
workspaceID := args[0]
cfg, err := cli.LoadCLIConfig()
if err != nil {
return err
}
if !cfg.RemoveWatchedWorkspace(workspaceID) {
return fmt.Errorf("workspace %s is not being watched", workspaceID)
}
if err := cli.SaveCLIConfig(cfg); err != nil {
return err
}
fmt.Fprintf(os.Stderr, "Stopped watching workspace %s\n", workspaceID)
return nil
}
func runWatches(_ *cobra.Command, _ []string) error {
cfg, err := cli.LoadCLIConfig()
if err != nil {
return err
}
if len(cfg.WatchedWorkspaces) == 0 {
fmt.Fprintln(os.Stderr, "No watched workspaces. Run 'multica watch <id>' to add one.")
return nil
}
w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
fmt.Fprintln(w, "ID\tNAME")
for _, ws := range cfg.WatchedWorkspaces {
name := ws.Name
if name == "" {
name = "-"
}
fmt.Fprintf(w, "%s\t%s\n", ws.ID, name)
}
return w.Flush()
}

View file

@ -28,6 +28,10 @@ func init() {
rootCmd.AddCommand(daemonCmd)
rootCmd.AddCommand(agentCmd)
rootCmd.AddCommand(runtimeCmd)
rootCmd.AddCommand(workspaceCmd)
rootCmd.AddCommand(watchCmd)
rootCmd.AddCommand(unwatchCmd)
rootCmd.AddCommand(watchesCmd)
rootCmd.AddCommand(configCmd)
rootCmd.AddCommand(statusCmd)
rootCmd.AddCommand(versionCmd)