mirror of
https://github.com/langgenius/dify.git
synced 2026-07-20 09:38:32 +08:00
499 lines
12 KiB
Go
499 lines
12 KiB
Go
package agentcli
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
const defaultConfigBase = ".dify_conf"
|
|
|
|
// RunConfigManifest executes the `config manifest` command.
|
|
func RunConfigManifest(env *Environment) error {
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
body, err := client.GetConfigManifest(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(string(body))
|
|
return nil
|
|
}
|
|
|
|
// RunConfigSkillsPull executes the `config skills pull` command.
|
|
func RunConfigSkillsPull(env *Environment, names []string, localDir string, jsonOutput bool) error {
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Get manifest to find available skills if no names specified
|
|
if len(names) == 0 {
|
|
body, err := client.GetConfigManifest(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var manifest struct {
|
|
Skills struct {
|
|
Items []struct {
|
|
Name string `json:"name"`
|
|
} `json:"items"`
|
|
} `json:"skills"`
|
|
}
|
|
if err := json.Unmarshal(body, &manifest); err != nil {
|
|
return fmt.Errorf("parse manifest: %w", err)
|
|
}
|
|
for _, item := range manifest.Skills.Items {
|
|
names = append(names, item.Name)
|
|
}
|
|
}
|
|
|
|
targetDir := localDir
|
|
if targetDir == "" {
|
|
targetDir = filepath.Join(defaultConfigBase, "skills")
|
|
}
|
|
if err := os.MkdirAll(targetDir, 0o755); err != nil {
|
|
return fmt.Errorf("create target directory: %w", err)
|
|
}
|
|
|
|
type pullItem struct {
|
|
Name string `json:"name"`
|
|
ArchivePath string `json:"archive_path"`
|
|
DirectoryPath string `json:"directory_path"`
|
|
SkillMD string `json:"skill_md"`
|
|
}
|
|
var items []pullItem
|
|
|
|
for _, name := range names {
|
|
archiveBytes, err := client.PullConfigSkill(ctx, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
archivePath := filepath.Join(targetDir, name+".zip")
|
|
skillDir := filepath.Join(targetDir, name)
|
|
if err := os.MkdirAll(skillDir, 0o755); err != nil {
|
|
return fmt.Errorf("create skill directory: %w", err)
|
|
}
|
|
if err := os.WriteFile(archivePath, archiveBytes, 0o644); err != nil {
|
|
return fmt.Errorf("write archive: %w", err)
|
|
}
|
|
|
|
// Extract archive
|
|
if err := extractZipArchive(archivePath, skillDir); err != nil {
|
|
return fmt.Errorf("extract skill archive: %w", err)
|
|
}
|
|
|
|
skillMDPath := filepath.Join(skillDir, "SKILL.md")
|
|
skillMD := ""
|
|
if data, err := os.ReadFile(skillMDPath); err == nil {
|
|
skillMD = string(data)
|
|
}
|
|
|
|
items = append(items, pullItem{
|
|
Name: name,
|
|
ArchivePath: archivePath,
|
|
DirectoryPath: skillDir,
|
|
SkillMD: skillMD,
|
|
})
|
|
}
|
|
|
|
if jsonOutput {
|
|
out, _ := json.Marshal(map[string]any{"items": items})
|
|
fmt.Println(string(out))
|
|
return nil
|
|
}
|
|
|
|
for i, item := range items {
|
|
if i > 0 {
|
|
fmt.Println()
|
|
}
|
|
fmt.Println(item.DirectoryPath)
|
|
fmt.Print(item.SkillMD)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RunConfigFilesPull executes the `config files pull` command.
|
|
func RunConfigFilesPull(env *Environment, names []string, localDir string, jsonOutput bool) error {
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
ctx := context.Background()
|
|
|
|
if len(names) == 0 {
|
|
body, err := client.GetConfigManifest(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var manifest struct {
|
|
Files struct {
|
|
Items []struct {
|
|
Name string `json:"name"`
|
|
} `json:"items"`
|
|
} `json:"files"`
|
|
}
|
|
if err := json.Unmarshal(body, &manifest); err != nil {
|
|
return fmt.Errorf("parse manifest: %w", err)
|
|
}
|
|
for _, item := range manifest.Files.Items {
|
|
names = append(names, item.Name)
|
|
}
|
|
}
|
|
|
|
targetDir := localDir
|
|
if targetDir == "" {
|
|
targetDir = filepath.Join(defaultConfigBase, "files")
|
|
}
|
|
if err := os.MkdirAll(targetDir, 0o755); err != nil {
|
|
return fmt.Errorf("create target directory: %w", err)
|
|
}
|
|
|
|
type fileItem struct {
|
|
Name string `json:"name"`
|
|
Path string `json:"path"`
|
|
}
|
|
var items []fileItem
|
|
|
|
for _, name := range names {
|
|
payload, err := client.PullConfigFile(ctx, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
targetPath := filepath.Join(targetDir, name)
|
|
if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil {
|
|
return fmt.Errorf("create parent directory: %w", err)
|
|
}
|
|
if err := os.WriteFile(targetPath, payload, 0o644); err != nil {
|
|
return fmt.Errorf("write file: %w", err)
|
|
}
|
|
items = append(items, fileItem{Name: name, Path: targetPath})
|
|
}
|
|
|
|
if jsonOutput {
|
|
out, _ := json.Marshal(map[string]any{"items": items})
|
|
fmt.Println(string(out))
|
|
return nil
|
|
}
|
|
|
|
for _, item := range items {
|
|
fmt.Println(item.Path)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RunConfigSkillsPush executes the `config skills push` command.
|
|
func RunConfigSkillsPush(env *Environment, paths []string) error {
|
|
if len(paths) == 0 {
|
|
return fmt.Errorf("at least one skill directory is required")
|
|
}
|
|
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
type skillPushItem struct {
|
|
Name string `json:"name"`
|
|
FileRef *DriveFileRef `json:"file_ref"`
|
|
}
|
|
var skills []skillPushItem
|
|
|
|
for _, path := range paths {
|
|
absPath, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return fmt.Errorf("resolve path: %w", err)
|
|
}
|
|
info, err := os.Stat(absPath)
|
|
if err != nil || !info.IsDir() {
|
|
return fmt.Errorf("config skill path must be a directory: %s", absPath)
|
|
}
|
|
skillMDPath := filepath.Join(absPath, "SKILL.md")
|
|
if _, err := os.Stat(skillMDPath); os.IsNotExist(err) {
|
|
return fmt.Errorf("config skill directory must contain SKILL.md: %s", absPath)
|
|
}
|
|
|
|
// Build archive and upload
|
|
archivePath, err := buildSkillArchive(absPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = os.Remove(archivePath) }()
|
|
|
|
name := filepath.Base(absPath)
|
|
commitItem, err := uploadAndPrepareCommitItem(client, archivePath, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
skills = append(skills, skillPushItem{
|
|
Name: name,
|
|
FileRef: &DriveFileRef{Kind: commitItem.FileRef.Kind, ID: commitItem.FileRef.ID},
|
|
})
|
|
}
|
|
|
|
payload := map[string]any{
|
|
"skills": skills,
|
|
"files": []any{},
|
|
}
|
|
|
|
body, err := client.PushConfig(context.Background(), payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(string(body))
|
|
return nil
|
|
}
|
|
|
|
// RunConfigFilesPush executes the `config files push` command.
|
|
func RunConfigFilesPush(env *Environment, paths []string) error {
|
|
if len(paths) == 0 {
|
|
return fmt.Errorf("at least one file path is required")
|
|
}
|
|
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
type filePushItem struct {
|
|
Name string `json:"name"`
|
|
FileRef *DriveFileRef `json:"file_ref"`
|
|
}
|
|
var files []filePushItem
|
|
|
|
for _, path := range paths {
|
|
absPath, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return fmt.Errorf("resolve path: %w", err)
|
|
}
|
|
info, err := os.Stat(absPath)
|
|
if err != nil || info.IsDir() {
|
|
return fmt.Errorf("config file path must be a regular file: %s", absPath)
|
|
}
|
|
|
|
name := filepath.Base(absPath)
|
|
commitItem, err := uploadAndPrepareCommitItem(client, absPath, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
files = append(files, filePushItem{
|
|
Name: name,
|
|
FileRef: &DriveFileRef{Kind: commitItem.FileRef.Kind, ID: commitItem.FileRef.ID},
|
|
})
|
|
}
|
|
|
|
payload := map[string]any{
|
|
"files": files,
|
|
"skills": []any{},
|
|
}
|
|
|
|
body, err := client.PushConfig(context.Background(), payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(string(body))
|
|
return nil
|
|
}
|
|
|
|
// RunConfigSkillsDelete executes the `config skills delete` command.
|
|
func RunConfigSkillsDelete(env *Environment, names []string) error {
|
|
if len(names) == 0 {
|
|
return fmt.Errorf("at least one skill name is required")
|
|
}
|
|
|
|
type skillDeleteItem struct {
|
|
Name string `json:"name"`
|
|
FileRef *any `json:"file_ref"`
|
|
}
|
|
var skills []skillDeleteItem
|
|
for _, name := range names {
|
|
skills = append(skills, skillDeleteItem{Name: name, FileRef: nil})
|
|
}
|
|
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
payload := map[string]any{
|
|
"skills": skills,
|
|
"files": []any{},
|
|
}
|
|
|
|
body, err := client.PushConfig(context.Background(), payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(string(body))
|
|
return nil
|
|
}
|
|
|
|
// RunConfigFilesDelete executes the `config files delete` command.
|
|
func RunConfigFilesDelete(env *Environment, names []string) error {
|
|
if len(names) == 0 {
|
|
return fmt.Errorf("at least one file name is required")
|
|
}
|
|
|
|
type fileDeleteItem struct {
|
|
Name string `json:"name"`
|
|
FileRef *any `json:"file_ref"`
|
|
}
|
|
var files []fileDeleteItem
|
|
for _, name := range names {
|
|
files = append(files, fileDeleteItem{Name: name, FileRef: nil})
|
|
}
|
|
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
payload := map[string]any{
|
|
"files": files,
|
|
"skills": []any{},
|
|
}
|
|
|
|
body, err := client.PushConfig(context.Background(), payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(string(body))
|
|
return nil
|
|
}
|
|
|
|
// RunConfigEnvPush executes the `config env push` command.
|
|
func RunConfigEnvPush(env *Environment, localPath string) error {
|
|
var envText string
|
|
if localPath == "-" {
|
|
data, err := os.ReadFile("/dev/stdin")
|
|
if err != nil {
|
|
return fmt.Errorf("read stdin: %w", err)
|
|
}
|
|
envText = string(data)
|
|
} else {
|
|
absPath, err := filepath.Abs(localPath)
|
|
if err != nil {
|
|
return fmt.Errorf("resolve path: %w", err)
|
|
}
|
|
data, err := os.ReadFile(absPath)
|
|
if err != nil {
|
|
return fmt.Errorf("read file: %w", err)
|
|
}
|
|
envText = string(data)
|
|
}
|
|
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
body, err := client.PatchConfigEnv(context.Background(), envText)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(string(body))
|
|
return nil
|
|
}
|
|
|
|
// RunConfigNotePull executes the `config note pull` command.
|
|
func RunConfigNotePull(env *Environment, localPath string) error {
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
body, err := client.GetConfigManifest(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var manifest struct {
|
|
Note string `json:"note"`
|
|
}
|
|
if err := json.Unmarshal(body, &manifest); err != nil {
|
|
return fmt.Errorf("parse manifest: %w", err)
|
|
}
|
|
|
|
targetPath := localPath
|
|
if targetPath == "" {
|
|
targetPath = filepath.Join(defaultConfigBase, "note.md")
|
|
}
|
|
absPath, err := filepath.Abs(targetPath)
|
|
if err != nil {
|
|
return fmt.Errorf("resolve path: %w", err)
|
|
}
|
|
if err := os.MkdirAll(filepath.Dir(absPath), 0o755); err != nil {
|
|
return fmt.Errorf("create directory: %w", err)
|
|
}
|
|
if err := os.WriteFile(absPath, []byte(manifest.Note), 0o644); err != nil {
|
|
return fmt.Errorf("write note: %w", err)
|
|
}
|
|
fmt.Println(absPath)
|
|
return nil
|
|
}
|
|
|
|
// RunConfigNotePush executes the `config note push` command.
|
|
func RunConfigNotePush(env *Environment, localPath string) error {
|
|
var note string
|
|
if localPath == "-" {
|
|
data, err := os.ReadFile("/dev/stdin")
|
|
if err != nil {
|
|
return fmt.Errorf("read stdin: %w", err)
|
|
}
|
|
note = string(data)
|
|
} else {
|
|
targetPath := localPath
|
|
if targetPath == "" {
|
|
targetPath = filepath.Join(defaultConfigBase, "note.md")
|
|
}
|
|
absPath, err := filepath.Abs(targetPath)
|
|
if err != nil {
|
|
return fmt.Errorf("resolve path: %w", err)
|
|
}
|
|
data, err := os.ReadFile(absPath)
|
|
if err != nil {
|
|
return fmt.Errorf("read file: %w", err)
|
|
}
|
|
note = string(data)
|
|
}
|
|
|
|
client, err := NewStubClient(env)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = client.Close() }()
|
|
|
|
body, err := client.PutConfigNote(context.Background(), note)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(string(body))
|
|
return nil
|
|
}
|
|
|
|
// extractZipArchive extracts a zip file into targetDir.
|
|
func extractZipArchive(archivePath string, targetDir string) error {
|
|
return extractZip(archivePath, targetDir)
|
|
}
|