dsh-plugin-teambition
Đã xác minhdsh-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.
Cài đặt
dsh plugin add dsh-plugin-teambition Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.
Mã nguồn
Phát hành lên npm mà không có repository công khai. Hãy kiểm tra nội dung package trước khi cài.
Thẻ
Tác giả
Readme
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