From df012dec90590ecba85a69ed6355cfa8382c1da3 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sat, 28 Mar 2026 11:17:50 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E9=98=BF=E9=87=8C=E4=BA=91dcdn?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=A0=B9=E6=8D=AE=E8=AF=81=E4=B9=A6=E5=9F=9F?= =?UTF-8?q?=E5=90=8D=E5=8C=B9=E9=85=8D=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/using-superpowers/SKILL.md | 115 ++++++++++++++++++ .../references/codex-tools.md | 100 +++++++++++++++ .../references/gemini-tools.md | 33 +++++ .cursor/skills/using-superpowers/SKILL.md | 115 ++++++++++++++++++ .../references/codex-tools.md | 100 +++++++++++++++ .../references/gemini-tools.md | 33 +++++ .trae/skills/task-plugin-dev/SKILL.md | 68 +++++++++-- .trae/skills/using-superpowers/SKILL.md | 115 ++++++++++++++++++ .../references/codex-tools.md | 100 +++++++++++++++ .../references/gemini-tools.md | 33 +++++ packages/core/pipeline/src/plugin/api.ts | 7 +- .../src/locales/langs/zh-CN/certd.ts | 4 +- .../src/locales/langs/zh-CN/vip.ts | 6 +- .../src/views/sys/account/index.vue | 2 +- .../mine/setting-two-factor-controller.ts | 4 +- .../user/mine/user-settings-controller.ts | 2 +- .../modules/login/service/login-service.ts | 2 +- .../pipeline/service/notification-service.ts | 2 +- .../pipeline/service/pipeline-service.ts | 12 +- .../plugin/deploy-to-dcdn/index.ts | 95 ++++++++++++--- 20 files changed, 906 insertions(+), 42 deletions(-) create mode 100644 .agents/skills/using-superpowers/SKILL.md create mode 100644 .agents/skills/using-superpowers/references/codex-tools.md create mode 100644 .agents/skills/using-superpowers/references/gemini-tools.md create mode 100644 .cursor/skills/using-superpowers/SKILL.md create mode 100644 .cursor/skills/using-superpowers/references/codex-tools.md create mode 100644 .cursor/skills/using-superpowers/references/gemini-tools.md create mode 100644 .trae/skills/using-superpowers/SKILL.md create mode 100644 .trae/skills/using-superpowers/references/codex-tools.md create mode 100644 .trae/skills/using-superpowers/references/gemini-tools.md diff --git a/.agents/skills/using-superpowers/SKILL.md b/.agents/skills/using-superpowers/SKILL.md new file mode 100644 index 000000000..d81353529 --- /dev/null +++ b/.agents/skills/using-superpowers/SKILL.md @@ -0,0 +1,115 @@ +--- +name: using-superpowers +description: Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions +--- + + +If you were dispatched as a subagent to execute a specific task, skip this skill. + + + +If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill. + +IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT. + +This is not negotiable. This is not optional. You cannot rationalize your way out of this. + + +## Instruction Priority + +Superpowers skills override default system prompt behavior, but **user instructions always take precedence**: + +1. **User's explicit instructions** (CLAUDE.md, GEMINI.md, AGENTS.md, direct requests) — highest priority +2. **Superpowers skills** — override default system behavior where they conflict +3. **Default system prompt** — lowest priority + +If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "always use TDD," follow the user's instructions. The user is in control. + +## How to Access Skills + +**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files. + +**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand. + +**In other environments:** Check your platform's documentation for how skills are loaded. + +## Platform Adaptation + +Skills use Claude Code tool names. Non-CC platforms: see `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md. + +# Using Skills + +## The Rule + +**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means that you should invoke the skill to check. If an invoked skill turns out to be wrong for the situation, you don't need to use it. + +```dot +digraph skill_flow { + "User message received" [shape=doublecircle]; + "About to EnterPlanMode?" [shape=doublecircle]; + "Already brainstormed?" [shape=diamond]; + "Invoke brainstorming skill" [shape=box]; + "Might any skill apply?" [shape=diamond]; + "Invoke Skill tool" [shape=box]; + "Announce: 'Using [skill] to [purpose]'" [shape=box]; + "Has checklist?" [shape=diamond]; + "Create TodoWrite todo per item" [shape=box]; + "Follow skill exactly" [shape=box]; + "Respond (including clarifications)" [shape=doublecircle]; + + "About to EnterPlanMode?" -> "Already brainstormed?"; + "Already brainstormed?" -> "Invoke brainstorming skill" [label="no"]; + "Already brainstormed?" -> "Might any skill apply?" [label="yes"]; + "Invoke brainstorming skill" -> "Might any skill apply?"; + + "User message received" -> "Might any skill apply?"; + "Might any skill apply?" -> "Invoke Skill tool" [label="yes, even 1%"]; + "Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"]; + "Invoke Skill tool" -> "Announce: 'Using [skill] to [purpose]'"; + "Announce: 'Using [skill] to [purpose]'" -> "Has checklist?"; + "Has checklist?" -> "Create TodoWrite todo per item" [label="yes"]; + "Has checklist?" -> "Follow skill exactly" [label="no"]; + "Create TodoWrite todo per item" -> "Follow skill exactly"; +} +``` + +## Red Flags + +These thoughts mean STOP—you're rationalizing: + +| Thought | Reality | +|---------|---------| +| "This is just a simple question" | Questions are tasks. Check for skills. | +| "I need more context first" | Skill check comes BEFORE clarifying questions. | +| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. | +| "I can check git/files quickly" | Files lack conversation context. Check for skills. | +| "Let me gather information first" | Skills tell you HOW to gather information. | +| "This doesn't need a formal skill" | If a skill exists, use it. | +| "I remember this skill" | Skills evolve. Read current version. | +| "This doesn't count as a task" | Action = task. Check for skills. | +| "The skill is overkill" | Simple things become complex. Use it. | +| "I'll just do this one thing first" | Check BEFORE doing anything. | +| "This feels productive" | Undisciplined action wastes time. Skills prevent this. | +| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. | + +## Skill Priority + +When multiple skills could apply, use this order: + +1. **Process skills first** (brainstorming, debugging) - these determine HOW to approach the task +2. **Implementation skills second** (frontend-design, mcp-builder) - these guide execution + +"Let's build X" → brainstorming first, then implementation skills. +"Fix this bug" → debugging first, then domain-specific skills. + +## Skill Types + +**Rigid** (TDD, debugging): Follow exactly. Don't adapt away discipline. + +**Flexible** (patterns): Adapt principles to context. + +The skill itself tells you which. + +## User Instructions + +Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows. diff --git a/.agents/skills/using-superpowers/references/codex-tools.md b/.agents/skills/using-superpowers/references/codex-tools.md new file mode 100644 index 000000000..539b2b1c7 --- /dev/null +++ b/.agents/skills/using-superpowers/references/codex-tools.md @@ -0,0 +1,100 @@ +# Codex Tool Mapping + +Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: + +| Skill references | Codex equivalent | +|-----------------|------------------| +| `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) | +| Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls | +| Task returns result | `wait` | +| Task completes automatically | `close_agent` to free slot | +| `TodoWrite` (task tracking) | `update_plan` | +| `Skill` tool (invoke a skill) | Skills load natively — just follow the instructions | +| `Read`, `Write`, `Edit` (files) | Use your native file tools | +| `Bash` (run commands) | Use your native shell tools | + +## Subagent dispatch requires multi-agent support + +Add to your Codex config (`~/.codex/config.toml`): + +```toml +[features] +multi_agent = true +``` + +This enables `spawn_agent`, `wait`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`. + +## Named agent dispatch + +Claude Code skills reference named agent types like `superpowers:code-reviewer`. +Codex does not have a named agent registry — `spawn_agent` creates generic agents +from built-in roles (`default`, `explorer`, `worker`). + +When a skill says to dispatch a named agent type: + +1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's + local prompt template like `code-quality-reviewer-prompt.md`) +2. Read the prompt content +3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.) +4. Spawn a `worker` agent with the filled content as the `message` + +| Skill instruction | Codex equivalent | +|-------------------|------------------| +| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content | +| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt | + +### Message framing + +The `message` parameter is user-level input, not a system prompt. Structure it +for maximum instruction adherence: + +``` +Your task is to perform the following. Follow the instructions below exactly. + + +[filled prompt content from the agent's .md file] + + +Execute this now. Output ONLY the structured response following the format +specified in the instructions above. +``` + +- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...") +- Wrap instructions in XML tags — the model treats tagged blocks as authoritative +- End with an explicit execution directive to prevent summarization of the instructions + +### When this workaround can be removed + +This approach compensates for Codex's plugin system not yet supporting an `agents` +field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the +plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and +skills can dispatch named agent types directly. + +## Environment Detection + +Skills that create worktrees or finish branches should detect their +environment with read-only git commands before proceeding: + +```bash +GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P) +GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P) +BRANCH=$(git branch --show-current) +``` + +- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation) +- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox) + +See `using-git-worktrees` Step 0 and `finishing-a-development-branch` +Step 1 for how each skill uses these signals. + +## Codex App Finishing + +When the sandbox blocks branch/push operations (detached HEAD in an +externally managed worktree), the agent commits all work and informs +the user to use the App's native controls: + +- **"Create branch"** — names the branch, then commit/push/PR via App UI +- **"Hand off to local"** — transfers work to the user's local checkout + +The agent can still run tests, stage files, and output suggested branch +names, commit messages, and PR descriptions for the user to copy. diff --git a/.agents/skills/using-superpowers/references/gemini-tools.md b/.agents/skills/using-superpowers/references/gemini-tools.md new file mode 100644 index 000000000..f8698033b --- /dev/null +++ b/.agents/skills/using-superpowers/references/gemini-tools.md @@ -0,0 +1,33 @@ +# Gemini CLI Tool Mapping + +Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: + +| Skill references | Gemini CLI equivalent | +|-----------------|----------------------| +| `Read` (file reading) | `read_file` | +| `Write` (file creation) | `write_file` | +| `Edit` (file editing) | `replace` | +| `Bash` (run commands) | `run_shell_command` | +| `Grep` (search file content) | `grep_search` | +| `Glob` (search files by name) | `glob` | +| `TodoWrite` (task tracking) | `write_todos` | +| `Skill` tool (invoke a skill) | `activate_skill` | +| `WebSearch` | `google_web_search` | +| `WebFetch` | `web_fetch` | +| `Task` tool (dispatch subagent) | No equivalent — Gemini CLI does not support subagents | + +## No subagent support + +Gemini CLI has no equivalent to Claude Code's `Task` tool. Skills that rely on subagent dispatch (`subagent-driven-development`, `dispatching-parallel-agents`) will fall back to single-session execution via `executing-plans`. + +## Additional Gemini CLI tools + +These tools are available in Gemini CLI but have no Claude Code equivalent: + +| Tool | Purpose | +|------|---------| +| `list_directory` | List files and subdirectories | +| `save_memory` | Persist facts to GEMINI.md across sessions | +| `ask_user` | Request structured input from the user | +| `tracker_create_task` | Rich task management (create, update, list, visualize) | +| `enter_plan_mode` / `exit_plan_mode` | Switch to read-only research mode before making changes | diff --git a/.cursor/skills/using-superpowers/SKILL.md b/.cursor/skills/using-superpowers/SKILL.md new file mode 100644 index 000000000..d81353529 --- /dev/null +++ b/.cursor/skills/using-superpowers/SKILL.md @@ -0,0 +1,115 @@ +--- +name: using-superpowers +description: Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions +--- + + +If you were dispatched as a subagent to execute a specific task, skip this skill. + + + +If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill. + +IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT. + +This is not negotiable. This is not optional. You cannot rationalize your way out of this. + + +## Instruction Priority + +Superpowers skills override default system prompt behavior, but **user instructions always take precedence**: + +1. **User's explicit instructions** (CLAUDE.md, GEMINI.md, AGENTS.md, direct requests) — highest priority +2. **Superpowers skills** — override default system behavior where they conflict +3. **Default system prompt** — lowest priority + +If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "always use TDD," follow the user's instructions. The user is in control. + +## How to Access Skills + +**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files. + +**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand. + +**In other environments:** Check your platform's documentation for how skills are loaded. + +## Platform Adaptation + +Skills use Claude Code tool names. Non-CC platforms: see `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md. + +# Using Skills + +## The Rule + +**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means that you should invoke the skill to check. If an invoked skill turns out to be wrong for the situation, you don't need to use it. + +```dot +digraph skill_flow { + "User message received" [shape=doublecircle]; + "About to EnterPlanMode?" [shape=doublecircle]; + "Already brainstormed?" [shape=diamond]; + "Invoke brainstorming skill" [shape=box]; + "Might any skill apply?" [shape=diamond]; + "Invoke Skill tool" [shape=box]; + "Announce: 'Using [skill] to [purpose]'" [shape=box]; + "Has checklist?" [shape=diamond]; + "Create TodoWrite todo per item" [shape=box]; + "Follow skill exactly" [shape=box]; + "Respond (including clarifications)" [shape=doublecircle]; + + "About to EnterPlanMode?" -> "Already brainstormed?"; + "Already brainstormed?" -> "Invoke brainstorming skill" [label="no"]; + "Already brainstormed?" -> "Might any skill apply?" [label="yes"]; + "Invoke brainstorming skill" -> "Might any skill apply?"; + + "User message received" -> "Might any skill apply?"; + "Might any skill apply?" -> "Invoke Skill tool" [label="yes, even 1%"]; + "Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"]; + "Invoke Skill tool" -> "Announce: 'Using [skill] to [purpose]'"; + "Announce: 'Using [skill] to [purpose]'" -> "Has checklist?"; + "Has checklist?" -> "Create TodoWrite todo per item" [label="yes"]; + "Has checklist?" -> "Follow skill exactly" [label="no"]; + "Create TodoWrite todo per item" -> "Follow skill exactly"; +} +``` + +## Red Flags + +These thoughts mean STOP—you're rationalizing: + +| Thought | Reality | +|---------|---------| +| "This is just a simple question" | Questions are tasks. Check for skills. | +| "I need more context first" | Skill check comes BEFORE clarifying questions. | +| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. | +| "I can check git/files quickly" | Files lack conversation context. Check for skills. | +| "Let me gather information first" | Skills tell you HOW to gather information. | +| "This doesn't need a formal skill" | If a skill exists, use it. | +| "I remember this skill" | Skills evolve. Read current version. | +| "This doesn't count as a task" | Action = task. Check for skills. | +| "The skill is overkill" | Simple things become complex. Use it. | +| "I'll just do this one thing first" | Check BEFORE doing anything. | +| "This feels productive" | Undisciplined action wastes time. Skills prevent this. | +| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. | + +## Skill Priority + +When multiple skills could apply, use this order: + +1. **Process skills first** (brainstorming, debugging) - these determine HOW to approach the task +2. **Implementation skills second** (frontend-design, mcp-builder) - these guide execution + +"Let's build X" → brainstorming first, then implementation skills. +"Fix this bug" → debugging first, then domain-specific skills. + +## Skill Types + +**Rigid** (TDD, debugging): Follow exactly. Don't adapt away discipline. + +**Flexible** (patterns): Adapt principles to context. + +The skill itself tells you which. + +## User Instructions + +Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows. diff --git a/.cursor/skills/using-superpowers/references/codex-tools.md b/.cursor/skills/using-superpowers/references/codex-tools.md new file mode 100644 index 000000000..539b2b1c7 --- /dev/null +++ b/.cursor/skills/using-superpowers/references/codex-tools.md @@ -0,0 +1,100 @@ +# Codex Tool Mapping + +Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: + +| Skill references | Codex equivalent | +|-----------------|------------------| +| `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) | +| Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls | +| Task returns result | `wait` | +| Task completes automatically | `close_agent` to free slot | +| `TodoWrite` (task tracking) | `update_plan` | +| `Skill` tool (invoke a skill) | Skills load natively — just follow the instructions | +| `Read`, `Write`, `Edit` (files) | Use your native file tools | +| `Bash` (run commands) | Use your native shell tools | + +## Subagent dispatch requires multi-agent support + +Add to your Codex config (`~/.codex/config.toml`): + +```toml +[features] +multi_agent = true +``` + +This enables `spawn_agent`, `wait`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`. + +## Named agent dispatch + +Claude Code skills reference named agent types like `superpowers:code-reviewer`. +Codex does not have a named agent registry — `spawn_agent` creates generic agents +from built-in roles (`default`, `explorer`, `worker`). + +When a skill says to dispatch a named agent type: + +1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's + local prompt template like `code-quality-reviewer-prompt.md`) +2. Read the prompt content +3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.) +4. Spawn a `worker` agent with the filled content as the `message` + +| Skill instruction | Codex equivalent | +|-------------------|------------------| +| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content | +| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt | + +### Message framing + +The `message` parameter is user-level input, not a system prompt. Structure it +for maximum instruction adherence: + +``` +Your task is to perform the following. Follow the instructions below exactly. + + +[filled prompt content from the agent's .md file] + + +Execute this now. Output ONLY the structured response following the format +specified in the instructions above. +``` + +- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...") +- Wrap instructions in XML tags — the model treats tagged blocks as authoritative +- End with an explicit execution directive to prevent summarization of the instructions + +### When this workaround can be removed + +This approach compensates for Codex's plugin system not yet supporting an `agents` +field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the +plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and +skills can dispatch named agent types directly. + +## Environment Detection + +Skills that create worktrees or finish branches should detect their +environment with read-only git commands before proceeding: + +```bash +GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P) +GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P) +BRANCH=$(git branch --show-current) +``` + +- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation) +- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox) + +See `using-git-worktrees` Step 0 and `finishing-a-development-branch` +Step 1 for how each skill uses these signals. + +## Codex App Finishing + +When the sandbox blocks branch/push operations (detached HEAD in an +externally managed worktree), the agent commits all work and informs +the user to use the App's native controls: + +- **"Create branch"** — names the branch, then commit/push/PR via App UI +- **"Hand off to local"** — transfers work to the user's local checkout + +The agent can still run tests, stage files, and output suggested branch +names, commit messages, and PR descriptions for the user to copy. diff --git a/.cursor/skills/using-superpowers/references/gemini-tools.md b/.cursor/skills/using-superpowers/references/gemini-tools.md new file mode 100644 index 000000000..f8698033b --- /dev/null +++ b/.cursor/skills/using-superpowers/references/gemini-tools.md @@ -0,0 +1,33 @@ +# Gemini CLI Tool Mapping + +Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: + +| Skill references | Gemini CLI equivalent | +|-----------------|----------------------| +| `Read` (file reading) | `read_file` | +| `Write` (file creation) | `write_file` | +| `Edit` (file editing) | `replace` | +| `Bash` (run commands) | `run_shell_command` | +| `Grep` (search file content) | `grep_search` | +| `Glob` (search files by name) | `glob` | +| `TodoWrite` (task tracking) | `write_todos` | +| `Skill` tool (invoke a skill) | `activate_skill` | +| `WebSearch` | `google_web_search` | +| `WebFetch` | `web_fetch` | +| `Task` tool (dispatch subagent) | No equivalent — Gemini CLI does not support subagents | + +## No subagent support + +Gemini CLI has no equivalent to Claude Code's `Task` tool. Skills that rely on subagent dispatch (`subagent-driven-development`, `dispatching-parallel-agents`) will fall back to single-session execution via `executing-plans`. + +## Additional Gemini CLI tools + +These tools are available in Gemini CLI but have no Claude Code equivalent: + +| Tool | Purpose | +|------|---------| +| `list_directory` | List files and subdirectories | +| `save_memory` | Persist facts to GEMINI.md across sessions | +| `ask_user` | Request structured input from the user | +| `tracker_create_task` | Rich task management (create, update, list, visualize) | +| `enter_plan_mode` / `exit_plan_mode` | Switch to read-only research mode before making changes | diff --git a/.trae/skills/task-plugin-dev/SKILL.md b/.trae/skills/task-plugin-dev/SKILL.md index b4f1dd021..8f145089c 100644 --- a/.trae/skills/task-plugin-dev/SKILL.md +++ b/.trae/skills/task-plugin-dev/SKILL.md @@ -89,16 +89,55 @@ certDomains!: string[]; accessId!: string; ``` -### 4. 实现插件方法 +### 4. 动态显隐配置(mergeScript) -#### 4.1 插件实例化时执行的方法 +使用 `mergeScript` 可以实现根据其他输入值动态控制当前输入项的显隐状态。 + +```typescript +@TaskInput({ + title: '匹配模式', + component: { + name: 'select', + options: [ + { label: '手动选择', value: 'manual' }, + { label: '根据证书匹配', value: 'auto' }, + ], + }, + default: 'manual', +}) +domainMatchMode!: 'manual' | 'auto'; + +@TaskInput( + createRemoteSelectInputDefine({ + title: 'DCDN加速域名', + helper: '你在阿里云上配置的DCDN加速域名', + action: DeployCertToAliyunDCDN.prototype.onGetDomainList.name, + watches: ['certDomains', 'accessId'], + required: true, + mergeScript: ` + return { + show: ctx.compute(({form})=>{ + return domainMatchMode === "manual" + }) + } + `, + }) +) +domainName!: string | string[]; +``` + +`mergeScript` 中的 `ctx.compute` 函数接收一个回调函数,通过 `form` 参数可以访问表单中的其他字段值。 + +### 5. 实现插件方法 + +#### 5.1 插件实例化时执行的方法 ```typescript // 插件实例化时执行的方法 async onInstance() {} ``` -#### 4.2 插件执行方法 +#### 5.2 插件执行方法 ```typescript // 插件执行方法 @@ -130,7 +169,9 @@ async execute(): Promise { } ``` -#### 4.3 后端获取选项方法 +#### 5.3 后端获取选项方法 + +使用 `createRemoteSelectInputDefine` 创建远程选择输入项,`action` 指向的方法接收 `PageSearch` 参数并返回 `{ list, total }` 格式。 ```typescript @TaskInput( @@ -145,8 +186,8 @@ async execute(): Promise { ) siteName!: string | string[]; -// 从后端获取选项的方法 -async onGetSiteList(req: PageSearch) { +// 从后端获取选项的方法,接收PageSearch参数 +async onGetSiteList(data: PageSearch) { if (!this.accessId) { throw new Error('请选择Access授权'); } @@ -154,7 +195,7 @@ async onGetSiteList(req: PageSearch) { // @ts-ignore const access = await this.getAccess(this.accessId); - // const siteRes = await access.GetDomainList(req); + // const siteRes = await access.GetDomainList(data); // 以下是模拟数据 const siteRes = [ { id: 1, siteName: 'site1.com' }, @@ -169,8 +210,12 @@ async onGetSiteList(req: PageSearch) { domain: item.siteName, }; }); - // 将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择 - return optionsUtils.buildGroupOptions(options, this.certDomains); + + // 返回{list, total}格式 + return { + list: optionsUtils.buildGroupOptions(options, this.certDomains), + total: siteRes.length, + }; } ``` @@ -383,7 +428,10 @@ export class DemoTest extends AbstractTaskPlugin { }; }); //将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择 - return optionsUtils.buildGroupOptions(options, this.certDomains); + return { + list: optionsUtils.buildGroupOptions(options, this.certDomains), + total: siteRes.length, + }; } } ``` \ No newline at end of file diff --git a/.trae/skills/using-superpowers/SKILL.md b/.trae/skills/using-superpowers/SKILL.md new file mode 100644 index 000000000..d81353529 --- /dev/null +++ b/.trae/skills/using-superpowers/SKILL.md @@ -0,0 +1,115 @@ +--- +name: using-superpowers +description: Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions +--- + + +If you were dispatched as a subagent to execute a specific task, skip this skill. + + + +If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill. + +IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT. + +This is not negotiable. This is not optional. You cannot rationalize your way out of this. + + +## Instruction Priority + +Superpowers skills override default system prompt behavior, but **user instructions always take precedence**: + +1. **User's explicit instructions** (CLAUDE.md, GEMINI.md, AGENTS.md, direct requests) — highest priority +2. **Superpowers skills** — override default system behavior where they conflict +3. **Default system prompt** — lowest priority + +If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "always use TDD," follow the user's instructions. The user is in control. + +## How to Access Skills + +**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files. + +**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand. + +**In other environments:** Check your platform's documentation for how skills are loaded. + +## Platform Adaptation + +Skills use Claude Code tool names. Non-CC platforms: see `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md. + +# Using Skills + +## The Rule + +**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means that you should invoke the skill to check. If an invoked skill turns out to be wrong for the situation, you don't need to use it. + +```dot +digraph skill_flow { + "User message received" [shape=doublecircle]; + "About to EnterPlanMode?" [shape=doublecircle]; + "Already brainstormed?" [shape=diamond]; + "Invoke brainstorming skill" [shape=box]; + "Might any skill apply?" [shape=diamond]; + "Invoke Skill tool" [shape=box]; + "Announce: 'Using [skill] to [purpose]'" [shape=box]; + "Has checklist?" [shape=diamond]; + "Create TodoWrite todo per item" [shape=box]; + "Follow skill exactly" [shape=box]; + "Respond (including clarifications)" [shape=doublecircle]; + + "About to EnterPlanMode?" -> "Already brainstormed?"; + "Already brainstormed?" -> "Invoke brainstorming skill" [label="no"]; + "Already brainstormed?" -> "Might any skill apply?" [label="yes"]; + "Invoke brainstorming skill" -> "Might any skill apply?"; + + "User message received" -> "Might any skill apply?"; + "Might any skill apply?" -> "Invoke Skill tool" [label="yes, even 1%"]; + "Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"]; + "Invoke Skill tool" -> "Announce: 'Using [skill] to [purpose]'"; + "Announce: 'Using [skill] to [purpose]'" -> "Has checklist?"; + "Has checklist?" -> "Create TodoWrite todo per item" [label="yes"]; + "Has checklist?" -> "Follow skill exactly" [label="no"]; + "Create TodoWrite todo per item" -> "Follow skill exactly"; +} +``` + +## Red Flags + +These thoughts mean STOP—you're rationalizing: + +| Thought | Reality | +|---------|---------| +| "This is just a simple question" | Questions are tasks. Check for skills. | +| "I need more context first" | Skill check comes BEFORE clarifying questions. | +| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. | +| "I can check git/files quickly" | Files lack conversation context. Check for skills. | +| "Let me gather information first" | Skills tell you HOW to gather information. | +| "This doesn't need a formal skill" | If a skill exists, use it. | +| "I remember this skill" | Skills evolve. Read current version. | +| "This doesn't count as a task" | Action = task. Check for skills. | +| "The skill is overkill" | Simple things become complex. Use it. | +| "I'll just do this one thing first" | Check BEFORE doing anything. | +| "This feels productive" | Undisciplined action wastes time. Skills prevent this. | +| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. | + +## Skill Priority + +When multiple skills could apply, use this order: + +1. **Process skills first** (brainstorming, debugging) - these determine HOW to approach the task +2. **Implementation skills second** (frontend-design, mcp-builder) - these guide execution + +"Let's build X" → brainstorming first, then implementation skills. +"Fix this bug" → debugging first, then domain-specific skills. + +## Skill Types + +**Rigid** (TDD, debugging): Follow exactly. Don't adapt away discipline. + +**Flexible** (patterns): Adapt principles to context. + +The skill itself tells you which. + +## User Instructions + +Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows. diff --git a/.trae/skills/using-superpowers/references/codex-tools.md b/.trae/skills/using-superpowers/references/codex-tools.md new file mode 100644 index 000000000..539b2b1c7 --- /dev/null +++ b/.trae/skills/using-superpowers/references/codex-tools.md @@ -0,0 +1,100 @@ +# Codex Tool Mapping + +Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: + +| Skill references | Codex equivalent | +|-----------------|------------------| +| `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) | +| Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls | +| Task returns result | `wait` | +| Task completes automatically | `close_agent` to free slot | +| `TodoWrite` (task tracking) | `update_plan` | +| `Skill` tool (invoke a skill) | Skills load natively — just follow the instructions | +| `Read`, `Write`, `Edit` (files) | Use your native file tools | +| `Bash` (run commands) | Use your native shell tools | + +## Subagent dispatch requires multi-agent support + +Add to your Codex config (`~/.codex/config.toml`): + +```toml +[features] +multi_agent = true +``` + +This enables `spawn_agent`, `wait`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`. + +## Named agent dispatch + +Claude Code skills reference named agent types like `superpowers:code-reviewer`. +Codex does not have a named agent registry — `spawn_agent` creates generic agents +from built-in roles (`default`, `explorer`, `worker`). + +When a skill says to dispatch a named agent type: + +1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's + local prompt template like `code-quality-reviewer-prompt.md`) +2. Read the prompt content +3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.) +4. Spawn a `worker` agent with the filled content as the `message` + +| Skill instruction | Codex equivalent | +|-------------------|------------------| +| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content | +| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt | + +### Message framing + +The `message` parameter is user-level input, not a system prompt. Structure it +for maximum instruction adherence: + +``` +Your task is to perform the following. Follow the instructions below exactly. + + +[filled prompt content from the agent's .md file] + + +Execute this now. Output ONLY the structured response following the format +specified in the instructions above. +``` + +- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...") +- Wrap instructions in XML tags — the model treats tagged blocks as authoritative +- End with an explicit execution directive to prevent summarization of the instructions + +### When this workaround can be removed + +This approach compensates for Codex's plugin system not yet supporting an `agents` +field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the +plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and +skills can dispatch named agent types directly. + +## Environment Detection + +Skills that create worktrees or finish branches should detect their +environment with read-only git commands before proceeding: + +```bash +GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P) +GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P) +BRANCH=$(git branch --show-current) +``` + +- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation) +- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox) + +See `using-git-worktrees` Step 0 and `finishing-a-development-branch` +Step 1 for how each skill uses these signals. + +## Codex App Finishing + +When the sandbox blocks branch/push operations (detached HEAD in an +externally managed worktree), the agent commits all work and informs +the user to use the App's native controls: + +- **"Create branch"** — names the branch, then commit/push/PR via App UI +- **"Hand off to local"** — transfers work to the user's local checkout + +The agent can still run tests, stage files, and output suggested branch +names, commit messages, and PR descriptions for the user to copy. diff --git a/.trae/skills/using-superpowers/references/gemini-tools.md b/.trae/skills/using-superpowers/references/gemini-tools.md new file mode 100644 index 000000000..f8698033b --- /dev/null +++ b/.trae/skills/using-superpowers/references/gemini-tools.md @@ -0,0 +1,33 @@ +# Gemini CLI Tool Mapping + +Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: + +| Skill references | Gemini CLI equivalent | +|-----------------|----------------------| +| `Read` (file reading) | `read_file` | +| `Write` (file creation) | `write_file` | +| `Edit` (file editing) | `replace` | +| `Bash` (run commands) | `run_shell_command` | +| `Grep` (search file content) | `grep_search` | +| `Glob` (search files by name) | `glob` | +| `TodoWrite` (task tracking) | `write_todos` | +| `Skill` tool (invoke a skill) | `activate_skill` | +| `WebSearch` | `google_web_search` | +| `WebFetch` | `web_fetch` | +| `Task` tool (dispatch subagent) | No equivalent — Gemini CLI does not support subagents | + +## No subagent support + +Gemini CLI has no equivalent to Claude Code's `Task` tool. Skills that rely on subagent dispatch (`subagent-driven-development`, `dispatching-parallel-agents`) will fall back to single-session execution via `executing-plans`. + +## Additional Gemini CLI tools + +These tools are available in Gemini CLI but have no Claude Code equivalent: + +| Tool | Purpose | +|------|---------| +| `list_directory` | List files and subdirectories | +| `save_memory` | Persist facts to GEMINI.md across sessions | +| `ask_user` | Request structured input from the user | +| `tracker_create_task` | Rich task management (create, update, list, visualize) | +| `enter_plan_mode` / `exit_plan_mode` | Switch to read-only research mode before making changes | diff --git a/packages/core/pipeline/src/plugin/api.ts b/packages/core/pipeline/src/plugin/api.ts index d4c837577..323ad82e2 100644 --- a/packages/core/pipeline/src/plugin/api.ts +++ b/packages/core/pipeline/src/plugin/api.ts @@ -4,7 +4,7 @@ import { FileStore } from "../core/file-store.js"; import { accessRegistry, IAccessService } from "../access/index.js"; import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService } from "../service/index.js"; import { CancelError, IContext, RunHistory, RunnableCollection } from "../core/index.js"; -import { HttpRequestConfig, ILogger, logger, utils } from "@certd/basic"; +import { HttpRequestConfig, ILogger, logger, optionsUtils, utils } from "@certd/basic"; import { HttpClient } from "@certd/basic"; import dayjs from "dayjs"; import { IPluginConfigService } from "../service/config.js"; @@ -315,6 +315,11 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin { getLastOutput(key: string) { return this.getLastStatus().status?.output?.[key]; } + + getMatchedDomains(domainList: string[], certDomains: string[]): string[] { + const { matched } = optionsUtils.groupByDomain(domainList, certDomains); + return matched; + } } export type OutputVO = { diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts index c6d71ba06..32376652e 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts @@ -589,11 +589,11 @@ export default { userValidityPeriodHelper: "有效期内用户可正常使用,失效后用户的流水线将被停用", enableUsernameRegistration: "开启用户名注册", enableEmailRegistration: "开启邮箱注册", - proFeature: "专业版功能", + proFeature: "Certd专业版功能", emailServerSetup: "设置邮箱服务器", enableSmsLoginRegister: "开启手机号登录、注册", defaultLoginType: "默认登录方式", - commFeature: "商业版功能", + commFeature: "Certd商业版功能", smsProvider: "短信提供商", aliyunSms: "阿里云短信", tencentSms: "腾讯云短信", diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts index 2877bce9f..b82a9e4cc 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts @@ -88,13 +88,13 @@ export default { activation_code_one_use: "激活码使用过一次之后,不可再次使用,如果要更换站点,请", bind_account: "绑定账号", transfer_vip: '然后"转移VIP"即可', - needVipTip: "此为专业版功能,请先开通专业版", + needVipTip: "此为Certd专业版功能,请先开通Certd专业版", manual_activation: "激活码手动激活", close: "关闭", have_activation_code: "已经有激活码了?", buy: "立即购买", - already_plus: "已经是专业版了,是否升级为商业版?注意:专业版时长将被覆盖", - already_comm: "已经是商业版了,不能降级为专业版", + already_plus: "已经是Certd专业版了,是否升级为商业版?注意:Certd专业版时长将被覆盖", + already_comm: "已经是Certd商业版了,不能降级为专业版", already_perpetual_plus: "您已经是永久专业版了,无法继续升级", confirm: "确认", not_effective: "VIP没有生效/时长未同步?", diff --git a/packages/ui/certd-client/src/views/sys/account/index.vue b/packages/ui/certd-client/src/views/sys/account/index.vue index 81c08a199..18a1f9685 100644 --- a/packages/ui/certd-client/src/views/sys/account/index.vue +++ b/packages/ui/certd-client/src/views/sys/account/index.vue @@ -80,7 +80,7 @@ onMounted(() => { await settingStore.doBindUrl(); notification.success({ message: "更新成功", - description: "专业版/商业版已激活", + description: "Certd专业版/商业版已激活", }); }); }); diff --git a/packages/ui/certd-server/src/controller/user/mine/setting-two-factor-controller.ts b/packages/ui/certd-server/src/controller/user/mine/setting-two-factor-controller.ts index 9f0e0bef0..8b84483f1 100644 --- a/packages/ui/certd-server/src/controller/user/mine/setting-two-factor-controller.ts +++ b/packages/ui/certd-server/src/controller/user/mine/setting-two-factor-controller.ts @@ -31,7 +31,7 @@ export class UserTwoFactorSettingController extends BaseController { @Post("/save", { description: Constants.per.authOnly, summary: "保存双因子认证设置" }) async save(@Body(ALL) bean: any) { if (!isPlus()) { - throw new Error('本功能需要开通专业版') + throw new Error('本功能需要开通Certd专业版') } const userId = this.getUserId(); const setting = new UserTwoFactorSetting(); @@ -57,7 +57,7 @@ export class UserTwoFactorSettingController extends BaseController { @Post("/authenticator/save", { description: Constants.per.authOnly, summary: "保存验证器设置" }) async authenticatorSave(@Body(ALL) bean: any) { if (!isPlus()) { - throw new Error('本功能需要开通专业版') + throw new Error('本功能需要开通Certd专业版') } const userId = this.getUserId(); await this.twoFactorService.saveAuthenticator({ diff --git a/packages/ui/certd-server/src/controller/user/mine/user-settings-controller.ts b/packages/ui/certd-server/src/controller/user/mine/user-settings-controller.ts index 755409b3d..cd4cf11c9 100644 --- a/packages/ui/certd-server/src/controller/user/mine/user-settings-controller.ts +++ b/packages/ui/certd-server/src/controller/user/mine/user-settings-controller.ts @@ -81,7 +81,7 @@ export class UserSettingsController extends CrudController @Post("/grant/save", { description: Constants.per.authOnly, summary: "保存授权设置" }) async grantSettingsSave(@Body(ALL) bean: UserGrantSetting) { if (!isPlus()) { - throw new Error('本功能需要开通专业版') + throw new Error('本功能需要开通Certd专业版') } const userId = this.getUserId(); const setting = new UserGrantSetting(); diff --git a/packages/ui/certd-server/src/modules/login/service/login-service.ts b/packages/ui/certd-server/src/modules/login/service/login-service.ts index c46472335..30b85c9a8 100644 --- a/packages/ui/certd-server/src/modules/login/service/login-service.ts +++ b/packages/ui/certd-server/src/modules/login/service/login-service.ts @@ -180,7 +180,7 @@ export class LoginService { async loginByTwoFactor(req: { loginId: string; verifyCode: string }) { //检查是否开启多重认证 if (!isPlus()) { - throw new Error('本功能需要开通专业版') + throw new Error('本功能需要开通Certd专业版') } const userId = cache.get(`login_2fa_code:${req.loginId}`) if (!userId) { diff --git a/packages/ui/certd-server/src/modules/pipeline/service/notification-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/notification-service.ts index dd7311be5..aaf1cc51e 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/notification-service.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/notification-service.ts @@ -83,7 +83,7 @@ export class NotificationService extends BaseService { const define = this.getDefineByType(type) //@ts-ignore if (define.needPlus && !isPlus()) { - throw new NeedVIPException("此通知类型为专业版功能,请升级到专业版或以上级别"); + throw new NeedVIPException("此通知类型为Certd专业版功能,请升级到专业版或以上级别"); } } diff --git a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts index 06df80d7b..8233fb9d9 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts @@ -941,7 +941,7 @@ export class PipelineService extends BaseService { async batchDelete(ids: number[], userId?: number, projectId?: number) { if (!isPlus()) { - throw new NeedVIPException("此功能需要升级专业版"); + throw new NeedVIPException("此功能需要升级Certd专业版"); } for (const id of ids) { if (userId && userId > 0) { @@ -956,7 +956,7 @@ export class PipelineService extends BaseService { async batchUpdateGroup(ids: number[], groupId: number, userId: any, projectId?: number) { if (!isPlus()) { - throw new NeedVIPException("此功能需要升级专业版"); + throw new NeedVIPException("此功能需要升级Certd专业版"); } const query: any = {} if (userId && userId > 0) { @@ -982,7 +982,7 @@ export class PipelineService extends BaseService { */ async batchTransfer(ids: number[], projectId: number) { if (!isPlus()) { - throw new NeedVIPException("此功能需要升级专业版"); + throw new NeedVIPException("此功能需要升级Certd专业版"); } if (!isEnterprise()) { throw new Error("当前为非企业模式,不允许转移到其他项目"); @@ -1075,7 +1075,7 @@ export class PipelineService extends BaseService { async batchUpdateTrigger(ids: number[], trigger: any, userId: any, projectId?: number) { if (!isPlus()) { - throw new NeedVIPException("此功能需要升级专业版"); + throw new NeedVIPException("此功能需要升级Certd专业版"); } //允许管理员修改,userId=null const query: any = {} @@ -1128,7 +1128,7 @@ export class PipelineService extends BaseService { async batchUpdateNotifications(ids: number[], notification: Notification, userId: any, projectId?: number) { if (!isPlus()) { - throw new NeedVIPException("此功能需要升级专业版"); + throw new NeedVIPException("此功能需要升级Certd专业版"); } //允许管理员修改,userId=null const query: any = {} @@ -1167,7 +1167,7 @@ export class PipelineService extends BaseService { async batchRerun(ids: number[], force: boolean, userId: any, projectId?: number) { if (!isPlus()) { - throw new NeedVIPException("此功能需要升级专业版"); + throw new NeedVIPException("此功能需要升级Certd专业版"); } //允许userId为空,为空则为管理员触发 if (ids.length === 0) { diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts index 632e3f660..b1732cfd3 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts @@ -1,4 +1,4 @@ -import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; +import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; import dayjs from 'dayjs'; import { createCertDomainGetterInputDefine, @@ -55,6 +55,19 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { }) certName!: string; + @TaskInput({ + title: '域名匹配模式', + helper: '选择域名匹配方式', + component: { + name: 'select', + options: [ + { label: '手动选择', value: 'manual' }, + { label: '根据证书匹配', value: 'auto' }, + ], + }, + default: 'manual', + }) + domainMatchMode!: 'manual' | 'auto'; @TaskInput( createRemoteSelectInputDefine({ @@ -63,6 +76,13 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { action: DeployCertToAliyunDCDN.prototype.onGetDomainList.name, watches: ['certDomains', 'accessId'], required: true, + mergeScript: ` + return { + show: ctx.compute(({form})=>{ + return domainMatchMode === "manual" + }) + } + `, }) ) domainName!: string | string[]; @@ -71,15 +91,30 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { async onInstance() { } async execute(): Promise { this.logger.info('开始部署证书到阿里云DCDN'); - if (!this.domainName) { - throw new Error('您还未选择DCDN域名'); - } const access = (await this.getAccess(this.accessId)) as AliyunAccess; const client = await this.getClient(access); - if (typeof this.domainName === 'string') { - this.domainName = [this.domainName]; + + let domains: string[] = []; + + if (this.domainMatchMode === 'auto') { + this.logger.info('使用根据证书匹配模式'); + if (!this.certDomains || this.certDomains.length === 0) { + throw new Error('未获取到证书域名信息'); + } + domains = await this.getAutoMatchedDomains(this.certDomains); + if (domains.length === 0) { + this.logger.warn('未找到匹配的DCDN域名'); + return; + } + this.logger.info(`找到 ${domains.length} 个匹配的DCDN域名`); + } else { + if (!this.domainName) { + throw new Error('您还未选择DCDN域名'); + } + domains = typeof this.domainName === 'string' ? [this.domainName] : this.domainName; } - for (const domainName of this.domainName) { + + for (const domainName of domains) { this.logger.info(`[${domainName}]开始部署`) const params = await this.buildParams(domainName); await this.doRequest(client, params); @@ -152,7 +187,36 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { } - async onGetDomainList(data: any) { + async getAutoMatchedDomains(certDomains: string[]): Promise { + const matchedDomains: string[] = []; + let pageNumber = 1; + + while (true) { + const result = await this.onGetDomainList({ pageNo: pageNumber }); + const pageData = result.list; + this.logger.info(`获取到 ${pageData.length} 个DCDN域名`); + + if (!pageData || pageData.length === 0) { + break; + } + + const matched = this.getMatchedDomains(pageData, certDomains); + matchedDomains.push(...matched); + + const totalCount = result.total || 0; + if (pageNumber * 500 >= totalCount) { + break; + } + + pageNumber++; + } + + return matchedDomains; + } + + + + async onGetDomainList(data: PageSearch) { if (!this.accessId) { throw new Error('请选择Access授权'); } @@ -161,7 +225,7 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { const client = await this.getClient(access); const params = { - // 'DomainName': 'aaa', + PageNumber: data.pageNo || 1, PageSize: 500, }; @@ -172,10 +236,9 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { const res = await client.request('DescribeDcdnUserDomains', params, requestOption); this.checkRet(res); - const pageData = res?.Domains?.PageData; - if (!pageData || pageData.length === 0) { - throw new Error('找不到CDN域名,您可以手动输入'); - } + const pageData = res?.Domains?.PageData || []; + const total = res?.Domains?.TotalCount || 0; + const options = pageData.map((item: any) => { return { value: item.DomainName, @@ -183,7 +246,11 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { domain: item.DomainName, }; }); - return optionsUtils.buildGroupOptions(options, this.certDomains); + + return { + list: optionsUtils.buildGroupOptions(options, this.certDomains), + total: total, + }; } } new DeployCertToAliyunDCDN();