Files
dingtalk-workspace-cli/internal/cli/schema_hints.go
T
玉澜 6d97bf7204 Merge upstream/main into feat/schema-on-main
Complete the registry-first Schema delivery invariants, bind the event command surface, and preserve the event subprocess contract from main.
2026-07-14 01:25:05 +08:00

128 lines
3.8 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"strings"
)
// ToolSchemaHint is reviewed field metadata for an existing registry-owned
// tool. It intentionally has no command path, alias, visibility, or canonical
// identity fields: all identity/navigation comes from CommandRegistry.
type ToolSchemaHint struct {
Title string
Description string
Parameters map[string]ParameterSchemaHint
}
// ParameterSchemaHint overrides the projected flat schema for one parameter.
// It can be keyed either by the original MCP parameter name (preferred) or by
// the final CLI flag name when a hint is purely CLI-facing.
type ParameterSchemaHint struct {
FlagName string
Type string
Description string
Required *bool
RequiredWhen string
}
type SchemaVisibility string
const (
SchemaVisibilityPublic SchemaVisibility = "public"
SchemaVisibilityCompat SchemaVisibility = "compat"
SchemaVisibilityInternal SchemaVisibility = "internal"
)
type SchemaHintRegistry struct {
tools map[string]ToolSchemaHint
}
func boolPtr(v bool) *bool { return &v }
var defaultSchemaHintRegistry = newSchemaHintRegistry()
func newSchemaHintRegistry() *SchemaHintRegistry {
return &SchemaHintRegistry{
tools: map[string]ToolSchemaHint{},
}
}
// RegisterSchemaHints registers curated hints for one product. Keys in tools may
// be either RPC names ("send_ding_message") or canonical paths
// ("ding.send_ding_message"). Duplicate canonical paths panic during init so
// conflicting product hint files are caught early.
func RegisterSchemaHints(productID string, tools map[string]ToolSchemaHint) {
defaultSchemaHintRegistry.RegisterProduct(productID, tools)
}
func (r *SchemaHintRegistry) RegisterProduct(productID string, tools map[string]ToolSchemaHint) {
productID = strings.TrimSpace(productID)
if productID == "" {
panic("schema hints: product id is required")
}
for name, hint := range tools {
path := canonicalHintPath(productID, name)
if path == "" {
panic(fmt.Sprintf("schema hints: empty tool name for product %q", productID))
}
if _, exists := r.tools[path]; exists {
panic(fmt.Sprintf("schema hints: duplicate hint for %s", path))
}
r.tools[path] = hint
}
}
func (r *SchemaHintRegistry) Lookup(canonicalPath string) (ToolSchemaHint, bool) {
canonicalPath = strings.TrimSpace(canonicalPath)
if canonicalPath == "" {
return ToolSchemaHint{}, false
}
hint, ok := r.tools[canonicalPath]
return hint, ok
}
func canonicalHintPath(productID, name string) string {
productID = strings.TrimSpace(productID)
name = strings.TrimSpace(name)
if name == "" {
return ""
}
if strings.Contains(name, ".") {
return name
}
return productID + "." + name
}
func schemaHintForCanonicalPath(canonicalPath string) ToolSchemaHint {
if hint, ok := defaultSchemaHintRegistry.Lookup(canonicalPath); ok {
return hint
}
return ToolSchemaHint{}
}
func lookupParameterSchemaHint(hints map[string]ParameterSchemaHint, paramName, flagName string) (ParameterSchemaHint, string, bool) {
if len(hints) == 0 {
return ParameterSchemaHint{}, "", false
}
if hint, ok := hints[paramName]; ok {
return hint, paramName, true
}
if hint, ok := hints[flagName]; ok {
return hint, flagName, true
}
return ParameterSchemaHint{}, "", false
}