mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-06-04 13:30:32 +08:00
cli/acceptance/contract/:
envelope_test.go — 16 envelope golden cases (9 commands × {success/error
variants}; 3 cases dropped with rationale: doctor.success
non-offline has unstable timing detail; auth_login.* needs
stdin/keyring scaffold deferred to v0.2; context_use.error
needs leaf-local --json deferred to follow-up)
errorcodes_test.go — single-direction AST scan of cli/cmd/ extracting first
arg of cmdutil.NewError / cmdutil.Wrapf calls;
ClassifyHTTPError dynamic-classify bridged via
cmdutil.ClassifyHTTPErrorOutputs() per spec §4.3.
testdata/envelopes/ — 16 JSON golden files
helpers_test.go (PR-6 scaffold) extended:
runCmd now wires cobra Out/Err sinks (version uses c.OutOrStdout) AND
replicates cmd.Execute()'s error-envelope path so error-case goldens are
populated. Without this, every error scenario's golden was 0 bytes.
cli/cmd/root.go: mapCobraError → MapCobraError, wantsJSONOutput → WantsJSONOutput
(exported so the contract test helper can replicate Execute()'s
envelope-printing path without calling Execute() itself).
root_test.go updated to use new exported names.
.github/dependabot.yml (新增):gomod /cli + github-actions weekly,gh-style
ignore semver-major to avoid noise. Open-source
dependency safety,independent of release cadence.
v0.1 不发布到任何分发平台 (release infra 推迟到发布窗口 milestone)。
90 lines
2.5 KiB
Go
90 lines
2.5 KiB
Go
package kb
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"text/tabwriter"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/Tencent/WeKnora/cli/internal/agent"
|
|
"github.com/Tencent/WeKnora/cli/internal/cmdutil"
|
|
"github.com/Tencent/WeKnora/cli/internal/format"
|
|
"github.com/Tencent/WeKnora/cli/internal/iostreams"
|
|
"github.com/Tencent/WeKnora/cli/internal/text"
|
|
sdk "github.com/Tencent/WeKnora/client"
|
|
)
|
|
|
|
// ListOptions captures `weknora kb list` flags.
|
|
type ListOptions struct {
|
|
JSONOut bool
|
|
}
|
|
|
|
// ListService is the narrow SDK surface this command depends on.
|
|
type ListService interface {
|
|
ListKnowledgeBases(ctx context.Context) ([]sdk.KnowledgeBase, error)
|
|
}
|
|
|
|
// listResult is the typed payload emitted under data.items.
|
|
type listResult struct {
|
|
Items []sdk.KnowledgeBase `json:"items"`
|
|
}
|
|
|
|
// NewCmdList builds `weknora kb list`.
|
|
func NewCmdList(f *cmdutil.Factory) *cobra.Command {
|
|
opts := &ListOptions{}
|
|
cmd := &cobra.Command{
|
|
Use: "list",
|
|
Short: "List knowledge bases visible to the active context",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(c *cobra.Command, _ []string) error {
|
|
cli, err := f.Client()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return runList(c.Context(), opts, cli)
|
|
},
|
|
}
|
|
cmd.Flags().BoolVar(&opts.JSONOut, "json", false, "Output JSON envelope")
|
|
agent.SetAgentHelp(cmd, "Lists all knowledge bases. Returns data.items: [{id, name, ...}]; empty array when none.")
|
|
return cmd
|
|
}
|
|
|
|
func runList(ctx context.Context, opts *ListOptions, svc ListService) error {
|
|
items, err := svc.ListKnowledgeBases(ctx)
|
|
if err != nil {
|
|
return cmdutil.Wrapf(cmdutil.ClassifyHTTPError(err), err, "list knowledge bases")
|
|
}
|
|
if items == nil {
|
|
items = []sdk.KnowledgeBase{} // ensure JSON [] not null
|
|
}
|
|
// Spec §1.2: default sort by updated_at desc. Server return order is not
|
|
// guaranteed, so client-side sort makes output deterministic regardless
|
|
// of backend storage choices.
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].UpdatedAt.After(items[j].UpdatedAt)
|
|
})
|
|
|
|
if opts.JSONOut {
|
|
return format.WriteEnvelope(iostreams.IO.Out, format.Success(listResult{Items: items}, nil))
|
|
}
|
|
|
|
if len(items) == 0 {
|
|
fmt.Fprintln(iostreams.IO.Out, "(no knowledge bases)")
|
|
return nil
|
|
}
|
|
|
|
tw := tabwriter.NewWriter(iostreams.IO.Out, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(tw, "ID\tNAME\tDOCS\tUPDATED")
|
|
now := time.Now()
|
|
for _, kb := range items {
|
|
name := text.Truncate(40, kb.Name)
|
|
docs := text.Pluralize(int(kb.KnowledgeCount), "doc")
|
|
updated := text.FuzzyAgo(now, kb.UpdatedAt)
|
|
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", kb.ID, name, docs, updated)
|
|
}
|
|
return tw.Flush()
|
|
}
|