npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-plugin-teambition

v1.1.3

Published

Connect Teambition Open Platform: projects, task types, tasks (by type / parent task), task status transition, requirements, bugs, documents, test plans/cases, worktime, stats.

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_tasks now defaults to executor = config.userId. Omitting executorId queries only the configured user's tasks; pass executorId: "all" (or "*") for the whole project, or another user id to filter by that member. The implicit filter is skipped when raw q is supplied, and is always echoed in warnings + the returned tql so 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 return code 403 — the app lacks the permission, and it is not being requested. Keeping dead-but-listed tools is worse than removing them. tb_list_testplans additionally used a wrong path (/v3/project/{id}/testplan; the official one is /v3/project/{id}/testplan/query?testplanIds=…, where testplanIds is required — there is no "list all test plans" endpoint).

Removed in v1.1.1: tb_list_stories and tb_list_bugs. The endpoints they called (GET /v3/project/{id}/story/query and /bug/query) do not exist — the API returns code 421 "url not found". In Teambition, 需求 / 缺陷 are task types (sfcId), not standalone resources. Query them through tb_list_tasks with sfcId (or taskType): 需求 = the type whose tasks have storygroupId set; 缺陷 = the type whose workflow contains 修复中 / 已解决 / 重复提交.

tb-core:project.sfc:list is still not granted, so tb_list_task_types currently returns code 403 — pass sfcId directly until it is. The endpoints they called (GET /v3/project/{id}/story/query and /bug/query) do not exist — the API returns code 421 "url not found". In Teambition, 需求 / 缺陷 are task types (sfcId), not standalone resources. Query them through tb_list_tasks with sfcId (or taskType): 需求 = the type whose tasks have storygroupId set; 缺陷 = the type whose workflow contains 修复中 / 已解决 / 重复提交.

Four other tools (tb_list_documents, tb_list_testplans, tb_list_testcases, tb_worktime) return code 403 in 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: true the tool only resolves the target status and returns a preview. No PUT is 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 5xx never 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).

  • projectId is the default project for project-scoped tools (still overridable per call).
  • userId is always sent as X-Operator-Id (visibility filtering and write attribution), and it is now the default executor filter: tb_list_tasks with executorId omitted queries only the configured user's tasks (addressed as "my tasks"). Pass executorId: "all" (or "*") to query the whole project, or another user id to filter by that member. The implicit filter never applies when raw q is supplied (raw TQL wins), and it is always echoed in warnings + the returned tql, 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 (not sfcId) — filtered query returned 50 tasks, all matching the requested sfcId.
  • TQL filter field for parent task is parentId (not parentTaskId) — filtered query returned 3 direct children, all matching, including a known child.
  • GET /v3/task/{taskId}/tfs returns the workflow statuses.

Two environment facts worth knowing:

  • GET /v3/project/{id}/scenariofieldconfig/search can return HTTP 200 with code: 403 when the app lacks tb-core:project.sfc:list. Success is therefore decided on the business envelope, not the HTTP status. Without that permission, tb_list_task_types and the taskType name-resolution path in tb_list_tasks will 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 explicit statusName or taskflowstatusId.

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 subprocess service to run curl (the dynamic Host sandbox has no fetch). Default C:\Windows\System32\curl.exe, overridable via curlPath.
  • 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 fields for a few whitelisted extras.

License

MIT