Przeglądaj źródła

feat: add bundle to plugin

Yeuoly 9 miesięcy temu
rodzic
commit
09e214f2b2

+ 111 - 0
cmd/commandline/bundle.go

@@ -0,0 +1,111 @@
+package main
+
+import (
+	"github.com/spf13/cobra"
+)
+
+var (
+	bundleCreateCommand = &cobra.Command{
+		Use:   "create",
+		Short: "Create a bundle",
+		Long:  "Create a bundle",
+		Run: func(c *cobra.Command, args []string) {
+		},
+	}
+
+	bundleAnalyzeCommand = &cobra.Command{
+		Use:   "analyze",
+		Short: "List all dependencies",
+		Long:  "List all dependencies",
+		Run: func(c *cobra.Command, args []string) {
+		},
+	}
+
+	bundleAppendDependencyCommand = &cobra.Command{
+		Use:   "append",
+		Short: "Append a dependency",
+		Long:  "Append a dependency",
+	}
+
+	bundleAppendGithubDependencyCommand = &cobra.Command{
+		Use:   "github",
+		Short: "Append a github dependency",
+		Long:  "Append a github dependency",
+		Run: func(c *cobra.Command, args []string) {
+		},
+	}
+
+	bundleAppendMarketplaceDependencyCommand = &cobra.Command{
+		Use:   "marketplace",
+		Short: "Append a marketplace dependency",
+		Long:  "Append a marketplace dependency",
+		Run: func(c *cobra.Command, args []string) {
+		},
+	}
+
+	bundleAppendPackageDependencyCommand = &cobra.Command{
+		Use:   "package",
+		Short: "Append a local package dependency",
+		Long:  "Append a local package dependency",
+		Run: func(c *cobra.Command, args []string) {
+		},
+	}
+
+	bundleRegenerateCommand = &cobra.Command{
+		Use:   "regenerate",
+		Short: "Regenerate the bundle",
+		Long:  "Regenerate the bundle",
+		Run: func(c *cobra.Command, args []string) {
+		},
+	}
+
+	bundleRemoveDependencyCommand = &cobra.Command{
+		Use:   "remove",
+		Short: "Remove a dependency",
+		Long:  "Remove a dependency",
+		Run: func(c *cobra.Command, args []string) {
+		},
+	}
+
+	bundleBumpVersionCommand = &cobra.Command{
+		Use:   "bump",
+		Short: "Bump the version of the bundle",
+		Long:  "Bump the version of the bundle",
+		Run: func(c *cobra.Command, args []string) {
+		},
+	}
+
+	bundleListDependenciesCommand = &cobra.Command{
+		Use:   "list",
+		Short: "List all dependencies",
+		Long:  "List all dependencies",
+		Run: func(c *cobra.Command, args []string) {
+		},
+	}
+)
+
+func init() {
+	bundleCommand.AddCommand(bundleCreateCommand)
+	bundleCommand.AddCommand(bundleAppendDependencyCommand)
+	bundleAppendDependencyCommand.AddCommand(bundleAppendGithubDependencyCommand)
+	bundleAppendDependencyCommand.AddCommand(bundleAppendMarketplaceDependencyCommand)
+	bundleAppendDependencyCommand.AddCommand(bundleAppendPackageDependencyCommand)
+	bundleCommand.AddCommand(bundleRemoveDependencyCommand)
+	bundleCommand.AddCommand(bundleRegenerateCommand)
+	bundleCommand.AddCommand(bundleBumpVersionCommand)
+	bundleCommand.AddCommand(bundleListDependenciesCommand)
+
+	bundleCommand.AddCommand(bundleAnalyzeCommand)
+
+	bundleAppendDependencyCommand.Flags().StringP("bundle_path", "i", "", "path to the bundle file")
+	bundleAppendDependencyCommand.MarkFlagRequired("bundle_path")
+
+	bundleAppendGithubDependencyCommand.Flags().StringP("repo_pattern", "r", "", "github repo pattern")
+	bundleAppendGithubDependencyCommand.MarkFlagRequired("repo_pattern")
+
+	bundleAppendMarketplaceDependencyCommand.Flags().StringP("marketplace_pattern", "m", "", "marketplace pattern")
+	bundleAppendMarketplaceDependencyCommand.MarkFlagRequired("marketplace_pattern")
+
+	bundleAppendPackageDependencyCommand.Flags().StringP("package_path", "p", "", "path to the package")
+	bundleAppendPackageDependencyCommand.MarkFlagRequired("package_path")
+}

+ 3 - 2
cmd/commandline/init/init.go

@@ -10,6 +10,7 @@ import (
 
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/constants"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/log"
 )
@@ -117,8 +118,8 @@ func (m model) createPlugin() {
 
 	manifest := &plugin_entities.PluginDeclaration{
 		PluginDeclarationWithoutAdvancedFields: plugin_entities.PluginDeclarationWithoutAdvancedFields{
-			Version: "0.0.1",
-			Type:    plugin_entities.PluginType,
+			Version: manifest_entities.Version("0.0.1"),
+			Type:    manifest_entities.PluginType,
 			Icon:    "icon.svg",
 			Author:  m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Author(),
 			Name:    m.subMenus[SUB_MENU_KEY_PROFILE].(profile).Name(),

+ 7 - 0
cmd/commandline/main.go

@@ -22,6 +22,12 @@ var (
 		Short: "Plugin",
 		Long:  "Plugin related commands",
 	}
+
+	bundleCommand = &cobra.Command{
+		Use:   "bundle",
+		Short: "Bundle",
+		Long:  "Bundle related commands",
+	}
 )
 
 func init() {
@@ -29,6 +35,7 @@ func init() {
 
 	rootCommand.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.dify.yaml)")
 	rootCommand.AddCommand(pluginCommand)
+	rootCommand.AddCommand(bundleCommand)
 }
 
 func initConfig() {

+ 2 - 1
internal/cluster/plugin_test.go

@@ -7,6 +7,7 @@ import (
 	"github.com/google/uuid"
 	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_manager/positive_manager"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 )
 
@@ -56,7 +57,7 @@ func getRandomPluginRuntime() fakePlugin {
 						EnUS: "label",
 					},
 					Version:   "0.0.1",
-					Type:      plugin_entities.PluginType,
+					Type:      manifest_entities.PluginType,
 					Author:    "Yeuoly",
 					CreatedAt: time.Now(),
 					Plugins: plugin_entities.PluginExtensions{

+ 34 - 0
internal/core/bundle_packager/bundle_packager.go

@@ -0,0 +1,34 @@
+package bundle_packager
+
+import (
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/bundle_entities"
+)
+
+type BundlePackager interface {
+	// Export exports the bundle to a zip byte array
+	Export() ([]byte, error)
+
+	// Icon returns the icon of the bundle
+	Icon() ([]byte, error)
+
+	// Manifest returns the manifest of the bundle
+	Manifest() (*bundle_entities.Bundle, error)
+
+	// Remove removes a dependency from the bundle
+	Remove(index int) error
+
+	// Append Github Dependency appends a github dependency to the bundle
+	AppendGithubDependency(repoPattern bundle_entities.GithubRepoPattern)
+
+	// Append Marketplace Dependency appends a marketplace dependency to the bundle
+	AppendMarketplaceDependency(marketplacePattern bundle_entities.MarketplacePattern)
+
+	// Append Package Dependency appends a local package dependency to the bundle
+	AppendPackageDependency(packagePath string)
+
+	// ListDependencies lists all the dependencies of the bundle
+	ListDependencies() ([]bundle_entities.Dependency, error)
+
+	// Regenerate regenerates the bundle, replace the basic information of the bundle like name, labels, description, icon, etc.
+	Regenerate(bundle bundle_entities.Bundle) error
+}

+ 54 - 0
internal/core/bundle_packager/impl.go

@@ -0,0 +1,54 @@
+package bundle_packager
+
+import (
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/bundle_entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
+)
+
+type BundlePackagerImpl struct {
+	bundle *bundle_entities.Bundle
+}
+
+func NewBundlePackager(zip []byte) BundlePackager {
+	return nil
+}
+
+func (p *BundlePackagerImpl) Export() ([]byte, error) {
+	return nil, nil
+}
+
+func (p *BundlePackagerImpl) Icon() ([]byte, error) {
+	return nil, nil
+}
+
+func (p *BundlePackagerImpl) Manifest() (*bundle_entities.Bundle, error) {
+	return p.bundle, nil
+}
+
+func (p *BundlePackagerImpl) Regenerate(bundle bundle_entities.Bundle) error {
+	return nil
+}
+
+func (p *BundlePackagerImpl) AppendGithubDependency(repoPattern bundle_entities.GithubRepoPattern) {
+
+}
+
+func (p *BundlePackagerImpl) AppendMarketplaceDependency(marketplacePattern bundle_entities.MarketplacePattern) {
+
+}
+
+func (p *BundlePackagerImpl) AppendPackageDependency(packagePath string) {
+
+}
+
+func (p *BundlePackagerImpl) ListDependencies() ([]bundle_entities.Dependency, error) {
+	return nil, nil
+}
+
+func (p *BundlePackagerImpl) Remove(index int) error {
+	return nil
+}
+
+func (p *BundlePackagerImpl) BumpVersion(target manifest_entities.Version) {
+
+}

+ 2 - 1
internal/core/plugin_manager/remote_manager/server_test.go

@@ -14,6 +14,7 @@ import (
 	"github.com/langgenius/dify-plugin-daemon/internal/oss/local"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/app"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/constants"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/cache"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/network"
@@ -151,7 +152,7 @@ func TestAcceptConnection(t *testing.T) {
 	pluginManifest := parser.MarshalJsonBytes(&plugin_entities.PluginDeclaration{
 		PluginDeclarationWithoutAdvancedFields: plugin_entities.PluginDeclarationWithoutAdvancedFields{
 			Version: "1.0.0",
-			Type:    plugin_entities.PluginType,
+			Type:    manifest_entities.PluginType,
 			Description: plugin_entities.I18nObject{
 				EnUS: "test",
 			},

+ 2 - 1
internal/core/plugin_manager/watcher_test.go

@@ -9,6 +9,7 @@ import (
 	"github.com/langgenius/dify-plugin-daemon/internal/oss/local"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/app"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/routine"
 )
@@ -70,7 +71,7 @@ func getRandomPluginRuntime() *fakePlugin {
 						EnUS: "label",
 					},
 					Version:   "0.0.1",
-					Type:      plugin_entities.PluginType,
+					Type:      manifest_entities.PluginType,
 					Author:    "Yeuoly",
 					CreatedAt: time.Now(),
 					Plugins: plugin_entities.PluginExtensions{

+ 2 - 1
internal/service/manage_plugin.go

@@ -5,6 +5,7 @@ import (
 
 	"github.com/langgenius/dify-plugin-daemon/internal/db"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/models"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/cache/helper"
@@ -23,7 +24,7 @@ func ListPlugins(tenant_id string, page int, page_size int) *entities.Response {
 		InstallationID         string                             `json:"installation_id"`
 		Declaration            *plugin_entities.PluginDeclaration `json:"declaration"`
 		RuntimeType            plugin_entities.PluginRuntimeType  `json:"runtime_type"`
-		Version                string                             `json:"version"`
+		Version                manifest_entities.Version          `json:"version"`
 		CreatedAt              time.Time                          `json:"created_at"`
 		UpdatedAt              time.Time                          `json:"updated_at"`
 		Source                 string                             `json:"source"`

+ 17 - 0
internal/types/entities/bundle_entities/bundle.go

@@ -0,0 +1,17 @@
+package bundle_entities
+
+import (
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
+)
+
+type Bundle struct {
+	Name         string                             `json:"name" yaml:"name" validate:"required"`
+	Labels       plugin_entities.I18nObject         `json:"labels" yaml:"labels" validate:"required"`
+	Description  plugin_entities.I18nObject         `json:"description" yaml:"description" validate:"required"`
+	Icon         string                             `json:"icon" yaml:"icon" validate:"required"`
+	Version      manifest_entities.Version          `json:"version" yaml:"version" validate:"required,version"`
+	Author       string                             `json:"author" yaml:"author" validate:"required"`
+	Type         manifest_entities.DifyManifestType `json:"type" yaml:"type" validate:"required,eq=bundle"`
+	Dependencies []Dependency                       `json:"dependencies" yaml:"dependencies" validate:"required"`
+}

+ 94 - 0
internal/types/entities/bundle_entities/dependency.go

@@ -0,0 +1,94 @@
+package bundle_entities
+
+import (
+	"fmt"
+	"regexp"
+
+	"github.com/go-playground/validator/v10"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/validators"
+)
+
+type DependencyType string
+
+const (
+	DEPENDENCY_TYPE_GITHUB      DependencyType = "github"
+	DEPENDENCY_TYPE_MARKETPLACE DependencyType = "marketplace"
+	DEPENDENCY_TYPE_PACKAGE     DependencyType = "package"
+)
+
+type Dependency struct {
+	Type  DependencyType `json:"type" yaml:"type" validate:"required,oneof=github marketplace package"`
+	Value any            `json:"value" yaml:"value" validate:"required"`
+}
+
+type GithubRepoPattern string
+type MarketplacePattern string
+
+var (
+	GITHUB_VERSION_PATTERN = fmt.Sprintf(
+		`([~^]?%s|%s(\.%s){2}|%s-%s)`,
+		manifest_entities.VERSION_PATTERN,
+		manifest_entities.VERSION_X_PATTERN,
+		manifest_entities.VERSION_X_PATTERN,
+		manifest_entities.VERSION_PATTERN,
+		manifest_entities.VERSION_PATTERN,
+	)
+	GITHUB_DEPENDENCY_PATTERN = fmt.Sprintf(`^[a-z0-9_-]{1,64}/[a-z0-9_-]{1,128}/%s/[^/]+$`, GITHUB_VERSION_PATTERN)
+
+	MARKETPLACE_VERSION_PATTERN = fmt.Sprintf(
+		`([~^]?%s|%s(\.%s){2}|%s-%s)`,
+		manifest_entities.VERSION_PATTERN,
+		manifest_entities.VERSION_X_PATTERN,
+		manifest_entities.VERSION_X_PATTERN,
+		manifest_entities.VERSION_PATTERN,
+		manifest_entities.VERSION_PATTERN,
+	)
+	MARKETPLACE_DEPENDENCY_PATTERN = fmt.Sprintf(`^[a-z0-9_-]{1,64}/[a-z0-9_-]{1,128}/%s$`, MARKETPLACE_VERSION_PATTERN)
+)
+
+var (
+	GITHUB_DEPENDENCY_PATTERN_REGEX_COMPILED = regexp.MustCompile(GITHUB_DEPENDENCY_PATTERN)
+	MARKETPLACE_PATTERN_REGEX_COMPILED       = regexp.MustCompile(MARKETPLACE_DEPENDENCY_PATTERN)
+)
+
+func validateGithubDependencyPattern(fl validator.FieldLevel) bool {
+	return GITHUB_DEPENDENCY_PATTERN_REGEX_COMPILED.MatchString(fl.Field().String())
+}
+
+func validateMarketplacePattern(fl validator.FieldLevel) bool {
+	return MARKETPLACE_PATTERN_REGEX_COMPILED.MatchString(fl.Field().String())
+}
+
+func init() {
+	validators.GlobalEntitiesValidator.RegisterValidation("github_dependency_pattern", validateGithubDependencyPattern)
+	validators.GlobalEntitiesValidator.RegisterValidation("marketplace_pattern", validateMarketplacePattern)
+}
+
+type GithubDependency struct {
+	// RepoPattern is the pattern of the repo, as for its content, at least one of the following patterns:
+	// 1. owner/repo/1.0.0/aaa.difypkg
+	// 2. owner/repo/1.0.0/*.difypkg
+	// 3. owner/repo/1.x.x/aaa.difypkg
+	// 4. owner/repo/^1.0.0/aaa.difypkg
+	// 5. owner/repo/~1.0.0/aaa.difypkg
+	// 6. owner/repo/1.0.0-2.0.0/aaa.difypkg
+	// 7. owner/repo/1.0.0-beta/aaa.difypkg
+	RepoPattern GithubRepoPattern `json:"repo_pattern" yaml:"repo_pattern" validate:"required,github_dependency_pattern"`
+}
+
+type MarketplaceDependency struct {
+	// MarketplacePattern is the pattern of the marketplace, as for its content, at least one of the following patterns:
+	// 1. org/plugin/1.0.0
+	// 2. org/plugin/1.x.x
+	// 3. org/plugin/^1.0.0
+	// 4. org/plugin/~1.0.0
+	// 5. org/plugin/1.0.0-2.0.0
+	// 6. org/plugin/1.0.0-beta
+	MarketplacePattern MarketplacePattern `json:"marketplace_pattern" yaml:"marketplace_pattern" validate:"required,marketplace_pattern"`
+}
+
+type PackageDependency struct {
+	// refers to the path of difypkg file in assets
+	Path string `json:"path" yaml:"path" validate:"required"`
+}

+ 147 - 0
internal/types/entities/bundle_entities/dependency_test.go

@@ -0,0 +1,147 @@
+package bundle_entities
+
+import (
+	"testing"
+)
+
+func TestGithubDependencyPatternRegex(t *testing.T) {
+	testCases := []struct {
+		name     string
+		input    string
+		expected bool
+	}{
+		// Valid patterns
+		{
+			name:     "basic version pattern",
+			input:    "owner/repo/1.0.0/manifest.json",
+			expected: true,
+		},
+		{
+			name:     "version with patch",
+			input:    "owner/repo/1.0.1/manifest.yaml",
+			expected: true,
+		},
+		{
+			name:     "version with pre-release",
+			input:    "owner/repo/1.0.0-beta/manifest.json",
+			expected: true,
+		},
+		{
+			name:     "version with x pattern",
+			input:    "owner/repo/1.x.x/manifest.json",
+			expected: true,
+		},
+		{
+			name:     "version with X pattern",
+			input:    "owner/repo/1.X.X/manifest.json",
+			expected: true,
+		},
+		{
+			name:     "version with mixed x pattern",
+			input:    "owner/repo/1.2.x/manifest.json",
+			expected: true,
+		},
+		{
+			name:     "version with tilde",
+			input:    "owner/repo/~1.0.0/manifest.json",
+			expected: true,
+		},
+		{
+			name:     "version with caret",
+			input:    "owner/repo/^1.0.0/manifest.json",
+			expected: true,
+		},
+		{
+			name:     "version range",
+			input:    "owner/repo/1.0.0-2.0.0/manifest.json",
+			expected: true,
+		},
+		{
+			name:     "complex owner and repo names",
+			input:    "complex-owner/complex-repo-name/1.0.0/manifest.json",
+			expected: true,
+		},
+		{
+			name:     "underscore in names",
+			input:    "owner_name/repo_name/1.0.0/manifest.json",
+			expected: true,
+		},
+
+		// Invalid patterns
+		{
+			name:     "four digit version",
+			input:    "owner/repo/1.0.0.1/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "empty owner",
+			input:    "/repo/1.0.0/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "empty repo",
+			input:    "owner//1.0.0/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "invalid version format",
+			input:    "owner/repo/1.0/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "missing manifest file",
+			input:    "owner/repo/1.0.0/",
+			expected: false,
+		},
+		{
+			name:     "uppercase in owner",
+			input:    "Owner/repo/1.0.0/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "uppercase in repo",
+			input:    "owner/Repo/1.0.0/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "invalid characters in owner",
+			input:    "owner@/repo/1.0.0/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "invalid characters in repo",
+			input:    "owner/repo#/1.0.0/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "too long owner name",
+			input:    "ownerwithaverylongnamethatshouldnotbeallowedinthiscaseownerwithaverylongnamethatshouldnotbeallowedinthiscase/repo/1.0.0/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "too long repo name",
+			input:    "owner/repowithavrepowithaverylongnamethatshouldnotbeallowedinthiscaseandshouldbeshorterthanspecifiedintherequirementsrepowithaverylongnamethatshouldnotbeallowedinthiscaseandshouldbeshorterthanspecifiedintherequirementserylongnamethatshouldnotbeallowedinthiscaseandshouldbeshorterthanspecifiedintherequirements/1.0.0/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "invalid version range format",
+			input:    "owner/repo/1.0.0-/manifest.json",
+			expected: false,
+		},
+		{
+			name:     "invalid pre-release format",
+			input:    "owner/repo/1.0.0-toolongprerelease/manifest.json",
+			expected: false,
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			result := GITHUB_DEPENDENCY_PATTERN_REGEX_COMPILED.MatchString(testCase.input)
+			if result != testCase.expected {
+				t.Errorf("Test case '%s' failed: input '%s' expected %v but got %v, pattern: %s",
+					testCase.name, testCase.input, testCase.expected, result, GITHUB_DEPENDENCY_PATTERN_REGEX_COMPILED.String())
+			}
+		})
+	}
+}

+ 8 - 0
internal/types/entities/manifest_entities/manifest.go

@@ -0,0 +1,8 @@
+package manifest_entities
+
+type DifyManifestType string
+
+const (
+	PluginType DifyManifestType = "plugin"
+	BundleType DifyManifestType = "bundle"
+)

+ 31 - 0
internal/types/entities/manifest_entities/version.go

@@ -0,0 +1,31 @@
+package manifest_entities
+
+import (
+	"regexp"
+
+	"github.com/go-playground/validator/v10"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/validators"
+)
+
+type Version string
+
+func (v Version) String() string {
+	return string(v)
+}
+
+const (
+	VERSION_PATTERN   = `\d{1,4}(\.\d{1,4}){2}(-\w{1,16})?`
+	VERSION_X_PATTERN = `(\d{1,4}|[xX])`
+)
+
+var PluginDeclarationVersionRegex = regexp.MustCompile("^" + VERSION_PATTERN + "$")
+
+func isVersion(fl validator.FieldLevel) bool {
+	// version format must be like x.x.x, at least 2 digits and most 5 digits, and it can be ends with a letter
+	value := fl.Field().String()
+	return PluginDeclarationVersionRegex.MatchString(value)
+}
+
+func init() {
+	validators.GlobalEntitiesValidator.RegisterValidation("version", isVersion)
+}

+ 16 - 29
internal/types/entities/plugin_entities/plugin_declaration.go

@@ -8,6 +8,7 @@ import (
 
 	"github.com/go-playground/validator/v10"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/constants"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/validators"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
 )
@@ -20,12 +21,6 @@ const (
 	PLUGIN_CATEGORY_EXTENSION PluginCategory = "extension"
 )
 
-type DifyManifestType string
-
-const (
-	PluginType DifyManifestType = "plugin"
-)
-
 type PluginTag string
 
 const (
@@ -191,18 +186,18 @@ type PluginExtensions struct {
 }
 
 type PluginDeclarationWithoutAdvancedFields struct {
-	Version     string                    `json:"version" yaml:"version,omitempty" validate:"required,version"`
-	Type        DifyManifestType          `json:"type" yaml:"type,omitempty" validate:"required,eq=plugin"`
-	Description I18nObject                `json:"description" yaml:"description" validate:"required"`
-	Label       I18nObject                `json:"label" yaml:"label" validate:"required"`
-	Author      string                    `json:"author" yaml:"author,omitempty" validate:"omitempty,max=64"`
-	Name        string                    `json:"name" yaml:"name,omitempty" validate:"required,max=128"`
-	Icon        string                    `json:"icon" yaml:"icon,omitempty" validate:"required,max=128"`
-	CreatedAt   time.Time                 `json:"created_at" yaml:"created_at,omitempty" validate:"required"`
-	Resource    PluginResourceRequirement `json:"resource" yaml:"resource,omitempty" validate:"required"`
-	Plugins     PluginExtensions          `json:"plugins" yaml:"plugins,omitempty" validate:"required"`
-	Meta        PluginMeta                `json:"meta" yaml:"meta,omitempty" validate:"required"`
-	Tags        []PluginTag               `json:"tags" yaml:"tags,omitempty" validate:"omitempty,dive,plugin_tag,max=128"`
+	Version     manifest_entities.Version          `json:"version" yaml:"version,omitempty" validate:"required,version"`
+	Type        manifest_entities.DifyManifestType `json:"type" yaml:"type,omitempty" validate:"required,eq=plugin"`
+	Description I18nObject                         `json:"description" yaml:"description" validate:"required"`
+	Label       I18nObject                         `json:"label" yaml:"label" validate:"required"`
+	Author      string                             `json:"author" yaml:"author,omitempty" validate:"omitempty,max=64"`
+	Name        string                             `json:"name" yaml:"name,omitempty" validate:"required,max=128"`
+	Icon        string                             `json:"icon" yaml:"icon,omitempty" validate:"required,max=128"`
+	CreatedAt   time.Time                          `json:"created_at" yaml:"created_at,omitempty" validate:"required"`
+	Resource    PluginResourceRequirement          `json:"resource" yaml:"resource,omitempty" validate:"required"`
+	Plugins     PluginExtensions                   `json:"plugins" yaml:"plugins,omitempty" validate:"required"`
+	Meta        PluginMeta                         `json:"meta" yaml:"meta,omitempty" validate:"required"`
+	Tags        []PluginTag                        `json:"tags" yaml:"tags,omitempty" validate:"omitempty,dive,plugin_tag,max=128"`
 }
 
 func (p *PluginDeclarationWithoutAdvancedFields) UnmarshalJSON(data []byte) error {
@@ -278,24 +273,17 @@ func (p *PluginDeclaration) MarshalJSON() ([]byte, error) {
 }
 
 var (
-	PluginNameRegex               = regexp.MustCompile(`^[a-z0-9_-]{1,128}$`)
-	AuthorRegex                   = regexp.MustCompile(`^[a-z0-9_-]{1,64}$`)
-	PluginDeclarationVersionRegex = regexp.MustCompile(`^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$`)
+	PluginNameRegex = regexp.MustCompile(`^[a-z0-9_-]{1,128}$`)
+	AuthorRegex     = regexp.MustCompile(`^[a-z0-9_-]{1,64}$`)
 )
 
-func isVersion(fl validator.FieldLevel) bool {
-	// version format must be like x.x.x, at least 2 digits and most 5 digits, and it can be ends with a letter
-	value := fl.Field().String()
-	return PluginDeclarationVersionRegex.MatchString(value)
-}
-
 func isPluginName(fl validator.FieldLevel) bool {
 	value := fl.Field().String()
 	return PluginNameRegex.MatchString(value)
 }
 
 func (p *PluginDeclaration) Identity() string {
-	return parser.MarshalPluginID(p.Author, p.Name, p.Version)
+	return parser.MarshalPluginID(p.Author, p.Name, p.Version.String())
 }
 
 func (p *PluginDeclaration) ManifestValidate() error {
@@ -339,7 +327,6 @@ func (p *PluginDeclaration) FillInDefaultValues() {
 
 func init() {
 	// init validator
-	validators.GlobalEntitiesValidator.RegisterValidation("version", isVersion)
 	validators.GlobalEntitiesValidator.RegisterValidation("plugin_name", isPluginName)
 }
 

+ 2 - 1
internal/types/entities/plugin_entities/plugin_declaration_test.go

@@ -5,6 +5,7 @@ import (
 	"time"
 
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/constants"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
 )
 
@@ -12,7 +13,7 @@ func preparePluginDeclaration() PluginDeclaration {
 	return PluginDeclaration{
 		PluginDeclarationWithoutAdvancedFields: PluginDeclarationWithoutAdvancedFields{
 			Version: "0.0.1",
-			Type:    PluginType,
+			Type:    manifest_entities.PluginType,
 			Description: I18nObject{
 				EnUS: "test",
 			},

+ 2 - 1
internal/types/models/curd/atomic.go

@@ -4,6 +4,7 @@ import (
 	"errors"
 
 	"github.com/langgenius/dify-plugin-daemon/internal/db"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/models"
 	"gorm.io/gorm"
@@ -313,7 +314,7 @@ func UpgradePlugin(
 				InstallType:            install_type,
 				Refers:                 0,
 				Declaration:            *new_declaration,
-				ManifestType:           plugin_entities.PluginType,
+				ManifestType:           manifest_entities.PluginType,
 			}
 
 			err := db.Create(&plugin, tx)

+ 6 - 5
internal/types/models/plugin.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/manifest_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 )
 
@@ -9,11 +10,11 @@ type Plugin struct {
 	// PluginUniqueIdentifier is a unique identifier for the plugin, it contains version and checksum
 	PluginUniqueIdentifier string `json:"plugin_unique_identifier" gorm:"index;size:127"`
 	// PluginID is the id of the plugin, only plugin name is considered
-	PluginID     string                            `json:"id" gorm:"index;size:127"`
-	Refers       int                               `json:"refers" gorm:"default:0"`
-	InstallType  plugin_entities.PluginRuntimeType `json:"install_type" gorm:"size:127;index"`
-	ManifestType plugin_entities.DifyManifestType  `json:"manifest_type" gorm:"size:127"`
-	Declaration  plugin_entities.PluginDeclaration `json:"declaration" gorm:"serializer:json;type:text;size:65535"`
+	PluginID     string                             `json:"id" gorm:"index;size:127"`
+	Refers       int                                `json:"refers" gorm:"default:0"`
+	InstallType  plugin_entities.PluginRuntimeType  `json:"install_type" gorm:"size:127;index"`
+	ManifestType manifest_entities.DifyManifestType `json:"manifest_type" gorm:"size:127"`
+	Declaration  plugin_entities.PluginDeclaration  `json:"declaration" gorm:"serializer:json;type:text;size:65535"`
 }
 
 type ServerlessRuntimeType string