@testplanit/mcp-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +93 -0
- package/README.md +1346 -0
- package/dist/cli.js +5525 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.mts +490 -0
- package/dist/index.d.ts +490 -0
- package/dist/index.js +5543 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +5501 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +68 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
|
|
3
|
+
interface EnvConfig {
|
|
4
|
+
apiToken: string;
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parse and validate the MCP server's environment.
|
|
9
|
+
*
|
|
10
|
+
* Throws a zod validation error when:
|
|
11
|
+
* - `TESTPLANIT_API_TOKEN` is missing or does not start with `tpi_`
|
|
12
|
+
* - `TESTPLANIT_API_URL` is set but is not a valid URL (omitting it uses the SaaS default)
|
|
13
|
+
*
|
|
14
|
+
* The returned `apiUrl` is normalized (trailing slash stripped).
|
|
15
|
+
*/
|
|
16
|
+
declare function parseEnv(env: NodeJS.ProcessEnv): EnvConfig;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Response shape from `GET /api/auth/whoami` — locked in lockstep with
|
|
20
|
+
* plan 05-02. Field semantics (frozen contract):
|
|
21
|
+
* - `readOnly === scopes.includes("mode:read")`
|
|
22
|
+
* - `isAgent === scopes.includes("client:mcp")`
|
|
23
|
+
*/
|
|
24
|
+
interface WhoamiUser {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string | null;
|
|
27
|
+
email: string;
|
|
28
|
+
scopes: string[];
|
|
29
|
+
readOnly: boolean;
|
|
30
|
+
isAgent: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Result of `validateToken()`.
|
|
34
|
+
*
|
|
35
|
+
* The failure variant carries `code` (upstream errorCode such as
|
|
36
|
+
* `READ_ONLY_TOKEN`, `EXPIRED_TOKEN`) and `statusCode` (HTTP status) so
|
|
37
|
+
* downstream tools — plan 05-07's `whoami` and Phase 6+ write tools —
|
|
38
|
+
* can construct `TestPlanItHttpError` without re-parsing the message
|
|
39
|
+
* string. This shape is locked at plan 05-06 and consumed directly by
|
|
40
|
+
* plan 05-07; do NOT widen retroactively.
|
|
41
|
+
*/
|
|
42
|
+
type ValidateResult = {
|
|
43
|
+
ok: true;
|
|
44
|
+
user: WhoamiUser;
|
|
45
|
+
} | {
|
|
46
|
+
ok: false;
|
|
47
|
+
message: string;
|
|
48
|
+
code?: string;
|
|
49
|
+
statusCode?: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Error class carrying upstream HTTP status + errorCode for tool handlers.
|
|
53
|
+
* Plan 05-07 throws this from the `whoami` tool when validateToken returns
|
|
54
|
+
* `ok: false` so the MCP error mapping retains the upstream codes.
|
|
55
|
+
*/
|
|
56
|
+
declare class TestPlanItHttpError extends Error {
|
|
57
|
+
statusCode?: number;
|
|
58
|
+
code?: string;
|
|
59
|
+
constructor(message: string, opts?: {
|
|
60
|
+
statusCode?: number;
|
|
61
|
+
code?: string;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Returns at most the `tpi_xxxx`-style 8-character prefix of the token for
|
|
66
|
+
* safe diagnostics. Never returns the full token. T-05-06 mitigation.
|
|
67
|
+
*/
|
|
68
|
+
declare function redactToken(token: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* Probe `GET /api/auth/whoami` with the configured Bearer token.
|
|
71
|
+
*
|
|
72
|
+
* Returns `{ ok: true, user }` on a 200 response with a valid JSON body.
|
|
73
|
+
* Returns `{ ok: false, message, code?, statusCode? }` on any non-2xx,
|
|
74
|
+
* unparseable body, or transport failure. The error message NEVER contains
|
|
75
|
+
* the raw token — only the redacted prefix appears (T-05-06).
|
|
76
|
+
*/
|
|
77
|
+
declare function validateToken(env: EnvConfig): Promise<ValidateResult>;
|
|
78
|
+
|
|
79
|
+
interface RunDeps {
|
|
80
|
+
parseEnvImpl: (env: NodeJS.ProcessEnv) => EnvConfig;
|
|
81
|
+
validateImpl: (env: EnvConfig) => Promise<ValidateResult>;
|
|
82
|
+
createServerImpl: (deps: {
|
|
83
|
+
env: EnvConfig;
|
|
84
|
+
user: WhoamiUser;
|
|
85
|
+
}) => McpServer;
|
|
86
|
+
connectImpl: (server: McpServer) => Promise<void>;
|
|
87
|
+
errLog: (...args: unknown[]) => void;
|
|
88
|
+
exitImpl: (code: number) => void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Default implementations wiring the real env, http, and stdio transport.
|
|
92
|
+
* Overridden in tests to assert exit codes + serial order without spawning
|
|
93
|
+
* a real subprocess.
|
|
94
|
+
*/
|
|
95
|
+
declare const defaultRunDeps: RunDeps;
|
|
96
|
+
/**
|
|
97
|
+
* Bootstrap the MCP server in strict serial order:
|
|
98
|
+
* parseEnv → validateToken → createServer → connect (stdio)
|
|
99
|
+
*
|
|
100
|
+
* Bad env or bad token exits with code 1 BEFORE any transport connect.
|
|
101
|
+
* All diagnostics go through `errLog` (defaults to `console.error`); raw
|
|
102
|
+
* token values are NEVER logged — only the redacted `tpi_xxxx` prefix
|
|
103
|
+
* appears in error messages (T-05-06).
|
|
104
|
+
*/
|
|
105
|
+
declare function runServer(deps?: RunDeps): Promise<void>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Dependencies the CLI bootstrap (cli.ts `runServer`) injects into
|
|
109
|
+
* `createServer`.
|
|
110
|
+
*
|
|
111
|
+
* `user` is the `WhoamiUser` resolved by the bootstrap probe; it is
|
|
112
|
+
* intentionally retained on the deps contract even though Phase 5's
|
|
113
|
+
* production tools (`whoami`) re-fetch live data on every call. Future
|
|
114
|
+
* tools may use `user` for static-shape decisions (e.g., gating Phase 6+
|
|
115
|
+
* write tools on `!user.readOnly` at registration time).
|
|
116
|
+
*/
|
|
117
|
+
interface ServerDeps {
|
|
118
|
+
env: EnvConfig;
|
|
119
|
+
user: WhoamiUser;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create an MCP server instance for TestPlanIt.
|
|
123
|
+
*
|
|
124
|
+
* Plan 05-06 registered a placeholder smoke tool so the
|
|
125
|
+
* `InMemoryTransport` handshake test had something to find. Plan 05-07
|
|
126
|
+
* replaces that with the production `whoami` tool, registered through the
|
|
127
|
+
* central `registerAll` registry — Phase 6+ tools plug into the registry
|
|
128
|
+
* without further edits to this file.
|
|
129
|
+
*/
|
|
130
|
+
declare function createServer(deps: ServerDeps): McpServer;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* MCP tool-result error envelope.
|
|
134
|
+
*
|
|
135
|
+
* The SDK accepts either a thrown exception (which it auto-converts) OR an
|
|
136
|
+
* explicit return value with `isError: true`. We use the explicit form so
|
|
137
|
+
* tool handlers control the agent-visible message verbatim — no SDK
|
|
138
|
+
* stack-trace leakage and no opaque "An error occurred" wrappers.
|
|
139
|
+
*
|
|
140
|
+
* Reference: 05-RESEARCH.md § Don't Hand-Roll row "Tool error formatting".
|
|
141
|
+
*/
|
|
142
|
+
interface ToolErrorResult {
|
|
143
|
+
isError: true;
|
|
144
|
+
content: Array<{
|
|
145
|
+
type: "text";
|
|
146
|
+
text: string;
|
|
147
|
+
}>;
|
|
148
|
+
[x: string]: unknown;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Translate any thrown error from a tool handler into the MCP tool-result
|
|
152
|
+
* error envelope. Three paths:
|
|
153
|
+
*
|
|
154
|
+
* 1. `TestPlanItHttpError` with a known `code`: friendly template, code
|
|
155
|
+
* appended in parentheses for log-grep traceability.
|
|
156
|
+
* 2. `TestPlanItHttpError` with an unknown / missing code: generic
|
|
157
|
+
* "Request failed: <message> (HTTP <status>)" fallback.
|
|
158
|
+
* 3. Any other `Error`: "Network or runtime error: <message>" fallback.
|
|
159
|
+
* 4. Non-Error throwable: "Unknown error".
|
|
160
|
+
*
|
|
161
|
+
* The friendly templates are fixed strings, NOT echoes of `err.message`,
|
|
162
|
+
* so a token-bearing message accidentally constructed upstream cannot leak
|
|
163
|
+
* the raw token through this layer (T-05-06 defense in depth). The fallback
|
|
164
|
+
* paths DO interpolate `err.message`, so the final text is run through
|
|
165
|
+
* `redactTokens` as a belt-and-suspenders scrub (WR-03).
|
|
166
|
+
*/
|
|
167
|
+
declare function mapHttpErrorToToolResult(err: unknown): ToolErrorResult;
|
|
168
|
+
|
|
169
|
+
interface WhoamiDeps {
|
|
170
|
+
env: EnvConfig;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Register the `whoami` MCP tool — the production replacement for plan
|
|
174
|
+
* 05-06's smoke tool.
|
|
175
|
+
*
|
|
176
|
+
* The tool re-fetches `GET /api/auth/whoami` on every invocation (NOT
|
|
177
|
+
* cached from the bootstrap probe) so scope changes — particularly the
|
|
178
|
+
* read-only flag — reflect immediately.
|
|
179
|
+
*
|
|
180
|
+
* On failure, the handler consumes the wider `ValidateResult` shape locked
|
|
181
|
+
* in plan 05-06 by reading `probe.code` and `probe.statusCode` directly,
|
|
182
|
+
* NOT by string-parsing `probe.message`. The constructed
|
|
183
|
+
* `TestPlanItHttpError` is then translated by `mapHttpErrorToToolResult`
|
|
184
|
+
* which knows how to format `READ_ONLY_TOKEN` (T-05-01 mitigation) and the
|
|
185
|
+
* other 7 host errorCodes.
|
|
186
|
+
*
|
|
187
|
+
* The description starts with `"Debug:"` so well-behaved agents
|
|
188
|
+
* deprioritize this tool versus the data tools shipping in Phase 6+.
|
|
189
|
+
*/
|
|
190
|
+
declare function registerWhoami(server: McpServer, deps: WhoamiDeps): void;
|
|
191
|
+
|
|
192
|
+
interface CasesListDeps {
|
|
193
|
+
env: EnvConfig;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
interface CasesGetDeps {
|
|
197
|
+
env: EnvConfig;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
interface CasesCreateDeps {
|
|
201
|
+
env: EnvConfig;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
interface CasesUpdateDeps {
|
|
205
|
+
env: EnvConfig;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
interface CasesDeleteDeps {
|
|
209
|
+
env: EnvConfig;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
type CasesDeps = CasesListDeps & CasesGetDeps & CasesCreateDeps & CasesUpdateDeps & CasesDeleteDeps;
|
|
213
|
+
declare function registerCases(server: McpServer, deps: CasesDeps): void;
|
|
214
|
+
|
|
215
|
+
interface FoldersListDeps {
|
|
216
|
+
env: EnvConfig;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
interface FoldersGetDeps {
|
|
220
|
+
env: EnvConfig;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
interface FoldersCreateDeps {
|
|
224
|
+
env: EnvConfig;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
interface FoldersUpdateDeps {
|
|
228
|
+
env: EnvConfig;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
interface FoldersDeleteDeps {
|
|
232
|
+
env: EnvConfig;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
type FoldersDeps = FoldersListDeps & FoldersGetDeps & FoldersCreateDeps & FoldersUpdateDeps & FoldersDeleteDeps;
|
|
236
|
+
declare function registerFolders(server: McpServer, deps: FoldersDeps): void;
|
|
237
|
+
|
|
238
|
+
interface TagsListDeps {
|
|
239
|
+
env: EnvConfig;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
type TagsDeps = TagsListDeps;
|
|
243
|
+
declare function registerTags(server: McpServer, deps: TagsDeps): void;
|
|
244
|
+
|
|
245
|
+
interface ProjectsListDeps {
|
|
246
|
+
env: EnvConfig;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
type ProjectsDeps = ProjectsListDeps;
|
|
250
|
+
declare function registerProjects(server: McpServer, deps: ProjectsDeps): void;
|
|
251
|
+
|
|
252
|
+
interface RunsListDeps {
|
|
253
|
+
env: EnvConfig;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
interface RunsGetDeps {
|
|
257
|
+
env: EnvConfig;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
interface RunsCasesListDeps {
|
|
261
|
+
env: EnvConfig;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface RunResultsListDeps {
|
|
265
|
+
env: EnvConfig;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
interface RunResultsGetDeps {
|
|
269
|
+
env: EnvConfig;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
interface RunResultsCreateDeps {
|
|
273
|
+
env: EnvConfig;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
type RunResultsDeps = RunResultsListDeps & RunResultsGetDeps & RunResultsCreateDeps;
|
|
277
|
+
|
|
278
|
+
interface RunsCreateDeps {
|
|
279
|
+
env: EnvConfig;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interface RunsUpdateDeps {
|
|
283
|
+
env: EnvConfig;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
interface RunsCasesAddDeps {
|
|
287
|
+
env: EnvConfig;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
type RunsDeps = RunsListDeps & RunsGetDeps & RunsCasesListDeps & RunResultsDeps & RunsCreateDeps & RunsUpdateDeps & RunsCasesAddDeps;
|
|
291
|
+
declare function registerRuns(server: McpServer, deps: RunsDeps): void;
|
|
292
|
+
|
|
293
|
+
interface SessionsListDeps {
|
|
294
|
+
env: EnvConfig;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
interface SessionsGetDeps {
|
|
298
|
+
env: EnvConfig;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
interface SessionResultsListDeps {
|
|
302
|
+
env: EnvConfig;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
interface SessionResultsGetDeps {
|
|
306
|
+
env: EnvConfig;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Aggregate dependencies for the SESS-03 / SESS-04 session-result read tools.
|
|
311
|
+
* Both tools share the same EnvConfig; this intersection lets the parent
|
|
312
|
+
* `registerSessions` pass a single deps object (mirrors the runs/results
|
|
313
|
+
* pattern from plan 07-03).
|
|
314
|
+
*/
|
|
315
|
+
type SessionResultsDeps = SessionResultsListDeps & SessionResultsGetDeps;
|
|
316
|
+
|
|
317
|
+
interface SessionsFindingsDeps {
|
|
318
|
+
env: EnvConfig;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
interface SessionsCreateDeps {
|
|
322
|
+
env: EnvConfig;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
interface SessionsUpdateDeps {
|
|
326
|
+
env: EnvConfig;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
type SessionsDeps = SessionsListDeps & SessionsGetDeps & SessionResultsDeps & SessionsFindingsDeps & SessionsCreateDeps & SessionsUpdateDeps;
|
|
330
|
+
declare function registerSessions(server: McpServer, deps: SessionsDeps): void;
|
|
331
|
+
|
|
332
|
+
interface CodeRepositoriesListDeps {
|
|
333
|
+
env: EnvConfig;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Aggregate dependencies for the Phase 8 code-repositories read tools. Only
|
|
338
|
+
* one tool today (`testplanit_code_repositories_list`); aliasing rather than
|
|
339
|
+
* intersecting keeps room to add `_get` etc. via union extension when the
|
|
340
|
+
* single-row-per-project invariant relaxes.
|
|
341
|
+
*
|
|
342
|
+
* Registry wiring into `tools/index.ts` is intentionally deferred to plan
|
|
343
|
+
* 08-05 (wave 3) so plans 08-01..08-04 can run in parallel without touching
|
|
344
|
+
* the same file.
|
|
345
|
+
*/
|
|
346
|
+
type CodeRepositoriesDeps = CodeRepositoriesListDeps;
|
|
347
|
+
|
|
348
|
+
interface IssuesFindByKeyDeps {
|
|
349
|
+
env: EnvConfig;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
interface IssuesListDeps {
|
|
353
|
+
env: EnvConfig;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
interface IssuesGetDeps {
|
|
357
|
+
env: EnvConfig;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
interface IssuesListLinksDeps {
|
|
361
|
+
env: EnvConfig;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
interface IssuesLinkDeps {
|
|
365
|
+
env: EnvConfig;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
type IssuesDeps = IssuesFindByKeyDeps & IssuesListDeps & IssuesGetDeps & IssuesListLinksDeps & IssuesLinkDeps;
|
|
369
|
+
|
|
370
|
+
interface RepositoryCaseLinksListDeps {
|
|
371
|
+
env: EnvConfig;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
type RepositoryCaseLinksDeps = RepositoryCaseLinksListDeps;
|
|
375
|
+
|
|
376
|
+
interface MilestonesListDeps {
|
|
377
|
+
env: EnvConfig;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
interface MilestonesGetDeps {
|
|
381
|
+
env: EnvConfig;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
interface MilestoneTypesListDeps {
|
|
385
|
+
env: EnvConfig;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
interface MilestonesCreateDeps {
|
|
389
|
+
env: EnvConfig;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
interface MilestonesUpdateDeps {
|
|
393
|
+
env: EnvConfig;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
type MilestonesDeps = MilestonesListDeps & MilestonesGetDeps & MilestoneTypesListDeps & MilestonesCreateDeps & MilestonesUpdateDeps;
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Aggregate dependencies for every tool registered by
|
|
400
|
+
* `@testplanit/mcp-server`. The intersection widens with each new domain;
|
|
401
|
+
* adding a new domain barrel means adding the matching `& <Domain>Deps`
|
|
402
|
+
* here so all registered tools see the same single deps object at runtime.
|
|
403
|
+
*/
|
|
404
|
+
type ToolRegistryDeps = WhoamiDeps & CasesDeps & FoldersDeps & TagsDeps & ProjectsDeps & RunsDeps & SessionsDeps & CodeRepositoriesDeps & IssuesDeps & RepositoryCaseLinksDeps & MilestonesDeps;
|
|
405
|
+
/**
|
|
406
|
+
* Register every tool shipped by `@testplanit/mcp-server`.
|
|
407
|
+
*
|
|
408
|
+
* Tools are grouped by domain: whoami (debug/identity), cases, folders,
|
|
409
|
+
* tags, projects (agent context disambiguation), runs, sessions,
|
|
410
|
+
* code-repositories, issues, repository-case-links, and milestones
|
|
411
|
+
* (milestones_list, milestones_get, milestone_types_list).
|
|
412
|
+
*/
|
|
413
|
+
declare function registerAll(server: McpServer, deps: ToolRegistryDeps): void;
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Internal ZenStack RPC client.
|
|
417
|
+
*
|
|
418
|
+
* Replicates the dispatch pattern from `packages/api/src/client.ts` so
|
|
419
|
+
* `@testplanit/mcp-server` has NO dependency on `@testplanit/api` (D-01).
|
|
420
|
+
*
|
|
421
|
+
* Read operations use GET with `?q=encodeURIComponent(JSON.stringify(body))`.
|
|
422
|
+
* Write operations use POST/PATCH/DELETE with the JSON body.
|
|
423
|
+
*
|
|
424
|
+
* Errors:
|
|
425
|
+
* - Non-2xx → throws TestPlanItHttpError with statusCode + (when present) code
|
|
426
|
+
* parsed from the response body. Critical: HTTP 422 may be a route.ts
|
|
427
|
+
* remap of a ZenStack 403 (policy denial) or 404 (P2025 missing record).
|
|
428
|
+
* The error mapper in errors.ts disambiguates by message content.
|
|
429
|
+
* - 2xx with `error` envelope → throws TestPlanItHttpError with envelope code.
|
|
430
|
+
* - 2xx with `data` → returns `data` unwrapped.
|
|
431
|
+
*
|
|
432
|
+
* Soft-delete invariant: callers MUST use `update` with `{ data: { isDeleted: true } }`
|
|
433
|
+
* for soft-delete, NEVER the `delete` or `deleteMany` operations (T-06-06).
|
|
434
|
+
*/
|
|
435
|
+
declare function zenstack<T>(model: string, operation: string, body: unknown, env: EnvConfig): Promise<T>;
|
|
436
|
+
/**
|
|
437
|
+
* Name → ID lookup via `/api/cli/lookup` (D-02).
|
|
438
|
+
*
|
|
439
|
+
* NOT all entity types are supported — see VERIFIED type union below. Notably,
|
|
440
|
+
* `CaseField` is NOT a lookup type; resolve custom fields via
|
|
441
|
+
* `zenstack("caseFields", "findMany", { where: { displayName: ..., isDeleted: false } })`.
|
|
442
|
+
*
|
|
443
|
+
* The `state` lookup type hardcodes `WorkflowScope.RUNS` on the host
|
|
444
|
+
* (`/api/cli/lookup/route.ts` line 106). For case workflow state, use
|
|
445
|
+
* `resolveCaseWorkflowState` instead.
|
|
446
|
+
*/
|
|
447
|
+
type LookupType = "project" | "state" | "config" | "milestone" | "tag" | "folder" | "testRun";
|
|
448
|
+
interface LookupRequest {
|
|
449
|
+
type: LookupType;
|
|
450
|
+
name: string;
|
|
451
|
+
projectId?: number;
|
|
452
|
+
createIfMissing?: boolean;
|
|
453
|
+
}
|
|
454
|
+
interface LookupResponse {
|
|
455
|
+
id: number;
|
|
456
|
+
name: string;
|
|
457
|
+
created?: boolean;
|
|
458
|
+
}
|
|
459
|
+
declare function lookup(options: LookupRequest, env: EnvConfig): Promise<LookupResponse>;
|
|
460
|
+
/**
|
|
461
|
+
* Resolve the single active repository for a project. Cases and folders
|
|
462
|
+
* require `repositoryId` on create; the active repository is selected by
|
|
463
|
+
* the first row matching `isActive=true, isDeleted=false, isArchived=false`.
|
|
464
|
+
*
|
|
465
|
+
* Throws TestPlanItHttpError with statusCode 422 (host-class "operation
|
|
466
|
+
* refused / missing context") when no active repository exists, with a
|
|
467
|
+
* human-readable message instructing the user to initialize the repo via
|
|
468
|
+
* the TestPlanIt UI. The 422 status routes the error through the same
|
|
469
|
+
* `mapHttpErrorToToolResult` branch as host-side missing-record errors.
|
|
470
|
+
*/
|
|
471
|
+
declare function resolveActiveRepository(projectId: number, env: EnvConfig): Promise<number>;
|
|
472
|
+
/**
|
|
473
|
+
* Resolve a default template assigned to the project (cases require
|
|
474
|
+
* `templateId` per RepositoryCases.templateId being non-nullable —
|
|
475
|
+
* VERIFIED in schema.zmodel).
|
|
476
|
+
*/
|
|
477
|
+
declare function resolveDefaultTemplate(projectId: number, env: EnvConfig): Promise<number>;
|
|
478
|
+
/**
|
|
479
|
+
* Resolve a workflow state for the CASES scope (NOT runs). Pass `name` to
|
|
480
|
+
* select by name; omit to take the first by `order asc`.
|
|
481
|
+
*
|
|
482
|
+
* Cannot use `/api/cli/lookup` — that endpoint hardcodes
|
|
483
|
+
* `WorkflowScope.RUNS` (see /api/cli/lookup/route.ts line 106).
|
|
484
|
+
*/
|
|
485
|
+
declare function resolveCaseWorkflowState(projectId: number, env: EnvConfig, name?: string): Promise<{
|
|
486
|
+
id: number;
|
|
487
|
+
name: string;
|
|
488
|
+
}>;
|
|
489
|
+
|
|
490
|
+
export { type CasesDeps, type EnvConfig, type FoldersDeps, type LookupRequest, type LookupResponse, type LookupType, type ProjectsDeps, type RunDeps, type RunsDeps, type ServerDeps, type SessionsDeps, type TagsDeps, TestPlanItHttpError, type ToolErrorResult, type ToolRegistryDeps, type ValidateResult, type WhoamiDeps, type WhoamiUser, createServer, defaultRunDeps, lookup, mapHttpErrorToToolResult, parseEnv, redactToken, registerAll, registerCases, registerFolders, registerProjects, registerRuns, registerSessions, registerTags, registerWhoami, resolveActiveRepository, resolveCaseWorkflowState, resolveDefaultTemplate, runServer, validateToken, zenstack };
|