Merge PR #354: feat(auth): 新增 auth token 子命令与 auth status --show-token(对标 g

# Conflicts:
#	internal/i18n/locales/en-US.json
#	internal/i18n/locales/zh-CN.json
This commit is contained in:
wbtiger 2026-07-14 22:49:56 +08:00
commit a5f7ed46af
6 changed files with 229 additions and 206 deletions

View File

@ -818,6 +818,14 @@ gitlink-cli repo +list # Ready to use
gitlink-cli auth status # Shows "✓ Logged in via GITLINK_TOKEN environment variable"
```
To reuse the active token in scripts (e.g. raw `curl` calls against endpoints the CLI does not wrap yet):
```bash
curl -H "Authorization: Bearer $(gitlink-cli auth token)" https://www.gitlink.org.cn/api/v1/...
gitlink-cli auth status --show-token # Inspect the raw token (hidden by default)
echo $MY_TOKEN | gitlink-cli auth login --with-token # Non-interactive login (CI, scripts)
```
Priority: `GITLINK_TOKEN` env var > keyring/file stored token. When the env var is not set, the original interactive login flow works as before.
### Q: What if npm installs successfully but `gitlink-cli` reports a missing binary?

View File

@ -1011,6 +1011,14 @@ gitlink-cli repo +list # 直接可用
gitlink-cli auth status # 显示 "✓ Logged in via GITLINK_TOKEN environment variable"
```
脚本中复用当前生效的 token例如直接 `curl` CLI 尚未封装的端点):
```bash
curl -H "Authorization: Bearer $(gitlink-cli auth token)" https://www.gitlink.org.cn/api/v1/...
gitlink-cli auth status --show-token # 查看原始 token默认隐藏
echo $MY_TOKEN | gitlink-cli auth login --with-token # 非交互登录CI/脚本)
```
Token 优先级:`GITLINK_TOKEN` 环境变量 > keyring/文件存储的 token。不设置环境变量时完全兼容原有交互式登录。
### Q: npm 安装成功但 `gitlink-cli` 提示缺少二进制怎么办?

View File

@ -34,16 +34,21 @@ func NewAuthCmd(translators ...*i18n.Translator) *cobra.Command {
cmd.AddCommand(newLoginCmd(tr))
cmd.AddCommand(newLogoutCmd(tr))
cmd.AddCommand(newStatusCmd(tr))
cmd.AddCommand(newTokenCmd(tr))
return cmd
}
func newLoginCmd(tr *i18n.Translator) *cobra.Command {
var tokenMode bool
var withToken bool
cmd := &cobra.Command{
Use: "login",
Short: tr.T("cmd.auth.login.short"),
RunE: func(cmd *cobra.Command, args []string) error {
if withToken {
return loginWithTokenStdin(cmd.InOrStdin(), cmd.OutOrStdout(), tr)
}
if tokenMode {
return loginWithToken(cmd.InOrStdin(), cmd.OutOrStdout(), tr)
}
@ -51,6 +56,7 @@ func newLoginCmd(tr *i18n.Translator) *cobra.Command {
},
}
cmd.Flags().BoolVar(&tokenMode, "token", false, tr.T("flag.auth.token"))
cmd.Flags().BoolVar(&withToken, "with-token", false, tr.T("flag.auth.with_token"))
return cmd
}
@ -117,6 +123,26 @@ func loginWithToken(in io.Reader, out io.Writer, tr *i18n.Translator) error {
return err
}
// loginWithTokenStdin reads a token from stdin without prompting, mirroring
// `gh auth login --with-token` for non-interactive use (CI, scripts):
//
// echo $TOKEN | gitlink-cli auth login --with-token
func loginWithTokenStdin(in io.Reader, out io.Writer, tr *i18n.Translator) error {
data, err := io.ReadAll(io.LimitReader(in, 4096))
if err != nil {
return err
}
token := strings.TrimSpace(string(data))
if token == "" {
return errors.New(tr.T("error.auth.token_empty"))
}
if err := storeToken(token); err != nil {
return errors.New(tr.Tf("error.auth.store_token_failed", i18n.Args{"message": err.Error()}))
}
_, err = fmt.Fprintln(out, tr.T("success.auth.token_saved"))
return err
}
func newLogoutCmd(tr *i18n.Translator) *cobra.Command {
return &cobra.Command{
Use: "logout",
@ -132,7 +158,9 @@ func newLogoutCmd(tr *i18n.Translator) *cobra.Command {
}
func newStatusCmd(tr *i18n.Translator) *cobra.Command {
return &cobra.Command{
var showToken bool
cmd := &cobra.Command{
Use: "status",
Short: tr.T("cmd.auth.status.short"),
RunE: func(cmd *cobra.Command, args []string) error {
@ -142,9 +170,19 @@ func newStatusCmd(tr *i18n.Translator) *cobra.Command {
if _, err := fmt.Fprintln(out, tr.Tf("success.auth.logged_in_via_env", i18n.Args{"env": envTokenVar})); err != nil {
return err
}
if showToken {
if _, err := fmt.Fprintln(out, tr.Tf("output.auth.token_value", i18n.Args{"token": envToken})); err != nil {
return err
}
}
}
token, err := loadToken()
if err == nil && token != "" && showToken {
if _, err := fmt.Fprintln(out, tr.Tf("output.auth.token_value", i18n.Args{"token": token})); err != nil {
return err
}
}
if err != nil || token == "" {
if os.Getenv(envTokenVar) == "" {
if _, err := fmt.Fprintln(out, tr.T("warning.auth.not_logged_in")); err != nil {
@ -180,4 +218,29 @@ func newStatusCmd(tr *i18n.Translator) *cobra.Command {
return err
},
}
cmd.Flags().BoolVar(&showToken, "show-token", false, tr.T("flag.auth.show_token"))
return cmd
}
// newTokenCmd prints the active token to stdout for scripting, mirroring
// `gh auth token`. Resolution order matches API calls: GITLINK_TOKEN env
// var first, then the stored keyring/file token.
func newTokenCmd(tr *i18n.Translator) *cobra.Command {
return &cobra.Command{
Use: "token",
Short: tr.T("cmd.auth.token.short"),
Long: tr.T("cmd.auth.token.long"),
RunE: func(cmd *cobra.Command, args []string) error {
token := os.Getenv(envTokenVar)
if token == "" {
stored, err := loadToken()
if err != nil || stored == "" {
return errors.New(tr.T("error.auth.no_token"))
}
token = stored
}
_, err := fmt.Fprintln(cmd.OutOrStdout(), token)
return err
},
}
}

View File

@ -1,9 +1,11 @@
package auth
import (
"bytes"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"github.com/spf13/cobra"
@ -35,7 +37,7 @@ func TestNewAuthCmd(t *testing.T) {
}
expectedSubs := map[string]bool{
"login": false, "logout": false, "status": false,
"login": false, "logout": false, "status": false, "token": false,
}
for _, sub := range cmd.Commands() {
if _, ok := expectedSubs[sub.Use]; !ok {
@ -130,6 +132,98 @@ func TestStatusCmdStoredTokenButLoadFails(t *testing.T) {
}
}
func TestTokenCmdEnvToken(t *testing.T) {
keyring.MockInitWithError(errors.New("keychain unavailable"))
tempConfigDir(t)
t.Setenv("GITLINK_TOKEN", "env-token-123")
_ = internalAuth.DeleteToken()
cmd := findSub(NewAuthCmd(), "token")
if cmd == nil {
t.Fatal("token subcommand not found")
}
var buf bytes.Buffer
cmd.SetOut(&buf)
if err := cmd.RunE(cmd, nil); err != nil {
t.Fatalf("token error: %v", err)
}
if got := strings.TrimSpace(buf.String()); got != "env-token-123" {
t.Fatalf("token output = %q, want env-token-123", got)
}
}
func TestTokenCmdStoredToken(t *testing.T) {
keyring.MockInitWithError(errors.New("keychain unavailable"))
dir := tempConfigDir(t)
t.Setenv("GITLINK_TOKEN", "")
os.MkdirAll(dir, 0700)
os.WriteFile(filepath.Join(dir, "credentials"), []byte("stored-token-456"), 0600)
cmd := findSub(NewAuthCmd(), "token")
var buf bytes.Buffer
cmd.SetOut(&buf)
if err := cmd.RunE(cmd, nil); err != nil {
t.Fatalf("token error: %v", err)
}
if got := strings.TrimSpace(buf.String()); got != "stored-token-456" {
t.Fatalf("token output = %q, want stored-token-456", got)
}
}
func TestTokenCmdNoToken(t *testing.T) {
keyring.MockInitWithError(errors.New("keychain unavailable"))
tempConfigDir(t)
t.Setenv("GITLINK_TOKEN", "")
_ = internalAuth.DeleteToken()
cmd := findSub(NewAuthCmd(), "token")
var buf bytes.Buffer
cmd.SetOut(&buf)
if err := cmd.RunE(cmd, nil); err == nil {
t.Fatal("expected error when no token is available")
}
}
func TestStatusCmdShowToken(t *testing.T) {
keyring.MockInitWithError(errors.New("keychain unavailable"))
dir := tempConfigDir(t)
t.Setenv("GITLINK_TOKEN", "")
os.MkdirAll(dir, 0700)
os.WriteFile(filepath.Join(dir, "credentials"), []byte("stored-token-789"), 0600)
cmd := findSub(NewAuthCmd(), "status")
var buf bytes.Buffer
cmd.SetOut(&buf)
cmd.Flags().Set("show-token", "true")
if err := cmd.RunE(cmd, nil); err != nil {
t.Fatalf("status error: %v", err)
}
if !strings.Contains(buf.String(), "stored-token-789") {
t.Fatalf("status --show-token output missing token: %q", buf.String())
}
}
func TestStatusCmdHidesTokenByDefault(t *testing.T) {
keyring.MockInitWithError(errors.New("keychain unavailable"))
dir := tempConfigDir(t)
t.Setenv("GITLINK_TOKEN", "")
os.MkdirAll(dir, 0700)
os.WriteFile(filepath.Join(dir, "credentials"), []byte("stored-token-789"), 0600)
cmd := findSub(NewAuthCmd(), "status")
var buf bytes.Buffer
cmd.SetOut(&buf)
if err := cmd.RunE(cmd, nil); err != nil {
t.Fatalf("status error: %v", err)
}
if strings.Contains(buf.String(), "stored-token-789") {
t.Fatalf("status leaked token without --show-token: %q", buf.String())
}
}
func TestLogoutCmdNoStoredToken(t *testing.T) {
keyring.MockInitWithError(errors.New("keychain unavailable"))
tempConfigDir(t)
@ -241,3 +335,43 @@ func findSub(cmd *cobra.Command, name string) *cobra.Command {
}
return nil
}
func TestLoginWithTokenStdin(t *testing.T) {
keyring.MockInit()
tempConfigDir(t)
t.Setenv("GITLINK_TOKEN", "")
_ = internalAuth.DeleteToken()
cmd := findSub(NewAuthCmd(), "login")
if cmd == nil {
t.Fatal("login subcommand not found")
}
if err := cmd.Flags().Set("with-token", "true"); err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
cmd.SetIn(strings.NewReader("stdin-token-456\n"))
cmd.SetOut(&buf)
if err := cmd.RunE(cmd, nil); err != nil {
t.Fatalf("login --with-token error: %v", err)
}
stored, err := internalAuth.LoadToken()
if err != nil || stored != "stdin-token-456" {
t.Fatalf("stored token = %q (err %v), want stdin-token-456", stored, err)
}
}
func TestLoginWithTokenStdinEmpty(t *testing.T) {
keyring.MockInit()
tempConfigDir(t)
cmd := findSub(NewAuthCmd(), "login")
if err := cmd.Flags().Set("with-token", "true"); err != nil {
t.Fatal(err)
}
cmd.SetIn(strings.NewReader("\n"))
cmd.SetOut(&bytes.Buffer{})
if err := cmd.RunE(cmd, nil); err == nil {
t.Fatal("expected error for empty stdin token")
}
}

View File

@ -1,33 +1,23 @@
{
"cmd.alias.delete.short": "Delete an alias",
"cmd.alias.import.short": "Import aliases from a YAML file",
"cmd.alias.list.short": "List all aliases",
"cmd.alias.set.short": "Create or update an alias",
"cmd.alias.short": "Manage command aliases",
"cmd.api.long": "Send arbitrary HTTP requests to the GitLink API. Authentication is injected automatically.",
"cmd.api.short": "Make raw API requests to GitLink",
"cmd.auth.login.short": "Login to GitLink",
"cmd.auth.logout.short": "Logout from GitLink",
"cmd.auth.short": "Authentication commands",
"cmd.auth.status.short": "Show authentication status",
"cmd.branch.all.short": "List all branch names (no paging)",
"cmd.auth.token.long": "Print the token that gitlink-cli would use for API calls, for use in scripts (e.g. `curl -H \"Authorization: Bearer $(gitlink-cli auth token)\"`). Resolution order: GITLINK_TOKEN environment variable, then the stored token.",
"cmd.auth.token.short": "Print the active authentication token",
"cmd.branch.create.short": "Create a branch",
"cmd.branch.delete.short": "Delete a branch",
"cmd.branch.list.short": "List branches",
"cmd.branch.protect.short": "Set branch protection",
"cmd.branch.set_default.short": "Set the repository default branch",
"cmd.branch.short": "Branch operations",
"cmd.branch.unprotect.short": "Remove branch protection",
"cmd.ci.activate.short": "Activate repository CI",
"cmd.ci.authorize.short": "Show repository CI authorization status",
"cmd.ci.builds.short": "List CI builds",
"cmd.ci.deactivate.short": "Deactivate repository CI",
"cmd.ci.logs.short": "View build logs",
"cmd.ci.restart.short": "Restart a build",
"cmd.ci.short": "CI/CD operations",
"cmd.ci.stop.short": "Stop a build",
"cmd.completion.long": "Generate shell completion scripts for gitlink-cli.\n\nLoad the generated script in your shell profile to enable command and flag completion.\n\nExamples:\n gitlink-cli completion bash > ~/.local/share/bash-completion/completions/gitlink-cli\n gitlink-cli completion zsh > \"${fpath[1]}/_gitlink-cli\"\n gitlink-cli completion fish > ~/.config/fish/completions/gitlink-cli.fish\n gitlink-cli completion powershell > gitlink-cli.ps1",
"cmd.completion.short": "Generate shell completion scripts",
"cmd.config.get.short": "Get a configuration value",
"cmd.config.init.short": "Initialize configuration file",
"cmd.config.list.short": "List all configuration values",
@ -46,7 +36,6 @@
"cmd.dataset.view.short": "View a repository's dataset",
"cmd.doctor.long": "Run local diagnostics for gitlink-cli configuration, authentication, repository context and API connectivity.",
"cmd.doctor.short": "Diagnose gitlink-cli environment problems",
"cmd.ignore.short": "Ignore file template operations",
"cmd.issue.batch_close.long": "Close filtered issues in bulk.\n\nThis command defaults to dry-run mode and only prints matching issues.\nPass --yes to execute remote close operations. Use restrictive filters and a small limit.\n\nExamples:\n gitlink-cli issue +batch-close --owner Gitlink --repo gitlink-cli --older-than-days 60 --limit 20\n gitlink-cli issue +batch-close --owner Gitlink --repo gitlink-cli --older-than-days 60 --limit 20 --yes",
"cmd.issue.batch_close.short": "Close filtered issues in bulk. Defaults to dry-run; pass --yes to execute.",
"cmd.issue.batch_label.long": "Add a label to filtered issues in bulk.\n\nThis command defaults to dry-run mode and only prints matching issues.\nPass --yes to execute remote label operations. The current implementation does not fake label writes when the API endpoint is unavailable.\n\nExamples:\n gitlink-cli issue +batch-label --owner Gitlink --repo gitlink-cli --add-label stale --older-than-days 30 --limit 50\n gitlink-cli issue +batch-label --owner Gitlink --repo gitlink-cli --add-label stale --older-than-days 30 --limit 50 --yes",
@ -55,52 +44,26 @@
"cmd.issue.batch_list.short": "List issue batch maintenance candidates without changing remote data",
"cmd.issue.close.short": "Close an issue",
"cmd.issue.comment.short": "Add a comment to an issue",
"cmd.issue.comment_delete.short": "Delete an issue comment",
"cmd.issue.comment_edit.short": "Edit an issue comment",
"cmd.issue.comments.short": "List comments (journals) of an issue",
"cmd.issue.create.short": "Create a new issue",
"cmd.issue.list.short": "List issues",
"cmd.invite.accept.long": "Accept a project invite via invite link",
"cmd.invite.accept.short": "Accept an invite",
"cmd.invite.generate.long": "Generate or retrieve the project invite link",
"cmd.invite.generate.short": "Generate an invite link",
"cmd.invite.join.long": "Join a project by invite code",
"cmd.invite.join.short": "Join a project",
"cmd.invite.quit.long": "Quit from the current project",
"cmd.invite.quit.short": "Quit a project",
"cmd.invite.short": "Project invite operations",
"cmd.invite.show.long": "Show invite link details by invite sign",
"cmd.invite.show.short": "Show invite link info",
"cmd.issue.short": "Issue operations",
"cmd.issue.update.short": "Update an issue",
"cmd.issue.view.short": "View issue details",
"cmd.message_settings.catalog.short": "List available message setting groups and keys",
"cmd.message_settings.preset.short": "Apply a preset to selected message settings",
"cmd.message_settings.short": "Message settings operations",
"cmd.message_settings.update.short": "Update selected message settings while preserving other values",
"cmd.message_settings.view.short": "Show effective message settings for a user",
"cmd.org.create.short": "Create an organization",
"cmd.org.info.short": "Show organization details",
"cmd.org.list.short": "List organizations",
"cmd.org.members.short": "List organization members",
"cmd.org.short": "Organization operations",
"cmd.org.teams.short": "List organization teams",
"cmd.pr.close.short": "Close a pull request",
"cmd.pr.comment.short": "Add a comment to a pull request",
"cmd.pr.comment_delete.short": "Delete a pull request comment",
"cmd.pr.comment_edit.short": "Edit a pull request comment",
"cmd.pr.comments.short": "List comments (journals) of a pull request",
"cmd.pr.create.short": "Create a pull request",
"cmd.pr.diff.short": "Show diff for a pull request",
"cmd.pr.edit.short": "Edit a pull request while preserving unspecified fields",
"cmd.pr.files.short": "List changed files in a pull request",
"cmd.pr.list.short": "List pull requests",
"cmd.pr.merge.short": "Merge a pull request",
"cmd.pr.review.short": "Create a pull request review",
"cmd.pr.reviews.short": "List pull request reviews",
"cmd.pr.short": "Pull request operations",
"cmd.pr.status.long": "Show open pull requests in this repository that are relevant to the current authenticated user, grouped into ones you created and ones requesting your review.",
"cmd.pr.status.short": "Show pull requests relevant to you",
"cmd.pr.version_diff.short": "Show diff for a pull request patchset version",
"cmd.pr.versions.short": "List pull request patchset versions",
"cmd.pr.view.short": "View pull request details",
@ -117,28 +80,18 @@
"cmd.profile.short": "User profile and statistics operations",
"cmd.release.create.short": "Create a release",
"cmd.release.delete.short": "Delete a release",
"cmd.release.assets.short": "List release attachments and source archives",
"cmd.release.download.short": "Download a release attachment or source archive",
"cmd.release.list.short": "List releases",
"cmd.release.short": "Release operations",
"cmd.release.view.short": "View release details",
"cmd.repo.clone.long": "Resolve the repository clone URL through the platform API and run `git clone`, mirroring `gh repo clone`. Git progress goes to stderr; the JSON/table result stays on stdout.",
"cmd.repo.clone.short": "Clone a repository with git",
"cmd.repo.create.short": "Create a new repository",
"cmd.repo.delete.short": "Delete a repository",
"cmd.repo.edit.short": "Update repository settings",
"cmd.repo.fork.short": "Fork a repository",
"cmd.repo.info.short": "Show repository details",
"cmd.repo.list.short": "List repositories for a user or organization",
"cmd.repo.short": "Repository operations",
"cmd.repo.transfer.short": "Transfer a repository to another owner",
"cmd.repo.transfer_cancel.short": "Cancel a pending repository transfer",
"cmd.repo.transfer_orgs.short": "List organizations that can receive this repository",
"cmd.repo.tree.short": "List repository files and directories",
"cmd.root.long": "Manage repositories, issues, pull requests, releases, CI and workflows on GitLink.",
"cmd.root.short": "GitLink CLI - command-line tool for GitLink",
"cmd.search.recommend.long": "List the platform's recommended/featured projects (id, name, visits, author, category).",
"cmd.search.recommend.short": "List recommended projects",
"cmd.search.repos.short": "Search repositories",
"cmd.search.short": "Search operations",
"cmd.search.users.short": "Search users",
@ -154,22 +107,16 @@
"cmd.webhook.test.short": "Trigger a test delivery for a webhook",
"cmd.webhook.update.short": "Update a repository webhook while preserving unspecified fields when available",
"cmd.webhook.view.short": "View webhook details",
"error.alias.empty_expansion": "alias expansion cannot be empty",
"error.alias.import_parse": "failed to parse aliases: {message}",
"error.alias.import_read": "failed to read aliases: {message}",
"error.alias.not_found": "no such alias: {name}",
"error.alias.save_failed": "failed to save alias: {message}",
"error.auth.delete_token_failed": "failed to delete token: {message}",
"error.auth.login_failed": "login failed: {message}",
"error.auth.no_token": "no token found: run `gitlink-cli auth login` or set GITLINK_TOKEN",
"error.auth.store_token_failed": "failed to store token: {message}",
"error.auth.token_empty": "token cannot be empty",
"error.config.save_failed": "failed to save config: {message}",
"error.dataset.delete_confirm": "dataset attachment deletion is destructive; run --dry-run first, then pass --yes to confirm",
"error.missing_required_flag": "required flag --{name} is missing",
"error.profile.user_required": "could not determine target user; pass --user or run gitlink-cli auth login",
"error.repo.clone_url_missing": "repository response did not include a clone_url; check the owner/repo",
"error.unsupported_language": "unsupported language: {lang}",
"flag.alias.file": "Read aliases from a YAML file (default: stdin)",
"flag.api.batch_continue_on_error": "Continue running remaining batch requests after a failure",
"flag.api.batch_dry_run": "Preview batch requests without sending remote requests",
"flag.api.batch_file": "Read an API batch plan from a JSON file",
@ -178,20 +125,16 @@
"flag.api.body_file": "Read request body JSON from a file",
"flag.api.body_stdin": "Read request body JSON from stdin",
"flag.api.header": "Additional headers (key:value)",
"flag.api.paginate": "Fetch every page of results and output as one combined array",
"flag.api.query": "Query parameters (key=val&key2=val2)",
"flag.auth.show_token": "Display the raw token in the status output",
"flag.auth.token": "Login by pasting an existing token",
"flag.auth.with_token": "Read the token from standard input (non-interactive)",
"flag.branch.from": "Source branch or commit",
"flag.branch.name": "Branch name",
"flag.ci.activate_dry_run": "Preview the CI activation request without changing remote state",
"flag.ci.activate_yes": "Confirm repository CI activation",
"flag.ci.build": "Build number",
"flag.ci.deactivate_dry_run": "Preview the CI deactivation request without changing remote state",
"flag.ci.deactivate_yes": "Confirm repository CI deactivation",
"flag.ci.stage": "Stage number",
"flag.ci.step": "Step number",
"flag.comment.body": "Comment body",
"flag.completion.no_descriptions": "Disable completion descriptions",
"flag.dataset.description": "Dataset description",
"flag.dataset.dry_run": "Preview the request without writing the dataset",
"flag.dataset.dry_run_delete": "Preview the request without deleting the attachment",
@ -208,10 +151,6 @@
"flag.doctor.skip_network": "Skip authenticated API connectivity checks",
"flag.dry_run": "Preview the request without creating it",
"flag.format": "Output format: json, table, yaml (default: table)",
"flag.invite.code": "Project invite code",
"flag.invite.is-apply": "Whether it is apply mode (true/false)",
"flag.invite.role": "Invite role (e.g. developer, manager)",
"flag.invite.sign": "Invite sign/invite code",
"flag.issue.add_label": "Label to add to each matching issue",
"flag.issue.assignee": "Assignee login",
"flag.issue.assignee_id": "Assignee user ID",
@ -224,9 +163,6 @@
"flag.issue.batch_list.limit": "Maximum issues to return, capped at 100",
"flag.issue.batch_process.limit": "Maximum issues to process, capped at 100",
"flag.issue.body": "Issue description",
"flag.issue.comment_id": "Comment (journal) ID",
"flag.issue.comments_category": "Journal category filter: comment or operate",
"flag.issue.comments_keyword": "Filter comments by keyword",
"flag.issue.label": "Label ID",
"flag.issue.label_filter": "Filter by existing label",
"flag.issue.milestone": "Milestone ID",
@ -240,16 +176,8 @@
"flag.issue.status_id": "Issue status ID",
"flag.issue.tag_ids": "Comma-separated issue tag IDs",
"flag.issue.title": "Issue title",
"flag.jq": "Extract a value from the output by dot-separated path (e.g. data.commits.0.sha)",
"flag.lang": "Display language",
"flag.limit": "Items per page",
"flag.message_settings.all": "Apply to all known setting keys",
"flag.message_settings.channel": "Channel to change: notification, email, or both",
"flag.message_settings.group": "Filter or select groups by short name, for example: Normal,ManageProject",
"flag.message_settings.keys": "Comma-separated setting keys, for example: Normal::Permission,ManageProject::Issue",
"flag.message_settings.login": "Target user login (defaults to current authenticated user)",
"flag.message_settings.preset_name": "Preset name: all-on, all-off, notification-only, email-only",
"flag.message_settings.state": "Desired state: on/off, true/false, enable/disable",
"flag.org.id": "Organization ID",
"flag.org.id_or_login": "Organization ID or login",
"flag.org.name": "Organization name",
@ -258,8 +186,6 @@
"flag.pr.assignee_id": "Assignee user ID",
"flag.pr.base": "Target branch",
"flag.pr.body": "PR description",
"flag.pr.comment_id": "Comment (journal) ID",
"flag.pr.comment_state": "Comment state: opened, resolved, or disabled (server requires one; default opened)",
"flag.pr.file": "Filter diff by file path",
"flag.pr.head": "Source branch",
"flag.pr.id": "PR number",
@ -273,7 +199,6 @@
"flag.pr.reviewer_id": "Reviewer user ID",
"flag.pr.state": "Filter: open, merged, closed",
"flag.pr.tag_id": "Issue tag ID",
"flag.pr.tag_ids": "Comma-separated issue tag IDs",
"flag.pr.title": "PR title",
"flag.pr.version_id": "Patchset version ID",
"flag.profile.end_time": "End time (Unix timestamp)",
@ -281,31 +206,17 @@
"flag.profile.user": "Target user login (defaults to the authenticated user)",
"flag.profile.year": "Year for the contribution heatmap (e.g. 2025)",
"flag.release.body": "Release notes",
"flag.release.archive": "Source archive type to download: zip or tar",
"flag.release.asset": "Attachment ID or filename to download",
"flag.release.force": "Allow overwriting an existing output file",
"flag.release.id": "Release ID",
"flag.release.id_or_tag": "Release ID or tag",
"flag.release.name": "Release name",
"flag.release.output": "Output file or directory path",
"flag.release.prerelease": "Mark as prerelease (true/false)",
"flag.release.tag": "Tag name",
"flag.release.target": "Target branch",
"flag.repo": "Repository name (auto-detected from git remote)",
"flag.repo.category": "Filter: manage/mirror/sync/fork/all (default: manage)",
"flag.repo.clone_branch": "Branch to check out after cloning",
"flag.repo.clone_dir": "Target directory (defaults to the repository name)",
"flag.repo.description": "Repository description",
"flag.repo.edit.category_id": "Project category ID",
"flag.repo.edit.default_branch": "Default branch name",
"flag.repo.edit.language_id": "Project language ID",
"flag.repo.edit.website": "Repository website URL",
"flag.repo.name": "Repository name",
"flag.repo.private": "Make repository private (true/false)",
"flag.repo.target_owner": "Target user or organization login",
"flag.repo.transfer_cancel_dry_run": "Preview the cancel request without changing repository transfer state",
"flag.repo.transfer_dry_run": "Preview the transfer request without changing repository ownership",
"flag.repo.transfer_yes": "Confirm and execute the repository transfer action",
"flag.repo.tree.path": "Directory path to list (default: repository root)",
"flag.repo.tree.ref": "Branch, tag, or commit ref",
"flag.search.keyword": "Search keyword",
@ -323,9 +234,9 @@
"flag.webhook.secret_update": "Webhook secret. Pass it again if the server does not return existing secrets.",
"flag.webhook.type": "Webhook type: gitea/slack/discord/dingtalk/telegram/msteams/feishu/matrix/jianmu/softbot",
"flag.webhook.url": "Webhook target URL",
"output.alias.none": "No aliases configured.",
"output.auth.env_hint": " Or set {env} environment variable",
"output.auth.login_hint": " Run: gitlink-cli auth login",
"output.auth.token_value": " Token: {token}",
"output.config.file": "Config file: {path}",
"output.config.not_set": "(not set)",
"output.doctor.api_auth.config_skipped": "API authentication check skipped because the configuration file is invalid.",
@ -354,9 +265,6 @@
"prompt.auth.password": "Password: ",
"prompt.auth.token": "Paste your access token: ",
"prompt.auth.username": "Username/Email/Phone: ",
"success.alias.deleted": "✓ Alias {name} deleted",
"success.alias.imported": "✓ Imported {count} alias(es)",
"success.alias.set": "✓ Alias {name} set to {expansion}",
"success.auth.logged_in_as": "✓ Logged in as {login}",
"success.auth.logged_in_via_env": "✓ Logged in via {env} environment variable",
"success.auth.logged_out": "✓ Logged out",
@ -365,8 +273,5 @@
"success.config.set": "✓ {key} = {value}",
"warning.auth.not_logged_in": "✗ Not logged in",
"warning.auth.token_unverified": "✓ Token stored (but cannot verify: {message})",
"warning.auth.user_unavailable": "✓ Token stored (user info unavailable)",
"cmd.repo.readme.short": "Read repository README file",
"flag.repo.readme_ref": "Branch, tag, or commit SHA",
"flag.repo.readme_path": "Directory path containing the README, for example docs"
"warning.auth.user_unavailable": "✓ Token stored (user info unavailable)"
}

View File

@ -1,33 +1,23 @@
{
"cmd.alias.delete.short": "删除别名",
"cmd.alias.import.short": "从 YAML 文件导入别名",
"cmd.alias.list.short": "列出所有别名",
"cmd.alias.set.short": "创建或更新别名",
"cmd.alias.short": "管理命令别名",
"cmd.api.long": "向 GitLink API 发送任意 HTTP 请求。认证信息会自动注入。",
"cmd.api.short": "向 GitLink 发起原始 API 请求",
"cmd.auth.login.short": "登录 GitLink",
"cmd.auth.logout.short": "退出 GitLink 登录",
"cmd.auth.short": "认证命令",
"cmd.auth.status.short": "显示认证状态",
"cmd.branch.all.short": "列出全部分支名(不分页)",
"cmd.auth.token.long": "输出 gitlink-cli 调用 API 时实际使用的 token便于脚本使用如 `curl -H \"Authorization: Bearer $(gitlink-cli auth token)\"`。解析顺序GITLINK_TOKEN 环境变量优先,其次是已保存的 token。",
"cmd.auth.token.short": "输出当前生效的认证 token",
"cmd.branch.create.short": "创建分支",
"cmd.branch.delete.short": "删除分支",
"cmd.branch.list.short": "列出分支",
"cmd.branch.protect.short": "设置分支保护",
"cmd.branch.set_default.short": "设置仓库默认分支",
"cmd.branch.short": "分支操作",
"cmd.branch.unprotect.short": "移除分支保护",
"cmd.ci.activate.short": "激活仓库 CI",
"cmd.ci.authorize.short": "显示仓库 CI 授权状态",
"cmd.ci.builds.short": "列出 CI 构建",
"cmd.ci.deactivate.short": "停用仓库 CI",
"cmd.ci.logs.short": "查看构建日志",
"cmd.ci.restart.short": "重启构建",
"cmd.ci.short": "CI/CD 操作",
"cmd.ci.stop.short": "停止构建",
"cmd.completion.long": "为 gitlink-cli 生成 shell 自动补全脚本。\n\n将生成的脚本加载到 shell 配置中,即可启用命令和参数补全。\n\n示例\n gitlink-cli completion bash > ~/.local/share/bash-completion/completions/gitlink-cli\n gitlink-cli completion zsh > \"${fpath[1]}/_gitlink-cli\"\n gitlink-cli completion fish > ~/.config/fish/completions/gitlink-cli.fish\n gitlink-cli completion powershell > gitlink-cli.ps1",
"cmd.completion.short": "生成 shell 自动补全脚本",
"cmd.config.get.short": "获取配置项",
"cmd.config.init.short": "初始化配置文件",
"cmd.config.list.short": "列出所有配置项",
@ -46,7 +36,6 @@
"cmd.dataset.view.short": "查看仓库数据集",
"cmd.doctor.long": "诊断 gitlink-cli 的配置、认证、仓库上下文和 API 连通性问题。",
"cmd.doctor.short": "诊断 gitlink-cli 环境问题",
"cmd.ignore.short": "忽略文件模板操作",
"cmd.issue.batch_close.long": "批量关闭筛选后的议题。\n\n该命令默认处于 dry-run 模式,只打印匹配的议题。\n传入 --yes 后执行远端关闭操作。请使用严格筛选条件和较小 limit。\n\n示例\n gitlink-cli issue +batch-close --owner Gitlink --repo gitlink-cli --older-than-days 60 --limit 20\n gitlink-cli issue +batch-close --owner Gitlink --repo gitlink-cli --older-than-days 60 --limit 20 --yes",
"cmd.issue.batch_close.short": "批量关闭筛选后的议题。默认 dry-run传入 --yes 后执行。",
"cmd.issue.batch_label.long": "给筛选后的议题批量添加标签。\n\n该命令默认处于 dry-run 模式,只打印匹配的议题。\n传入 --yes 后执行远端标签操作。当前实现不会在 API 端点不可用时伪造写入结果。\n\n示例\n gitlink-cli issue +batch-label --owner Gitlink --repo gitlink-cli --add-label stale --older-than-days 30 --limit 50\n gitlink-cli issue +batch-label --owner Gitlink --repo gitlink-cli --add-label stale --older-than-days 30 --limit 50 --yes",
@ -55,52 +44,26 @@
"cmd.issue.batch_list.short": "列出议题批量维护候选项,不修改远端数据",
"cmd.issue.close.short": "关闭议题",
"cmd.issue.comment.short": "给议题添加评论",
"cmd.issue.comment_delete.short": "删除疑修评论",
"cmd.issue.comment_edit.short": "编辑疑修评论",
"cmd.issue.comments.short": "列出疑修的评论journals",
"cmd.issue.create.short": "创建新议题",
"cmd.issue.list.short": "列出议题",
"cmd.invite.accept.long": "通过邀请链接接受项目邀请",
"cmd.invite.accept.short": "接受邀请",
"cmd.invite.generate.long": "生成或获取项目邀请链接",
"cmd.invite.generate.short": "生成邀请链接",
"cmd.invite.join.long": "通过邀请码加入项目",
"cmd.invite.join.short": "加入项目",
"cmd.invite.quit.long": "退出当前项目",
"cmd.invite.quit.short": "退出项目",
"cmd.invite.short": "项目邀请操作",
"cmd.invite.show.long": "通过邀请签名查看邀请链接详情",
"cmd.invite.show.short": "查看邀请链接信息",
"cmd.issue.short": "议题操作",
"cmd.issue.update.short": "更新议题",
"cmd.issue.view.short": "查看议题详情",
"cmd.message_settings.catalog.short": "列出可用的消息通知设置分组和键",
"cmd.message_settings.preset.short": "将预设应用到选中的消息通知设置",
"cmd.message_settings.short": "消息通知设置操作",
"cmd.message_settings.update.short": "在保留其他值的前提下更新选中的消息通知设置",
"cmd.message_settings.view.short": "查看用户当前生效的消息通知设置",
"cmd.org.create.short": "创建组织",
"cmd.org.info.short": "显示组织详情",
"cmd.org.list.short": "列出组织",
"cmd.org.members.short": "列出组织成员",
"cmd.org.short": "组织操作",
"cmd.org.teams.short": "列出组织团队",
"cmd.pr.close.short": "关闭拉取请求",
"cmd.pr.comment.short": "给拉取请求添加评论",
"cmd.pr.comment_delete.short": "删除合并请求评论",
"cmd.pr.comment_edit.short": "编辑合并请求评论",
"cmd.pr.comments.short": "列出合并请求的评论journals",
"cmd.pr.create.short": "创建拉取请求",
"cmd.pr.diff.short": "显示拉取请求 diff",
"cmd.pr.edit.short": "编辑拉取请求并保留未指定字段",
"cmd.pr.files.short": "列出拉取请求中的变更文件",
"cmd.pr.list.short": "列出拉取请求",
"cmd.pr.merge.short": "合并拉取请求",
"cmd.pr.review.short": "创建拉取请求评审",
"cmd.pr.reviews.short": "列出拉取请求评审",
"cmd.pr.short": "拉取请求操作",
"cmd.pr.status.long": "显示当前登录用户在本仓库相关的开启中拉取请求,分为你创建的和请求你评审的两组。",
"cmd.pr.status.short": "显示与你相关的拉取请求",
"cmd.pr.version_diff.short": "显示拉取请求补丁集版本 diff",
"cmd.pr.versions.short": "列出拉取请求补丁集版本",
"cmd.pr.view.short": "查看拉取请求详情",
@ -117,28 +80,18 @@
"cmd.profile.short": "用户画像与统计操作",
"cmd.release.create.short": "创建发布",
"cmd.release.delete.short": "删除发布",
"cmd.release.assets.short": "列出发布附件和源码包",
"cmd.release.download.short": "下载发布附件或源码包",
"cmd.release.list.short": "列出发布",
"cmd.release.short": "发布操作",
"cmd.release.view.short": "查看发布详情",
"cmd.repo.clone.long": "通过平台 API 解析仓库克隆地址并执行 `git clone`,对标 `gh repo clone`。git 进度输出到 stderrJSON/表格结果保持在 stdout。",
"cmd.repo.clone.short": "使用 git 克隆仓库",
"cmd.repo.create.short": "创建新仓库",
"cmd.repo.delete.short": "删除仓库",
"cmd.repo.edit.short": "更新仓库设置",
"cmd.repo.fork.short": "Fork 仓库",
"cmd.repo.info.short": "显示仓库详情",
"cmd.repo.list.short": "列出用户或组织的仓库",
"cmd.repo.short": "仓库操作",
"cmd.repo.transfer.short": "将仓库转移给其他所有者",
"cmd.repo.transfer_cancel.short": "取消待处理的仓库转移",
"cmd.repo.transfer_orgs.short": "列出可接收该仓库的组织",
"cmd.repo.tree.short": "列出仓库文件和目录",
"cmd.root.long": "用于管理 GitLink 上的仓库、议题、拉取请求、发布、CI 和工作流。",
"cmd.root.short": "GitLink CLI - GitLink 命令行工具",
"cmd.search.recommend.long": "列出平台推荐/精选项目ID、名称、访问量、作者、分类。",
"cmd.search.recommend.short": "列出推荐项目",
"cmd.search.repos.short": "搜索仓库",
"cmd.search.short": "搜索操作",
"cmd.search.users.short": "搜索用户",
@ -154,22 +107,16 @@
"cmd.webhook.test.short": "触发 Webhook 测试投递",
"cmd.webhook.update.short": "更新仓库 Webhook并在可用时保留未指定字段",
"cmd.webhook.view.short": "查看 Webhook 详情",
"error.alias.empty_expansion": "别名展开内容不能为空",
"error.alias.import_parse": "解析别名失败:{message}",
"error.alias.import_read": "读取别名失败:{message}",
"error.alias.not_found": "别名不存在:{name}",
"error.alias.save_failed": "保存别名失败:{message}",
"error.auth.delete_token_failed": "删除 Token 失败:{message}",
"error.auth.login_failed": "登录失败:{message}",
"error.auth.no_token": "未找到 token请运行 `gitlink-cli auth login` 或设置 GITLINK_TOKEN",
"error.auth.store_token_failed": "保存 Token 失败:{message}",
"error.auth.token_empty": "Token 不能为空",
"error.config.save_failed": "保存配置失败:{message}",
"error.dataset.delete_confirm": "删除数据集附件具有破坏性;请先 --dry-run 预览,再传 --yes 确认",
"error.missing_required_flag": "缺少必需参数 --{name}",
"error.profile.user_required": "无法确定目标用户;请通过 --user 指定,或先运行 gitlink-cli auth login 登录",
"error.repo.clone_url_missing": "仓库响应缺少 clone_url请检查 owner/repo 是否正确",
"error.unsupported_language": "不支持的语言:{lang}",
"flag.alias.file": "从 YAML 文件读取别名(默认:标准输入)",
"flag.api.batch_continue_on_error": "批处理请求失败后继续执行后续请求",
"flag.api.batch_dry_run": "预览批处理请求,不发送远端请求",
"flag.api.batch_file": "从 JSON 文件读取 API 批处理计划",
@ -178,20 +125,16 @@
"flag.api.body_file": "从文件读取 JSON 请求体",
"flag.api.body_stdin": "从标准输入读取 JSON 请求体",
"flag.api.header": "附加请求头key:value",
"flag.api.paginate": "抓取所有分页结果并合并为一个数组输出",
"flag.api.query": "查询参数key=val&key2=val2",
"flag.auth.show_token": "在状态输出中显示原始 token",
"flag.auth.token": "通过粘贴已有 Token 登录",
"flag.auth.with_token": "从标准输入读取令牌(非交互,适合脚本/CI",
"flag.branch.from": "源分支或 Commit",
"flag.branch.name": "分支名称",
"flag.ci.activate_dry_run": "预览 CI 激活请求,不修改远端状态",
"flag.ci.activate_yes": "确认激活仓库 CI",
"flag.ci.build": "构建编号",
"flag.ci.deactivate_dry_run": "预览 CI 停用请求,不修改远端状态",
"flag.ci.deactivate_yes": "确认停用仓库 CI",
"flag.ci.stage": "阶段编号",
"flag.ci.step": "步骤编号",
"flag.comment.body": "评论内容",
"flag.completion.no_descriptions": "关闭补全描述",
"flag.dataset.description": "数据集描述",
"flag.dataset.dry_run": "预览请求,不写入数据集",
"flag.dataset.dry_run_delete": "预览请求,不删除附件",
@ -208,10 +151,6 @@
"flag.doctor.skip_network": "跳过需要访问 GitLink 的认证连通性检查",
"flag.dry_run": "预览请求,不实际创建",
"flag.format": "输出格式json、table、yaml默认table",
"flag.invite.code": "项目邀请码",
"flag.invite.is-apply": "是否为申请模式true/false",
"flag.invite.role": "邀请角色(如 developer、manager",
"flag.invite.sign": "邀请签名/邀请码",
"flag.issue.add_label": "要添加到每个匹配议题的标签",
"flag.issue.assignee": "负责人登录名",
"flag.issue.assignee_id": "负责人用户 ID",
@ -224,9 +163,6 @@
"flag.issue.batch_list.limit": "最多返回的议题数,上限 100",
"flag.issue.batch_process.limit": "最多处理的议题数,上限 100",
"flag.issue.body": "议题描述",
"flag.issue.comment_id": "评论journalID",
"flag.issue.comments_category": "journal 类别过滤comment 或 operate",
"flag.issue.comments_keyword": "按关键字过滤评论",
"flag.issue.label": "标签 ID",
"flag.issue.label_filter": "按已有标签筛选",
"flag.issue.milestone": "里程碑 ID",
@ -240,16 +176,8 @@
"flag.issue.status_id": "议题状态 ID",
"flag.issue.tag_ids": "逗号分隔的议题标签 ID",
"flag.issue.title": "议题标题",
"flag.jq": "按点分路径从输出中提取字段(如 data.commits.0.sha",
"flag.lang": "显示语言",
"flag.limit": "每页条目数",
"flag.message_settings.all": "应用到所有已知的设置键",
"flag.message_settings.channel": "要修改的通道notification、email 或 both",
"flag.message_settings.group": "按短分组名筛选或选中分组例如Normal,ManageProject",
"flag.message_settings.keys": "逗号分隔的设置键例如Normal::Permission,ManageProject::Issue",
"flag.message_settings.login": "目标用户登录名(默认:当前认证用户)",
"flag.message_settings.preset_name": "预设名称all-on、all-off、notification-only、email-only",
"flag.message_settings.state": "目标状态on/off、true/false、enable/disable",
"flag.org.id": "组织 ID",
"flag.org.id_or_login": "组织 ID 或登录名",
"flag.org.name": "组织名称",
@ -258,8 +186,6 @@
"flag.pr.assignee_id": "指派人用户 ID",
"flag.pr.base": "目标分支",
"flag.pr.body": "PR 描述",
"flag.pr.comment_id": "评论journalID",
"flag.pr.comment_state": "评论状态opened、resolved 或 disabled服务端必填默认 opened",
"flag.pr.file": "按文件路径筛选 diff",
"flag.pr.head": "源分支",
"flag.pr.id": "PR 编号",
@ -273,7 +199,6 @@
"flag.pr.reviewer_id": "评审人用户 ID",
"flag.pr.state": "筛选open、merged、closed",
"flag.pr.tag_id": "议题标签 ID",
"flag.pr.tag_ids": "逗号分隔的议题标签 ID",
"flag.pr.title": "PR 标题",
"flag.pr.version_id": "补丁集版本 ID",
"flag.profile.end_time": "结束时间Unix 时间戳)",
@ -281,31 +206,17 @@
"flag.profile.user": "目标用户登录名(默认为当前认证用户)",
"flag.profile.year": "贡献热力图的年份(如 2025",
"flag.release.body": "发布说明",
"flag.release.archive": "下载源码包类型zip 或 tar",
"flag.release.asset": "要下载的附件 ID 或文件名",
"flag.release.force": "允许覆盖已存在的输出文件",
"flag.release.id": "发布 ID",
"flag.release.id_or_tag": "发布 ID 或标签",
"flag.release.name": "发布名称",
"flag.release.output": "输出文件或目录路径",
"flag.release.prerelease": "标记为预发布true/false",
"flag.release.tag": "标签名称",
"flag.release.target": "目标分支",
"flag.repo": "仓库名称(自动从 git remote 检测)",
"flag.repo.category": "筛选manage/mirror/sync/fork/all默认manage",
"flag.repo.clone_branch": "克隆后检出的分支",
"flag.repo.clone_dir": "目标目录(默认使用仓库名)",
"flag.repo.description": "仓库描述",
"flag.repo.edit.category_id": "项目分类 ID",
"flag.repo.edit.default_branch": "默认分支名称",
"flag.repo.edit.language_id": "项目语言 ID",
"flag.repo.edit.website": "仓库网站 URL",
"flag.repo.name": "仓库名称",
"flag.repo.private": "设为私有仓库true/false",
"flag.repo.target_owner": "目标用户或组织登录名",
"flag.repo.transfer_cancel_dry_run": "预览取消请求,不修改仓库转移状态",
"flag.repo.transfer_dry_run": "预览转移请求,不修改仓库所有者",
"flag.repo.transfer_yes": "确认并执行仓库转移相关操作",
"flag.repo.tree.path": "要列出的目录路径(默认:仓库根目录)",
"flag.repo.tree.ref": "分支、标签或提交引用",
"flag.search.keyword": "搜索关键词",
@ -323,9 +234,9 @@
"flag.webhook.secret_update": "Webhook 密钥。如果服务端不返回已有密钥,请再次传入。",
"flag.webhook.type": "Webhook 类型gitea/slack/discord/dingtalk/telegram/msteams/feishu/matrix/jianmu/softbot",
"flag.webhook.url": "Webhook 目标 URL",
"output.alias.none": "尚未配置任何别名。",
"output.auth.env_hint": " 或设置 {env} 环境变量",
"output.auth.login_hint": " 运行gitlink-cli auth login",
"output.auth.token_value": " Token: {token}",
"output.config.file": "配置文件:{path}",
"output.config.not_set": "(未设置)",
"output.doctor.api_auth.config_skipped": "配置文件无效,已跳过 API 认证检查。",
@ -354,9 +265,6 @@
"prompt.auth.password": "密码:",
"prompt.auth.token": "粘贴你的访问 Token",
"prompt.auth.username": "用户名/邮箱/手机号:",
"success.alias.deleted": "✓ 已删除别名 {name}",
"success.alias.imported": "✓ 已导入 {count} 个别名",
"success.alias.set": "✓ 已设置别名 {name} = {expansion}",
"success.auth.logged_in_as": "✓ 已登录为 {login}",
"success.auth.logged_in_via_env": "✓ 已通过 {env} 环境变量登录",
"success.auth.logged_out": "✓ 已退出登录",
@ -365,8 +273,5 @@
"success.config.set": "✓ 已设置 {key} = {value}",
"warning.auth.not_logged_in": "✗ 未登录",
"warning.auth.token_unverified": "✓ Token 已保存(但无法验证:{message}",
"warning.auth.user_unavailable": "✓ Token 已保存(用户信息不可用)",
"cmd.repo.readme.short": "读取仓库 README 文件",
"flag.repo.readme_ref": "分支、标签或 Commit SHA",
"flag.repo.readme_path": "README 所在目录路径,例如 docs"
"warning.auth.user_unavailable": "✓ Token 已保存(用户信息不可用)"
}