dsh-plugin-teambition
已验证dsh-plugin-teambition · v1.1.3 · MIT
Connect Teambition Open Platform: projects, task types, tasks (by type / parent task), task status transition, requirements, bugs, documents, test plans/cases, worktime, stats.
安装
dsh plugin add dsh-plugin-teambition 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
发布到 npm 但没有公开仓库。安装前请检查包内容。
标签
作者
说明文档
dsh-plugin-teambition
A DeepSeek Harness Studio plugin that connects to the Teambition Open Platform and exposes model tools to query projects, task types, tasks (by type / parent task), workflow statuses, requirements, bugs, documents, test plans/cases, worktime and statistics — plus one write operation: updating a task's workflow status.
Implements the design in dsh-plugin-teambition-开发设计文档.md.
Tools
| Tool | Endpoint | Notes |
|---|---|---|
tb_set_config |
— | configure credentials + connectivity check |
tb_get_token |
POST /appToken |
verify appId/appSecret; never echoes the secret |
tb_list_projects |
GET /v3/project/query |
by id / name / uniqueIdPrefix |
tb_list_task_types |
GET /v3/project/{id}/scenariofieldconfig/search |
task type name ↔ sfcId |
tb_list_tasks |
GET /v3/project/{id}/task/query |
by task type + project + parent task |
tb_get_task_detail |
GET /v3/task/query |
by taskId / shortIds / parentTaskId |
tb_list_task_statuses |
GET /v3/task/{taskId}/tfs |
statuses of the task's workflow |
tb_update_task_status |
PUT /v3/task/{taskId}/taskflowstatus |
the only write tool — safe by default |
tb_project_stats |
GET /v3/project/{id}/task/count |
任务计数 |
Changed in v1.1.3:
tb_list_tasksnow defaults toexecutor = config.userId. OmittingexecutorIdqueries only the configured user's tasks; passexecutorId: "all"(or"*") for the whole project, or another user id to filter by that member. The implicit filter is skipped when rawqis supplied, and is always echoed inwarnings+ the returnedtqlso a narrowed query is never silent.Removed in v1.1.2:
tb_list_documents,tb_list_testplans,tb_list_testcases,tb_worktime. Their endpoints exist but returncode 403— the app lacks the permission, and it is not being requested. Keeping dead-but-listed tools is worse than removing them.tb_list_testplansadditionally used a wrong path (/v3/project/{id}/testplan; the official one is/v3/project/{id}/testplan/query?testplanIds=…, wheretestplanIdsis required — there is no "list all test plans" endpoint).Removed in v1.1.1:
tb_list_storiesandtb_list_bugs. The endpoints they called (GET /v3/project/{id}/story/queryand/bug/query) do not exist — the API returnscode 421 "url not found". In Teambition, 需求 / 缺陷 are task types (sfcId), not standalone resources. Query them throughtb_list_taskswithsfcId(ortaskType): 需求 = the type whose tasks havestorygroupIdset; 缺陷 = the type whose workflow contains 修复中 / 已解决 / 重复提交.
tb-core:project.sfc:listis still not granted, sotb_list_task_typescurrently returnscode 403— passsfcIddirectly until it is. The endpoints they called (GET /v3/project/{id}/story/queryand/bug/query) do not exist — the API returnscode 421 "url not found". In Teambition, 需求 / 缺陷 are task types (sfcId), not standalone resources. Query them throughtb_list_taskswithsfcId(ortaskType): 需求 = the type whose tasks havestorygroupIdset; 缺陷 = the type whose workflow contains 修复中 / 已解决 / 重复提交.Four other tools (
tb_list_documents,tb_list_testplans,tb_list_testcases,tb_worktime) returncode 403in this tenant: their endpoints do exist, but the app lacks the permission. They will work once granted.
Auth
Enterprise app mode: appId + appSecret + orgId. The plugin signs a local HS256 JWT ({ _appId, iat, exp }) as the appAccessToken and sends Authorization: Bearer <jwt> plus X-Tenant-Type: organization and X-Tenant-Id: <orgId>.
iat is aligned to the hour boundary with a 660s margin (exp = iat + 3960), so the token is identical within a natural hour and is reused without a round-trip.
A raw token is also accepted as a fallback (used only when appId/appSecret are absent). appSecret is never echoed in tool output.
Task status update is safe by default
tb_update_task_status(taskId, statusName="已完成") → resolves + previews, NO write
tb_update_task_status(taskId, statusName="已完成", confirm=true) → performs the PUT
- Without
confirm: truethe tool only resolves the target status and returns a preview. NoPUTis issued. - Status names are resolved locally against
GET /v3/task/{taskId}/tfs. A name that is ambiguous or not found is refused — the plugin never guesses. - Write operations are never retried automatically, and
5xxnever triggers a replay. - One call affects exactly one task; there is no batch mode.
Configuration items
orgId, appId, appSecret, projectId, userId are stored in the teambition settings namespace and survive restart. baseUrl and curlPath are also configurable (private cloud / non-Windows hosts).
projectIdis the default project for project-scoped tools (still overridable per call).userIdis always sent asX-Operator-Id(visibility filtering and write attribution), and it is now the default executor filter:tb_list_taskswithexecutorIdomitted queries only the configured user's tasks (addressed as "my tasks"). PassexecutorId: "all"(or"*") to query the whole project, or another user id to filter by that member. The implicit filter never applies when rawqis supplied (raw TQL wins), and it is always echoed inwarnings+ the returnedtql, so a narrowed query is never silent.
teambition:
orgId: ""
appId: ""
appSecret: ""
projectId: ""
userId: ""
Verified against the live API
npm run probe runs a read-only check (tools/probe.mjs, never calls any write endpoint) and confirmed:
- TQL filter field for task type is
scenarioId(notsfcId) — filtered query returned 50 tasks, all matching the requestedsfcId. - TQL filter field for parent task is
parentId(notparentTaskId) — filtered query returned 3 direct children, all matching, including a known child. GET /v3/task/{taskId}/tfsreturns the workflow statuses.
Two environment facts worth knowing:
GET /v3/project/{id}/scenariofieldconfig/searchcan return HTTP 200 withcode: 403when the app lackstb-core:project.sfc:list. Success is therefore decided on the business envelope, not the HTTP status. Without that permission,tb_list_task_typesand thetaskTypename-resolution path intb_list_taskswill fail; grant the permission and republish the app.- A workflow may have more than one
kind: "end"status (e.g. 已完成 and 已取消), so "the status with kind=end" is not a safe way to pick a target. Pass an explicitstatusNameortaskflowstatusId.
Permissions required
| Permission | Used by |
|---|---|
tb-core:project:get |
tb_list_projects |
tb-core:project.sfc:list |
tb_list_task_types, tb_list_tasks (taskType resolution) |
tb-core:task:list |
tb_list_tasks |
tb-core:task:get |
tb_get_task_detail, tb_list_task_statuses |
tb-core:task:update |
tb_update_task_status (write) |
Permission changes require republishing the app and re-approval by the enterprise admin.
Development
npm test # 51 unit tests (TQL builder, HTTP encoding, envelope, status resolution, JWT)
npm run smoke # 25 offline integration checks (tool registration, generated curl argv, write safety, output rendering)
npm run probe # read-only verification against the live API (needs configured credentials)
Tool output rendering (learned the hard way)
defineTool requires output: { schema, render }, and DSH calls render(args, result) — the second parameter is what execute returned. A render that returns an empty array (or only reads the first parameter) silently discards every result: the tool runs, but the model sees nothing. This plugin therefore renders the result explicitly:
const renderJson = (args, result) => [{
type: 'text',
text: typeof result === 'string' ? result : JSON.stringify(result ?? null, null, 2),
}]
Related requirement: a tool's return value must be lossless JSON. An undefined property is rejected at runtime ("must be lossless JSON data"), so nullable fields use || null and optional keys are omitted rather than set to undefined. Both rules are covered by regression checks in npm run smoke.
Layout:
lib/constants.js field-name traps (scenarioId/sfcId, parentId/parentTaskId) + endpoint table
lib/tql.js TQL builder with quoting and an ORDER BY allow-list
lib/http.js URL building (encoding) + response envelope unwrapping
lib/task.js task field projection + status-name resolution (refuses ambiguity)
lib/jwt.js pure-JS HS256 appAccessToken signing (no node:crypto dependency)
lib/index.js Cordis plugin: settings, transport, tools
tools/probe.mjs dev-only read-only probe (not shipped)
Install
dsh plugin add dsh-plugin-teambition
Or reference the .tgz from your profile package.json as a file: dependency.
Notes
- Transport uses the harness
subprocessservice to runcurl(the dynamic Host sandbox has nofetch). DefaultC:\Windows\System32\curl.exe, overridable viacurlPath. - Success is decided on the business envelope
{ code, errorMessage, result }, not the HTTP status alone. - Task results are projected to a default field set to control context cost; use
fieldsfor a few whitelisted extras.
License
MIT