fix(skill): 修正算力配置治理流程 (#100)

## 中文

- 将算力配置流程改为 `ManageCompute` 与 `ComputeCredential set`
- 明确密钥只允许写入且工具结果保持脱敏
- 补全 GUI `mode=control` 回退、旧客户端条件化验证和升级提示
- 禁止引导 Agent 绕过 renderer Origin/token 边界

## English

- Route compute configuration through `ManageCompute` and
`ComputeCredential set`
- Keep credentials write-only and redacted from tool results
- Complete the GUI `mode=control` fallback, legacy-client validation,
and upgrade guidance
- Stop directing Agents around renderer Origin/token protections

## Validation

- Translation freshness check
- Skill i18n validation
- `git diff --check`
This commit is contained in:
2026-08-29 14:26:24 -04:00
committed by GitHub
parent a625b5ec84
commit 54716ea43a
3 changed files with 127 additions and 256 deletions

View File

@@ -1,5 +1,12 @@
# Changelog # Changelog
## [1.1.0] - 2026-08-30
- 改用 `ManageCompute``ComputeCredential(action='set')` 完成受治理的 Provider 启停、模型同步和密钥只写
- 补充 `ControlDesireCoreGui` 的 control 模式回退及旧客户端升级路径
- 禁止 Agent 绕过 renderer Origin/token 边界调用本机算力管理 HTTP 端点
- 明确敏感输入在审批、事件、回执和会话持久化中的统一脱敏契约
## [1.0.3] - 2026-07-29 ## [1.0.3] - 2026-07-29
- 市场图标统一为纯白,确保各市场消费端视觉一致 - 市场图标统一为纯白,确保各市场消费端视觉一致

View File

@@ -1,12 +1,12 @@
--- ---
name: configuring-compute name: configuring-compute
description: >- description: >-
Configure compute providers (model API endpoints and API keys) for the user Configure DesireCore compute providers through governed tools: inspect,
via the Agent Service HTTP API: create/update providers, write API keys enable or disable providers, sync models, and set API keys without reading
(write-only), verify them, and reload the configuration. Use when the user them back. Use the dedicated DesireCore GUI tool for fields not yet covered
asks to add or configure a model provider, set an API key, or fix compute by ManageCompute. Never call renderer-only /api/compute management endpoints
configuration. 用户要求配置算力、添加模型供应商或设置 API Key 时使用。 from Bash or HttpRequest. 用户要求配置算力、同步模型或设置 API Key 时使用。
version: 1.0.3 version: 1.1.0
type: meta type: meta
risk_level: medium risk_level: medium
status: enabled status: enabled
@@ -18,7 +18,7 @@ tags:
- meta - meta
metadata: metadata:
author: desirecore author: desirecore
updated_at: '2026-07-29' updated_at: '2026-08-30'
i18n: i18n:
default_locale: en-US default_locale: en-US
source_locale: zh-CN source_locale: zh-CN
@@ -29,22 +29,23 @@ metadata:
name: 配置算力 name: 配置算力
short_desc: 帮用户配置模型供应商与 API Key密钥只写不读 short_desc: 帮用户配置模型供应商与 API Key密钥只写不读
description: >- description: >-
通过 Agent Service HTTP API 为用户配置算力供应商(模型 API 端点与 API Key 通过受治理工具配置 DesireCore 算力:查看和启停 provider、同步模型、只写 API Key
创建/更新 provider、写入 API Key只写、验证密钥、刷新配置。 ManageCompute 尚未覆盖的字段使用专用 DesireCore GUI 工具。禁止从 Bash 或 HttpRequest
Use when 用户要求添加或配置模型供应商、设置 API Key、修复算力配置 调用仅供可信渲染器使用的 /api/compute 管理端点
body: ./SKILL.zh-CN.md body: ./SKILL.zh-CN.md
translated_by: human translated_by: human
source_hash: sha256:b37d5baa849e88e9
en-US: en-US:
name: Configure Compute name: Configure Compute
short_desc: Configure model providers and API keys for the user (keys are write-only) short_desc: Configure model providers and API keys for the user (keys are write-only)
description: >- description: >-
Configure compute providers (model API endpoints and API keys) for the user Configure DesireCore compute through governed tools: inspect and enable providers,
via the Agent Service HTTP API: create/update providers, write API keys sync models, and write API keys without reading them back. Use the dedicated
(write-only), verify them, and reload the configuration. Use when the user DesireCore GUI tool for fields not covered by ManageCompute; never call renderer-only
asks to add or configure a model provider, set an API key, or fix compute configuration. /api/compute management endpoints from Bash or HttpRequest.
body: ./SKILL.md body: ./SKILL.md
translated_by: human translated_by: human
source_hash: sha256:15e9247a2c27edf7 source_hash: sha256:b37d5baa849e88e9
market: market:
icon: >- icon: >-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M9 2v2M15 2v2M9 20v2M15 20v2M2 9h2M2 15h2M20 9h2M20 15h2"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M9 2v2M15 2v2M9 20v2M15 20v2M2 9h2M2 15h2M20 9h2M20 15h2"/></svg>
@@ -57,140 +58,76 @@ market:
# Configure Compute # Configure Compute
Help the user configure compute providers — model API endpoints and API keys — Configure DesireCore compute through governed tools. Do not call the local
through the Agent Service HTTP API. `/api/compute/*` management endpoints from Bash, HttpRequest, or scripts: those
endpoints intentionally require a trusted renderer Origin and instance token.
## Security model (read this first, non-negotiable) ## Security contract
- **API keys are write-only.** You can create and overwrite keys; you can NEVER - API keys are write-only for agents. Never read `secrets.json`, request
read an existing key back. There is no API that returns key plaintext to you, `ComputeCredential(action='get', raw=true)`, or repeat a key in tool results or chat.
`GET /api/compute/config` returns masked values only, and the runtime blocks - Use `ComputeCredential(action='set')` to create or replace the key of an
file reads of `secrets.json` (Read/Grep tools and shell commands alike). existing user-managed provider. If the provider has no credential reference,
- **Do not try to work around this.** Never attempt to read the tool creates and attaches one without returning the plaintext. This path
`~/.desirecore/config/secrets.json`, never echo a key the user gave you back is approval-gated and audited; approval cards, tool events, receipts, and
into chat more than necessary, never store keys anywhere except via session history redact the sensitive value. System-managed credentials are
`POST /api/compute/secrets`. not writable.
- If the user asks "what is my current key", answer: keys cannot be read back; - Pass a value to `ComputeCredential(action='set')` only when the user already supplied the
they can only be replaced. Point them to the provider settings UI, which has replacement key in the current request. If the agent must never handle the
a user-only reveal control. plaintext, use the GUI to focus the password field, let the user type into it
directly, then continue the save flow. Never ask for the key in ordinary chat
or read the masked field back into the model.
- `credentialMode=none` means the provider needs no key. Ollama is treated as
`none` even when an older config does not declare the field.
- If the user asks for the current key, explain that the agent can replace it but
cannot read it back. The human-only UI reveal flow remains separate.
## How to call the API ## Workflow for an existing provider
- Prefer the `Bash` tool with `curl`. The API base URL is already injected into First confirm `ManageCompute` is available through the current tool catalog. If
the "Local API" section of the system prompt; reference it directly. it is absent (for example on an older installed client), use the governed GUI
- On Windows without Git Bash, use the `HttpRequest` tool with the same URLs. workflow below for the whole task; do not fall back to local HTTP.
## Workflow 1. Call `ManageCompute(action='list')`. Record the exact provider ID,
enabled state, credential mode, status, and model count.
2. If credential mode is `required` and the user supplied a new key in the
current request, call `ComputeCredential(action='set', providerId=..., value=...)`.
Do not echo the value. If the agent must not handle plaintext,
use the human-entry GUI flow below. For `none`, skip this step.
3. Call `ManageCompute(action='set_enabled', providerId=..., enabled=true)`.
4. Call `ManageCompute(action='sync_models', providerId=...)`. For Ollama
this discovers locally installed models; for supported cloud providers it
merges the built-in model list.
5. Call `InspectModels` to verify the intended model is selectable. When the
user asked for a real test, run one short fixed-model conversation and verify
the run receipt names the requested provider/model.
### 1. Inspect current configuration Mutating ManageCompute and ComputeCredential operations use the platform's
approval policy. Do not add a second confirmation in prose unless information
is missing or the user requested a destructive replacement.
```bash ## Fields not yet covered by ManageCompute
curl -s $BASE/api/compute/config
```
Response contains `providers[]` where `apiKey` is a **masked display value** Creating a new custom provider, changing base URL/API format, deleting a
(first 4 chars + bullets) and `hasApiKey: boolean` tells you whether a key is provider, and interactive key verification currently remain GUI operations.
configured. Use this to decide between creating a new provider and updating an Use `ControlDesireCoreGui`, not a generic browser/CUA tool:
existing one. Never treat the masked `apiKey` as a real key.
### 2. Create a provider (if needed) 1. `list_instances`, then `begin(instance=<id>, mode=control, reason=...)` for
the intended DesireCore instance. The default `observe` mode is read-only and
cannot modify compute settings.
2. Use the governed CDP methods to open Resources → Compute and make the change.
3. Finish with `end`. If `ManageCompute` exists, call `ManageCompute(action='list')`; on
an older client, verify the saved state in the GUI instead. Call
`InspectModels` when available to confirm model selection.
```bash If the installed version includes `ControlDesireCoreGui` but reports that GUI
curl -s -X POST $BASE/api/compute/providers \ control is disabled, the owner must set
-H 'Content-Type: application/json' \ `config/security.json#desktopGuiControl.enabled=true` and restart that instance.
-d '{ If the tool is absent from the catalog entirely, the client is too old and must
"provider": "deepseek", be upgraded; changing the switch cannot add a missing tool. Do not bypass the
"label": "DeepSeek", renderer HTTP boundary.
"baseUrl": "https://api.deepseek.com",
"services": ["chat"],
"apiFormat": "openai-completions",
"priceCurrency": "CNY"
}'
```
Required fields: `provider`, `label`, `baseUrl`, `services`. The response ## Completion report
returns the created provider with its generated `id` — keep it for later steps.
Known provider presets and model lists are available via
`GET /api/compute/pi-providers` and `GET /api/compute/pi-models/:provider`.
### 3. Set the API key (write-only) Report the provider ID, enabled state, synchronized model count, and model test
result. Never include the key, its encrypted storage, or a plaintext fingerprint.
Ask the user for the key. Reuse the provider's existing `apiKeyRef` if set;
otherwise generate one like `key-<random>` and attach it to the provider:
```bash
# Write the secret (write-only endpoint; there is no GET counterpart)
curl -s -X POST $BASE/api/compute/secrets \
-H 'Content-Type: application/json' \
-d '{"ref": "key-abc123", "value": "<API_KEY_FROM_USER>"}'
# Attach the ref to the provider if it was not set yet
curl -s -X PUT $BASE/api/compute/providers/<id> \
-H 'Content-Type: application/json' \
-d '{"apiKeyRef": "key-abc123"}'
```
### 4. Verify the key
```bash
curl -s -X POST $BASE/api/compute/verify-key \
-H 'Content-Type: application/json' \
-d '{"provider": "deepseek", "baseUrl": "https://api.deepseek.com", "apiKeyRef": "key-abc123", "apiFormat": "openai-completions"}'
```
Returns `{ valid, latencyMs, errorMessage?, suggestedBaseUrl? }`. If
`suggestedBaseUrl` is present, offer to update the provider's `baseUrl`.
### 5. Enable and populate models
```bash
curl -s -X PUT $BASE/api/compute/providers/<id> \
-H 'Content-Type: application/json' -d '{"enabled": true}'
# Optional: sync the model list from the built-in registry
curl -s -X POST $BASE/api/compute/sync-models/<id>
```
### 6. Reload so the UI reflects the change
Always finish with:
```bash
curl -s -X POST $BASE/api/compute/reload
```
This re-validates the configuration and broadcasts a refresh event to the
client UI. Report the returned `providerCount` / `enabledProviderCount` /
`modelCount` to the user as confirmation.
## Endpoint reference
| Method | Path | Purpose |
| ------ | ---- | ------- |
| GET | `/api/compute/config` | Full config; `apiKey` masked + `hasApiKey` |
| POST | `/api/compute/providers` | Create provider |
| PUT | `/api/compute/providers/:id` | Patch provider (label/baseUrl/enabled/apiFormat/apiKeyRef/...) |
| DELETE | `/api/compute/providers/:id` | Remove provider |
| POST | `/api/compute/secrets` | Write key `{ref, value}`**write-only** |
| DELETE | `/api/compute/secrets/:ref` | Delete key |
| POST | `/api/compute/verify-key` | Probe a key end-to-end |
| POST | `/api/compute/sync-models/:id` | Sync model list from registry |
| POST | `/api/compute/reload` | Re-validate config + broadcast UI refresh |
| GET | `/api/compute/pi-providers` | Known provider presets |
| GET | `/api/compute/pi-models/:provider` | Known models for a preset |
## Error handling
| Symptom | Cause | Action |
| ------- | ----- | ------ |
| 400 on create | Missing required field / unknown field | Fix the request body (schema is strict, `additionalProperties: false`) |
| 404 on PUT | Wrong provider id | Re-list via GET config |
| `valid: false` on verify | Wrong key / wrong baseUrl / wrong apiFormat | Show `errorMessage` to the user; try `suggestedBaseUrl` if present |
| 403 anywhere | You tried a credential-gated endpoint (key reveal) | Stop — that endpoint is for the human user's UI only |
## Confirmation etiquette
- Before overwriting an existing key (`hasApiKey: true`), confirm with the user.
- Before deleting a provider or key, confirm with the user.
- After finishing, summarize what changed (provider, enabled state, model count)
without repeating the key value.

View File

@@ -1,130 +1,57 @@
# 配置算力 # 配置算力
通过 Agent Service HTTP API 帮用户配置算力供应商——模型 API 端点与 API Key。 通过受治理工具配置 DesireCore 算力。禁止从 Bash、HttpRequest 或脚本调用本机
`/api/compute/*` 管理端点;这些端点有意要求可信渲染器 Origin 和实例令牌。
## 安全模型(先读这里,不可协商) ## 安全契约
- **API Key 只写不读。** 你可以创建和覆盖密钥,但**永远无法**读回已有密钥。 - 对 Agent 而言 API Key 只写不读。不得读取 `secrets.json`、请求
没有任何 API 会向你返回密钥明文:`GET /api/compute/config` 只返回掩码值, `ComputeCredential(action='get', raw=true)`,也不得在工具结果或聊天中复述密钥。
运行时同时拦截对 `secrets.json` 的文件读取Read/Grep 工具与 shell 命令一律拒绝)。 - 使用 `ComputeCredential(action='set')` 为已有的用户自管 Provider 创建或替换密钥。若该
- **不要尝试绕过。** 不要读取 `~/.desirecore/config/secrets.json`,不要在对话中 Provider 尚无凭据引用,工具会创建并挂载专属引用,但不会把明文返回给 Agent。此路径
超出必要地回显用户提供的密钥,除 `POST /api/compute/secrets` 外不要在任何地方存放密钥。 受审批、留审计;敏感值在审批卡、工具事件、回执和会话历史中统一脱敏,系统托管凭据
- 如果用户问「我现在的 key 是什么」,回答:密钥无法读回,只能替换; 不可由该操作覆盖。
可引导用户到供应商设置页,那里有仅限用户本人的明文查看控件。 - 只有当用户已经在当前请求中主动提供了替换密钥时,才把该值传给
`ComputeCredential(action='set')`;若要求
Agent 从未接触明文,则用 GUI 聚焦密码输入框,让用户直接输入后再继续保存。不得让用户
把密钥发到普通聊天,也不得把掩码字段读回模型。
- `credentialMode=none` 表示 Provider 无需密钥。旧配置未声明时Ollama 也按 `none` 处理。
- 用户询问当前 Key 时,说明 Agent 只能替换、不能读回;人类 UI 的明文查看流程保持独立。
## 如何调用 API ## 已有 Provider 的工作流程
- 优先使用 `Bash` 工具 + `curl`。API 基础地址已注入到 system prompt 的 先通过当前工具目录确认 `ManageCompute` 可用。若工具不存在(例如安装版客户端较旧),
「本机 API」小节直接引用即可 整项任务改走下方受治理 GUI 流程;不得回退到本地 HTTP
- Windows 无 Git Bash 时,用 `HttpRequest` 工具调用相同 URL。
## 工作流程 1. 调用 `ManageCompute(action='list')`,记录准确的 Provider ID、启用状态、凭据模式、状态和模型数。
2. 若凭据模式为 `required` 且用户已在当前请求中提供新密钥,调用
`ComputeCredential(action='set', providerId=..., value=...)`;不得回显。若用户要求 Agent 不接触明文,改走
下方 GUI 人工直填密码框。凭据模式为 `none` 时跳过。
3. 调用 `ManageCompute(action='set_enabled', providerId=..., enabled=true)`
4. 调用 `ManageCompute(action='sync_models', providerId=...)`。Ollama 会发现本机已安装模型;
支持的云 Provider 会合并内置模型清单。
5. 调用 `InspectModels` 确认目标模型可选。若用户要求真实测试,发起一次简短固定模型对话,
并核对运行回执中的 Provider/模型就是目标项。
### 1. 查看当前配置 ManageCompute 和 ComputeCredential 的变更操作会走平台审批策略。除非信息缺失或用户要求
破坏性替换,不要在文字里再加一层重复确认。
```bash ## ManageCompute 尚未覆盖的字段
curl -s $BASE/api/compute/config
```
响应中的 `providers[]``apiKey` 是**掩码展示值**(前 4 位 + 圆点), 新建自定义 Provider、修改 Base URL/API 格式、删除 Provider、交互式验证密钥目前仍走 GUI。
`hasApiKey: boolean` 表示是否已配置密钥。据此决定是新建 provider 还是更新现有的。 使用 `ControlDesireCoreGui`,不要使用通用浏览器/CUA 工具:
掩码 `apiKey` 不是真实密钥,不要当作密钥使用。
### 2. 创建 provider如需要 1. `list_instances`,再对目标 DesireCore 实例执行
`begin(instance=<id>, mode=control, reason=...)`。默认 `observe` 只能读取界面,不能修改算力。
2. 用受治理 CDP 方法进入“资源 → 算力”完成修改。
3. 执行 `end`。若当前版本有 `ManageCompute`,再调用 `ManageCompute(action='list')`;旧客户端则在 GUI
内复核保存状态。`InspectModels` 可用时再用它确认模型可选。
```bash 若当前版本包含 `ControlDesireCoreGui`,但工具报告 GUI 控制已关闭,实例所有者需设置
curl -s -X POST $BASE/api/compute/providers \ `config/security.json#desktopGuiControl.enabled=true` 并重启该实例。若工具目录里完全没有该工具,
-H 'Content-Type: application/json' \ 说明客户端版本过旧,必须升级;修改开关不能补出旧版本不存在的工具。不得绕过渲染器 HTTP 边界。
-d '{
"provider": "deepseek",
"label": "DeepSeek",
"baseUrl": "https://api.deepseek.com",
"services": ["chat"],
"apiFormat": "openai-completions",
"priceCurrency": "CNY"
}'
```
必填字段:`provider``label``baseUrl``services`。响应返回创建的 provider ## 完成交付
及其生成的 `id`——后续步骤要用。已知供应商预设与模型列表可通过
`GET /api/compute/pi-providers``GET /api/compute/pi-models/:provider` 获取。
### 3. 设置 API Key只写 报告 Provider ID、启用状态、同步后的模型数量和真实模型测试结果。不要包含密钥、加密存储内容
或明文指纹。
向用户索取密钥。provider 已有 `apiKeyRef` 则复用;否则生成一个形如
`key-<随机串>` 的引用名并挂到 provider 上:
```bash
# 写入密钥(只写端点,没有对应的 GET
curl -s -X POST $BASE/api/compute/secrets \
-H 'Content-Type: application/json' \
-d '{"ref": "key-abc123", "value": "<用户提供的API_KEY>"}'
# 如果 provider 还没有 apiKeyRef补挂上去
curl -s -X PUT $BASE/api/compute/providers/<id> \
-H 'Content-Type: application/json' \
-d '{"apiKeyRef": "key-abc123"}'
```
### 4. 验证密钥
```bash
curl -s -X POST $BASE/api/compute/verify-key \
-H 'Content-Type: application/json' \
-d '{"provider": "deepseek", "baseUrl": "https://api.deepseek.com", "apiKeyRef": "key-abc123", "apiFormat": "openai-completions"}'
```
返回 `{ valid, latencyMs, errorMessage?, suggestedBaseUrl? }`。若返回
`suggestedBaseUrl`,主动提议更新 provider 的 `baseUrl`
### 5. 启用并填充模型
```bash
curl -s -X PUT $BASE/api/compute/providers/<id> \
-H 'Content-Type: application/json' -d '{"enabled": true}'
# 可选:从内置注册表同步模型列表
curl -s -X POST $BASE/api/compute/sync-models/<id>
```
### 6. 刷新配置让 UI 生效
收尾必做:
```bash
curl -s -X POST $BASE/api/compute/reload
```
该端点重新校验配置并向客户端 UI 广播刷新事件。把返回的 `providerCount` /
`enabledProviderCount` / `modelCount` 报告给用户作为完成确认。
## 端点速查
| 方法 | 路径 | 用途 |
| ---- | ---- | ---- |
| GET | `/api/compute/config` | 完整配置;`apiKey` 为掩码 + `hasApiKey` |
| POST | `/api/compute/providers` | 创建 provider |
| PUT | `/api/compute/providers/:id` | 局部更新label/baseUrl/enabled/apiFormat/apiKeyRef/... |
| DELETE | `/api/compute/providers/:id` | 删除 provider |
| POST | `/api/compute/secrets` | 写入密钥 `{ref, value}` —— **只写** |
| DELETE | `/api/compute/secrets/:ref` | 删除密钥 |
| POST | `/api/compute/verify-key` | 端到端验证密钥 |
| POST | `/api/compute/sync-models/:id` | 从注册表同步模型列表 |
| POST | `/api/compute/reload` | 重新校验配置 + 广播 UI 刷新 |
| GET | `/api/compute/pi-providers` | 已知供应商预设 |
| GET | `/api/compute/pi-models/:provider` | 预设供应商的已知模型 |
## 错误处理
| 现象 | 原因 | 处理 |
| ---- | ---- | ---- |
| 创建时 400 | 缺必填字段 / 携带未知字段 | 修正请求体schema 严格,`additionalProperties: false` |
| PUT 时 404 | provider id 错误 | 重新 GET config 拿列表 |
| 验证 `valid: false` | 密钥错 / baseUrl 错 / apiFormat 错 | 把 `errorMessage` 转告用户;有 `suggestedBaseUrl` 则尝试 |
| 任何端点 403 | 误触凭证门控端点(明文查看) | 立即停止——该端点仅供人类用户的 UI 使用 |
## 确认礼仪
- 覆盖已有密钥(`hasApiKey: true`)前,先向用户确认。
- 删除 provider 或密钥前,先向用户确认。
- 完成后向用户总结变更内容provider、启用状态、模型数量不要复述密钥值。