@testchimp/cli 0.1.84 → 0.1.85
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/README.md +78 -1
- package/dist/agentwatch/connect.d.ts +37 -0
- package/dist/agentwatch/connect.js +250 -0
- package/dist/agentwatch/connect.test.d.ts +1 -0
- package/dist/agentwatch/connect.test.js +166 -0
- package/dist/agentwatch/credentialsFile.d.ts +40 -0
- package/dist/agentwatch/credentialsFile.js +76 -0
- package/dist/cli/program.d.ts +2 -0
- package/dist/cli/program.js +288 -4
- package/dist/core/client.d.ts +32 -0
- package/dist/core/client.js +133 -9
- package/dist/core/client.test.js +130 -2
- package/dist/core/schemas.d.ts +91 -23
- package/dist/core/schemas.js +49 -0
- package/dist/core/tools.d.ts +5 -1
- package/dist/core/tools.js +110 -0
- package/dist/core/tools.test.js +94 -0
- package/dist/mcp/httpServer.d.ts +14 -0
- package/dist/mcp/httpServer.js +169 -0
- package/dist/mcp/httpServer.test.d.ts +1 -0
- package/dist/mcp/httpServer.test.js +108 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +8 -2
- package/dist/workspace/gitRepo.d.ts +18 -0
- package/dist/workspace/gitRepo.js +103 -0
- package/dist/workspace/projectsRegistry.d.ts +82 -0
- package/dist/workspace/projectsRegistry.js +246 -0
- package/dist/workspace/projectsRegistry.test.d.ts +1 -0
- package/dist/workspace/projectsRegistry.test.js +249 -0
- package/package.json +2 -2
package/dist/core/client.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { afterEach, describe, it } from "node:test";
|
|
3
|
-
import { getBackendUrl, postMcp } from "./client.js";
|
|
2
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
3
|
+
import { getBackendUrl, isValidBotId, postIngress, postMcp, resetClientWarningsForTests, runWithRequestAuth, } from "./client.js";
|
|
4
4
|
const originalBackendUrl = process.env.TESTCHIMP_BACKEND_URL;
|
|
5
5
|
const originalApiKey = process.env.TESTCHIMP_API_KEY;
|
|
6
6
|
const originalFetch = globalThis.fetch;
|
|
@@ -52,3 +52,131 @@ describe("postMcp", () => {
|
|
|
52
52
|
assert.equal(requestInit?.redirect, "manual");
|
|
53
53
|
});
|
|
54
54
|
});
|
|
55
|
+
describe("request headers", () => {
|
|
56
|
+
const originalBotId = process.env.TESTCHIMP_BOT_ID;
|
|
57
|
+
const originalOauthToken = process.env.TESTCHIMP_OAUTH_TOKEN;
|
|
58
|
+
const originalIngressUrl = process.env.TESTCHIMP_INGRESS_URL;
|
|
59
|
+
const originalConsoleError = console.error;
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
process.env.TESTCHIMP_BACKEND_URL = "https://featureservice.example.com";
|
|
62
|
+
delete process.env.TESTCHIMP_API_KEY;
|
|
63
|
+
delete process.env.TESTCHIMP_BOT_ID;
|
|
64
|
+
delete process.env.TESTCHIMP_OAUTH_TOKEN;
|
|
65
|
+
delete process.env.TESTCHIMP_INGRESS_URL;
|
|
66
|
+
resetClientWarningsForTests();
|
|
67
|
+
});
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
for (const [key, value] of [
|
|
70
|
+
["TESTCHIMP_BOT_ID", originalBotId],
|
|
71
|
+
["TESTCHIMP_OAUTH_TOKEN", originalOauthToken],
|
|
72
|
+
["TESTCHIMP_INGRESS_URL", originalIngressUrl],
|
|
73
|
+
]) {
|
|
74
|
+
if (value == null)
|
|
75
|
+
delete process.env[key];
|
|
76
|
+
else
|
|
77
|
+
process.env[key] = value;
|
|
78
|
+
}
|
|
79
|
+
console.error = originalConsoleError;
|
|
80
|
+
});
|
|
81
|
+
function captureFetch() {
|
|
82
|
+
const capture = { calls: [] };
|
|
83
|
+
globalThis.fetch = (async (input, init) => {
|
|
84
|
+
capture.calls.push({ url: String(input), init });
|
|
85
|
+
return new Response("{}", { status: 200 });
|
|
86
|
+
});
|
|
87
|
+
return capture;
|
|
88
|
+
}
|
|
89
|
+
it("sends exactly the legacy header set with an API key and no bot env", async () => {
|
|
90
|
+
process.env.TESTCHIMP_API_KEY = "test-key";
|
|
91
|
+
const capture = captureFetch();
|
|
92
|
+
await postMcp("/api/mcp/example", { a: 1 });
|
|
93
|
+
assert.equal(capture.calls[0].url, "https://featureservice.example.com/api/mcp/example");
|
|
94
|
+
assert.deepEqual(capture.calls[0].init?.headers, {
|
|
95
|
+
"Content-Type": "application/json",
|
|
96
|
+
"TestChimp-Api-Key": "test-key",
|
|
97
|
+
});
|
|
98
|
+
assert.equal(capture.calls[0].init?.body, JSON.stringify({ a: 1 }));
|
|
99
|
+
});
|
|
100
|
+
it("keeps the missing API key error when no credentials are set", async () => {
|
|
101
|
+
captureFetch();
|
|
102
|
+
await assert.rejects(postMcp("/api/mcp/example", {}), /TESTCHIMP_API_KEY is required/);
|
|
103
|
+
});
|
|
104
|
+
it("adds a valid bot-id header", async () => {
|
|
105
|
+
process.env.TESTCHIMP_API_KEY = "test-key";
|
|
106
|
+
process.env.TESTCHIMP_BOT_ID = "grok-bot_01";
|
|
107
|
+
const capture = captureFetch();
|
|
108
|
+
await postMcp("/api/mcp/example", {});
|
|
109
|
+
assert.deepEqual(capture.calls[0].init?.headers, {
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
"TestChimp-Api-Key": "test-key",
|
|
112
|
+
"bot-id": "grok-bot_01",
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
it("drops an invalid bot id and warns once", async () => {
|
|
116
|
+
process.env.TESTCHIMP_API_KEY = "test-key";
|
|
117
|
+
process.env.TESTCHIMP_BOT_ID = "bad bot id";
|
|
118
|
+
const warnings = [];
|
|
119
|
+
console.error = (msg) => warnings.push(String(msg));
|
|
120
|
+
const capture = captureFetch();
|
|
121
|
+
await postMcp("/api/mcp/example", {});
|
|
122
|
+
await postMcp("/api/mcp/example", {});
|
|
123
|
+
assert.deepEqual(capture.calls[0].init?.headers, {
|
|
124
|
+
"Content-Type": "application/json",
|
|
125
|
+
"TestChimp-Api-Key": "test-key",
|
|
126
|
+
});
|
|
127
|
+
assert.equal(warnings.length, 1);
|
|
128
|
+
assert.match(warnings[0], /TESTCHIMP_BOT_ID/);
|
|
129
|
+
assert.equal(isValidBotId("x".repeat(64)), true);
|
|
130
|
+
assert.equal(isValidBotId("x".repeat(65)), false);
|
|
131
|
+
assert.equal(isValidBotId("bot\u0007"), false);
|
|
132
|
+
});
|
|
133
|
+
it("uses the OAuth token without requiring an API key", async () => {
|
|
134
|
+
process.env.TESTCHIMP_OAUTH_TOKEN = "jwt-token";
|
|
135
|
+
const capture = captureFetch();
|
|
136
|
+
await postMcp("/api/mcp/example", {});
|
|
137
|
+
assert.deepEqual(capture.calls[0].init?.headers, {
|
|
138
|
+
"Content-Type": "application/json",
|
|
139
|
+
Authorization: "Bearer jwt-token",
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
it("sends both the API key and the bearer when both are set", async () => {
|
|
143
|
+
process.env.TESTCHIMP_API_KEY = "test-key";
|
|
144
|
+
process.env.TESTCHIMP_OAUTH_TOKEN = "jwt-token";
|
|
145
|
+
process.env.TESTCHIMP_BOT_ID = "bot-1";
|
|
146
|
+
const capture = captureFetch();
|
|
147
|
+
await postMcp("/api/mcp/example", {});
|
|
148
|
+
assert.deepEqual(capture.calls[0].init?.headers, {
|
|
149
|
+
"Content-Type": "application/json",
|
|
150
|
+
"TestChimp-Api-Key": "test-key",
|
|
151
|
+
Authorization: "Bearer jwt-token",
|
|
152
|
+
"bot-id": "bot-1",
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
it("isolated request auth ignores env credentials", async () => {
|
|
156
|
+
process.env.TESTCHIMP_API_KEY = "server-key";
|
|
157
|
+
process.env.TESTCHIMP_BOT_ID = "server-bot";
|
|
158
|
+
const capture = captureFetch();
|
|
159
|
+
await runWithRequestAuth({ bearerToken: "caller-token", isolated: true }, () => postMcp("/api/mcp/example", {}));
|
|
160
|
+
assert.deepEqual(capture.calls[0].init?.headers, {
|
|
161
|
+
"Content-Type": "application/json",
|
|
162
|
+
Authorization: "Bearer caller-token",
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
it("posts to the ingress base or a trusted absolute URL", async () => {
|
|
166
|
+
process.env.TESTCHIMP_API_KEY = "test-key";
|
|
167
|
+
const capture = captureFetch();
|
|
168
|
+
await postIngress("/bot/events/ack", { eventIds: ["e1"] });
|
|
169
|
+
assert.equal(capture.calls[0].url, "https://ingress.testchimp.io/bot/events/ack");
|
|
170
|
+
assert.equal(capture.calls[0].init?.redirect, "manual");
|
|
171
|
+
await postIngress("https://ingress-staging.testchimp.io/bot/events/ack", {});
|
|
172
|
+
assert.equal(capture.calls[1].url, "https://ingress-staging.testchimp.io/bot/events/ack");
|
|
173
|
+
await postIngress("http://localhost:4302/bot/events/ack", {});
|
|
174
|
+
assert.equal(capture.calls[2].url, "http://localhost:4302/bot/events/ack");
|
|
175
|
+
await assert.rejects(postIngress("http://ingress.testchimp.io/bot/events/ack", {}), /must use https/);
|
|
176
|
+
await assert.rejects(postIngress("https://evil.example.com/bot/events/ack", {}), /untrusted ingress host/);
|
|
177
|
+
assert.equal(capture.calls.length, 3);
|
|
178
|
+
process.env.TESTCHIMP_INGRESS_URL = "https://ingress.customer.example/";
|
|
179
|
+
await postIngress("https://ingress.customer.example/bot/events/ack", {});
|
|
180
|
+
assert.equal(capture.calls[3].url, "https://ingress.customer.example/bot/events/ack");
|
|
181
|
+
});
|
|
182
|
+
});
|
package/dist/core/schemas.d.ts
CHANGED
|
@@ -18,9 +18,9 @@ export declare const listCoverageInput: z.ZodObject<{
|
|
|
18
18
|
includeNonCoveredTestScenarios: z.ZodOptional<z.ZodBoolean>;
|
|
19
19
|
branchName: z.ZodOptional<z.ZodString>;
|
|
20
20
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
21
|
+
android: "android";
|
|
21
22
|
web: "web";
|
|
22
23
|
ios: "ios";
|
|
23
|
-
android: "android";
|
|
24
24
|
}>>;
|
|
25
25
|
recordTypes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
26
26
|
manual: "manual";
|
|
@@ -47,9 +47,9 @@ export declare const listExecutionInput: z.ZodObject<{
|
|
|
47
47
|
scenarioId: z.ZodOptional<z.ZodString>;
|
|
48
48
|
testId: z.ZodOptional<z.ZodString>;
|
|
49
49
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
50
|
+
android: "android";
|
|
50
51
|
web: "web";
|
|
51
52
|
ios: "ios";
|
|
52
|
-
android: "android";
|
|
53
53
|
}>>;
|
|
54
54
|
dimensionFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
55
55
|
dimension: z.ZodString;
|
|
@@ -70,9 +70,9 @@ export declare const suiteExecutionStatsInput: z.ZodObject<{
|
|
|
70
70
|
scenarioId: z.ZodOptional<z.ZodString>;
|
|
71
71
|
testId: z.ZodOptional<z.ZodString>;
|
|
72
72
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
73
|
+
android: "android";
|
|
73
74
|
web: "web";
|
|
74
75
|
ios: "ios";
|
|
75
|
-
android: "android";
|
|
76
76
|
}>>;
|
|
77
77
|
dimensionFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
78
78
|
dimension: z.ZodString;
|
|
@@ -828,14 +828,14 @@ export declare const executionScopeSchema: z.ZodObject<{
|
|
|
828
828
|
release: z.ZodOptional<z.ZodString>;
|
|
829
829
|
branchName: z.ZodOptional<z.ZodString>;
|
|
830
830
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
831
|
+
android: "android";
|
|
831
832
|
web: "web";
|
|
832
833
|
ios: "ios";
|
|
833
|
-
android: "android";
|
|
834
834
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
835
835
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
836
836
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
837
837
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
838
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
838
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
839
839
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
840
840
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
841
841
|
key: z.ZodString;
|
|
@@ -868,14 +868,14 @@ export declare const listTruecoverageEventsInput: z.ZodObject<{
|
|
|
868
868
|
release: z.ZodOptional<z.ZodString>;
|
|
869
869
|
branchName: z.ZodOptional<z.ZodString>;
|
|
870
870
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
871
|
+
android: "android";
|
|
871
872
|
web: "web";
|
|
872
873
|
ios: "ios";
|
|
873
|
-
android: "android";
|
|
874
874
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
875
875
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
876
876
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
877
877
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
878
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
878
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
879
879
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
880
880
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
881
881
|
key: z.ZodString;
|
|
@@ -907,14 +907,14 @@ export declare const listTruecoverageEventsInput: z.ZodObject<{
|
|
|
907
907
|
release: z.ZodOptional<z.ZodString>;
|
|
908
908
|
branchName: z.ZodOptional<z.ZodString>;
|
|
909
909
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
910
|
+
android: "android";
|
|
910
911
|
web: "web";
|
|
911
912
|
ios: "ios";
|
|
912
|
-
android: "android";
|
|
913
913
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
914
914
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
915
915
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
916
916
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
917
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
917
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
918
918
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
919
919
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
920
920
|
key: z.ZodString;
|
|
@@ -949,14 +949,14 @@ export declare const getTruecoverageEventDetailsInput: z.ZodObject<{
|
|
|
949
949
|
release: z.ZodOptional<z.ZodString>;
|
|
950
950
|
branchName: z.ZodOptional<z.ZodString>;
|
|
951
951
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
952
|
+
android: "android";
|
|
952
953
|
web: "web";
|
|
953
954
|
ios: "ios";
|
|
954
|
-
android: "android";
|
|
955
955
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
956
956
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
957
957
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
958
958
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
959
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
959
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
960
960
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
961
961
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
962
962
|
key: z.ZodString;
|
|
@@ -988,14 +988,14 @@ export declare const getTruecoverageEventDetailsInput: z.ZodObject<{
|
|
|
988
988
|
release: z.ZodOptional<z.ZodString>;
|
|
989
989
|
branchName: z.ZodOptional<z.ZodString>;
|
|
990
990
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
991
|
+
android: "android";
|
|
991
992
|
web: "web";
|
|
992
993
|
ios: "ios";
|
|
993
|
-
android: "android";
|
|
994
994
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
995
995
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
996
996
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
997
997
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
998
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
998
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
999
999
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
1000
1000
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1001
1001
|
key: z.ZodString;
|
|
@@ -1030,14 +1030,14 @@ export declare const listTruecoverageChildEventTreeInput: z.ZodObject<{
|
|
|
1030
1030
|
release: z.ZodOptional<z.ZodString>;
|
|
1031
1031
|
branchName: z.ZodOptional<z.ZodString>;
|
|
1032
1032
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
1033
|
+
android: "android";
|
|
1033
1034
|
web: "web";
|
|
1034
1035
|
ios: "ios";
|
|
1035
|
-
android: "android";
|
|
1036
1036
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
1037
1037
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
1038
1038
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
1039
1039
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
1040
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
1040
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
1041
1041
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
1042
1042
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1043
1043
|
key: z.ZodString;
|
|
@@ -1069,14 +1069,14 @@ export declare const listTruecoverageChildEventTreeInput: z.ZodObject<{
|
|
|
1069
1069
|
release: z.ZodOptional<z.ZodString>;
|
|
1070
1070
|
branchName: z.ZodOptional<z.ZodString>;
|
|
1071
1071
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
1072
|
+
android: "android";
|
|
1072
1073
|
web: "web";
|
|
1073
1074
|
ios: "ios";
|
|
1074
|
-
android: "android";
|
|
1075
1075
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
1076
1076
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
1077
1077
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
1078
1078
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
1079
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
1079
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
1080
1080
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
1081
1081
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1082
1082
|
key: z.ZodString;
|
|
@@ -1112,14 +1112,14 @@ export declare const getTruecoverageEventTransitionInput: z.ZodObject<{
|
|
|
1112
1112
|
release: z.ZodOptional<z.ZodString>;
|
|
1113
1113
|
branchName: z.ZodOptional<z.ZodString>;
|
|
1114
1114
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
1115
|
+
android: "android";
|
|
1115
1116
|
web: "web";
|
|
1116
1117
|
ios: "ios";
|
|
1117
|
-
android: "android";
|
|
1118
1118
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
1119
1119
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
1120
1120
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
1121
1121
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
1122
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
1122
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
1123
1123
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
1124
1124
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1125
1125
|
key: z.ZodString;
|
|
@@ -1151,14 +1151,14 @@ export declare const getTruecoverageEventTransitionInput: z.ZodObject<{
|
|
|
1151
1151
|
release: z.ZodOptional<z.ZodString>;
|
|
1152
1152
|
branchName: z.ZodOptional<z.ZodString>;
|
|
1153
1153
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
1154
|
+
android: "android";
|
|
1154
1155
|
web: "web";
|
|
1155
1156
|
ios: "ios";
|
|
1156
|
-
android: "android";
|
|
1157
1157
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
1158
1158
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
1159
1159
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
1160
1160
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
1161
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
1161
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
1162
1162
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
1163
1163
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1164
1164
|
key: z.ZodString;
|
|
@@ -1205,14 +1205,14 @@ export declare const getTruecoverageEventTimeSeriesInput: z.ZodObject<{
|
|
|
1205
1205
|
release: z.ZodOptional<z.ZodString>;
|
|
1206
1206
|
branchName: z.ZodOptional<z.ZodString>;
|
|
1207
1207
|
platform: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
|
|
1208
|
+
android: "android";
|
|
1208
1209
|
web: "web";
|
|
1209
1210
|
ios: "ios";
|
|
1210
|
-
android: "android";
|
|
1211
1211
|
UNKNOWN_EXECUTION_PLATFORM: "UNKNOWN_EXECUTION_PLATFORM";
|
|
1212
1212
|
WEB_EXECUTION_PLATFORM: "WEB_EXECUTION_PLATFORM";
|
|
1213
1213
|
IOS_EXECUTION_PLATFORM: "IOS_EXECUTION_PLATFORM";
|
|
1214
1214
|
ANDROID_EXECUTION_PLATFORM: "ANDROID_EXECUTION_PLATFORM";
|
|
1215
|
-
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "
|
|
1215
|
+
}>, z.ZodTransform<"UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM", "android" | "web" | "ios" | "UNKNOWN_EXECUTION_PLATFORM" | "WEB_EXECUTION_PLATFORM" | "IOS_EXECUTION_PLATFORM" | "ANDROID_EXECUTION_PLATFORM">>>;
|
|
1216
1216
|
automationEmitsOnly: z.ZodOptional<z.ZodBoolean>;
|
|
1217
1217
|
metadataFilters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1218
1218
|
key: z.ZodString;
|
|
@@ -2008,3 +2008,71 @@ export declare const uploadAttachmentInput: z.ZodObject<{
|
|
|
2008
2008
|
export declare const getBatchViewUrlInput: z.ZodObject<{
|
|
2009
2009
|
batchInvocationId: z.ZodString;
|
|
2010
2010
|
}, z.core.$strip>;
|
|
2011
|
+
export declare const botRoleSchema: z.ZodEnum<{
|
|
2012
|
+
QA_LEAD: "QA_LEAD";
|
|
2013
|
+
PM: "PM";
|
|
2014
|
+
QA_ENGINEER: "QA_ENGINEER";
|
|
2015
|
+
DEVELOPER: "DEVELOPER";
|
|
2016
|
+
}>;
|
|
2017
|
+
export declare const botCapabilitySchema: z.ZodEnum<{
|
|
2018
|
+
REQUIREMENTS_UPDATE: "REQUIREMENTS_UPDATE";
|
|
2019
|
+
E2E_AUTHORING: "E2E_AUTHORING";
|
|
2020
|
+
ISSUE_FIX: "ISSUE_FIX";
|
|
2021
|
+
MANUAL_TEST_COORDINATION: "MANUAL_TEST_COORDINATION";
|
|
2022
|
+
TEST_BATCH_FIX: "TEST_BATCH_FIX";
|
|
2023
|
+
QA_POSTURE: "QA_POSTURE";
|
|
2024
|
+
}>;
|
|
2025
|
+
export declare const botSubscriptionFilterSchema: z.ZodObject<{
|
|
2026
|
+
field: z.ZodString;
|
|
2027
|
+
op: z.ZodDefault<z.ZodLiteral<"eq">>;
|
|
2028
|
+
value: z.ZodString;
|
|
2029
|
+
}, z.core.$strip>;
|
|
2030
|
+
export declare const botSubscriptionSchema: z.ZodObject<{
|
|
2031
|
+
eventType: z.ZodString;
|
|
2032
|
+
filters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
2033
|
+
field: z.ZodString;
|
|
2034
|
+
op: z.ZodDefault<z.ZodLiteral<"eq">>;
|
|
2035
|
+
value: z.ZodString;
|
|
2036
|
+
}, z.core.$strip>>>;
|
|
2037
|
+
}, z.core.$strip>;
|
|
2038
|
+
export declare const registerBotProfileInput: z.ZodObject<{
|
|
2039
|
+
botId: z.ZodOptional<z.ZodString>;
|
|
2040
|
+
role: z.ZodEnum<{
|
|
2041
|
+
QA_LEAD: "QA_LEAD";
|
|
2042
|
+
PM: "PM";
|
|
2043
|
+
QA_ENGINEER: "QA_ENGINEER";
|
|
2044
|
+
DEVELOPER: "DEVELOPER";
|
|
2045
|
+
}>;
|
|
2046
|
+
responsibilities: z.ZodOptional<z.ZodString>;
|
|
2047
|
+
capabilities: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
2048
|
+
REQUIREMENTS_UPDATE: "REQUIREMENTS_UPDATE";
|
|
2049
|
+
E2E_AUTHORING: "E2E_AUTHORING";
|
|
2050
|
+
ISSUE_FIX: "ISSUE_FIX";
|
|
2051
|
+
MANUAL_TEST_COORDINATION: "MANUAL_TEST_COORDINATION";
|
|
2052
|
+
TEST_BATCH_FIX: "TEST_BATCH_FIX";
|
|
2053
|
+
QA_POSTURE: "QA_POSTURE";
|
|
2054
|
+
}>>>;
|
|
2055
|
+
subscriptions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
2056
|
+
eventType: z.ZodString;
|
|
2057
|
+
filters: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
2058
|
+
field: z.ZodString;
|
|
2059
|
+
op: z.ZodDefault<z.ZodLiteral<"eq">>;
|
|
2060
|
+
value: z.ZodString;
|
|
2061
|
+
}, z.core.$strip>>>;
|
|
2062
|
+
}, z.core.$strip>>>;
|
|
2063
|
+
}, z.core.$strip>;
|
|
2064
|
+
export declare const getBotProfileInput: z.ZodObject<{
|
|
2065
|
+
botId: z.ZodOptional<z.ZodString>;
|
|
2066
|
+
}, z.core.$strip>;
|
|
2067
|
+
export declare const getMyTasksInput: z.ZodObject<{
|
|
2068
|
+
userId: z.ZodOptional<z.ZodString>;
|
|
2069
|
+
}, z.core.$strip>;
|
|
2070
|
+
export declare const listTestsAwaitingVerificationInput: z.ZodObject<{
|
|
2071
|
+
userId: z.ZodOptional<z.ZodString>;
|
|
2072
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
2073
|
+
}, z.core.$strip>;
|
|
2074
|
+
export declare const MAX_BOT_ACK_EVENT_IDS = 100;
|
|
2075
|
+
export declare const ackBotEventsInput: z.ZodObject<{
|
|
2076
|
+
eventIds: z.ZodArray<z.ZodString>;
|
|
2077
|
+
ackUrl: z.ZodOptional<z.ZodString>;
|
|
2078
|
+
}, z.core.$strip>;
|
package/dist/core/schemas.js
CHANGED
|
@@ -1044,3 +1044,52 @@ export const uploadAttachmentInput = z.object({
|
|
|
1044
1044
|
export const getBatchViewUrlInput = z.object({
|
|
1045
1045
|
batchInvocationId: z.string().min(1),
|
|
1046
1046
|
});
|
|
1047
|
+
export const botRoleSchema = z.enum(["QA_LEAD", "PM", "QA_ENGINEER", "DEVELOPER"]);
|
|
1048
|
+
export const botCapabilitySchema = z.enum([
|
|
1049
|
+
"REQUIREMENTS_UPDATE",
|
|
1050
|
+
"E2E_AUTHORING",
|
|
1051
|
+
"ISSUE_FIX",
|
|
1052
|
+
"MANUAL_TEST_COORDINATION",
|
|
1053
|
+
"TEST_BATCH_FIX",
|
|
1054
|
+
"QA_POSTURE",
|
|
1055
|
+
]);
|
|
1056
|
+
export const botSubscriptionFilterSchema = z.object({
|
|
1057
|
+
/** Payload filter field, e.g. author / assignee / adder. */
|
|
1058
|
+
field: z.string().min(1),
|
|
1059
|
+
op: z.literal("eq").default("eq"),
|
|
1060
|
+
/** Literal value, or the token `me` (resolved to the bot's user). */
|
|
1061
|
+
value: z.string().min(1),
|
|
1062
|
+
});
|
|
1063
|
+
export const botSubscriptionSchema = z.object({
|
|
1064
|
+
/** Wire event type, e.g. git-push, issue-assigned, e2e-batch-completed. */
|
|
1065
|
+
eventType: z.string().min(1),
|
|
1066
|
+
filters: z.array(botSubscriptionFilterSchema).optional(),
|
|
1067
|
+
});
|
|
1068
|
+
export const registerBotProfileInput = z.object({
|
|
1069
|
+
/** Defaults to the bot-id header (TESTCHIMP_BOT_ID) or the OAuth token's bot. */
|
|
1070
|
+
botId: z.string().min(1).max(64).optional(),
|
|
1071
|
+
role: botRoleSchema,
|
|
1072
|
+
responsibilities: z.string().optional(),
|
|
1073
|
+
capabilities: z.array(botCapabilitySchema).default([]),
|
|
1074
|
+
subscriptions: z.array(botSubscriptionSchema).default([]),
|
|
1075
|
+
});
|
|
1076
|
+
export const getBotProfileInput = z.object({
|
|
1077
|
+
botId: z.string().min(1).max(64).optional(),
|
|
1078
|
+
});
|
|
1079
|
+
export const getMyTasksInput = z.object({
|
|
1080
|
+
/** Required with an API key; ignored with an OAuth token (the token's user is used). */
|
|
1081
|
+
userId: z.string().min(1).optional(),
|
|
1082
|
+
});
|
|
1083
|
+
export const listTestsAwaitingVerificationInput = z.object({
|
|
1084
|
+
userId: z.string().min(1).optional(),
|
|
1085
|
+
limit: z.number().int().positive().max(500).optional(),
|
|
1086
|
+
});
|
|
1087
|
+
export const MAX_BOT_ACK_EVENT_IDS = 100;
|
|
1088
|
+
export const ackBotEventsInput = z.object({
|
|
1089
|
+
eventIds: z
|
|
1090
|
+
.array(z.string().regex(/^[\x20-\x7e]+$/, "eventIds must be printable ASCII").min(1))
|
|
1091
|
+
.min(1)
|
|
1092
|
+
.max(MAX_BOT_ACK_EVENT_IDS),
|
|
1093
|
+
/** Webhook delivery ackUrl; defaults to ${TESTCHIMP_INGRESS_URL}/bot/events/ack. */
|
|
1094
|
+
ackUrl: z.string().min(1).optional(),
|
|
1095
|
+
});
|
package/dist/core/tools.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { type ZodTypeAny } from "zod";
|
|
2
|
-
import type
|
|
2
|
+
import { type PostIngressFn, type PostMcpFn } from "./client.js";
|
|
3
3
|
import { type ProgressLog } from "./ephemeralWait.js";
|
|
4
4
|
export interface ToolContext {
|
|
5
5
|
postMcp: PostMcpFn;
|
|
6
|
+
/** Ingress poster (bot acks); defaults to the env-configured ingress client. */
|
|
7
|
+
postIngress?: PostIngressFn;
|
|
6
8
|
onProgress?: ProgressLog;
|
|
7
9
|
}
|
|
10
|
+
export declare const BOT_ACK_PATH = "/bot/events/ack";
|
|
11
|
+
export declare const BOT_ACK_UNSUPPORTED_MESSAGE: string;
|
|
8
12
|
export interface ToolDefinition {
|
|
9
13
|
kebab: string;
|
|
10
14
|
description: string;
|
package/dist/core/tools.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { normalizeScope } from "./normalize.js";
|
|
2
|
+
import { postIngress, TestChimpHttpError } from "./client.js";
|
|
2
3
|
import { runProvisionEphemeralEnvironmentAndWait } from "./ephemeralWait.js";
|
|
3
4
|
import * as S from "./schemas.js";
|
|
4
5
|
import { resolveGitHeadSha } from "./gitSha.js";
|
|
5
6
|
import { parseDateBoundToMillis } from "./dates.js";
|
|
6
7
|
import { buildAgentTraceabilityPayload, resolveToolchainVersions } from "./agentTraceability.js";
|
|
8
|
+
export const BOT_ACK_PATH = "/bot/events/ack";
|
|
9
|
+
export const BOT_ACK_UNSUPPORTED_MESSAGE = "This TestChimp deployment does not support bot acks yet (ingress returned 404 for /bot/events/ack). " +
|
|
10
|
+
"Upgrade the TestChimp platform, or ask your admin to enable QA bots.";
|
|
7
11
|
function platformToProtoEnum(platform) {
|
|
8
12
|
switch (platform) {
|
|
9
13
|
case "ios":
|
|
@@ -1618,6 +1622,112 @@ export const TOOL_DEFINITIONS = [
|
|
|
1618
1622
|
});
|
|
1619
1623
|
},
|
|
1620
1624
|
},
|
|
1625
|
+
{
|
|
1626
|
+
kebab: "get-my-tasks",
|
|
1627
|
+
description: "List a team member's open QA work in this project: manual test scenarios assigned to them in test runs " +
|
|
1628
|
+
"(with testRunUrl), issues assigned to them (status, severity, due date, issueUrl), and SmartTests they " +
|
|
1629
|
+
"authored that still await verification. With an OAuth token the user is the token's user; with an API key " +
|
|
1630
|
+
"pass userId. Use for daily reminders and 'what should I work on' questions.",
|
|
1631
|
+
inputSchema: S.getMyTasksInput,
|
|
1632
|
+
execute: async (args, { postMcp }) => {
|
|
1633
|
+
const a = args;
|
|
1634
|
+
return postMcp("/api/mcp/get_my_tasks", a.userId ? { userId: a.userId.trim() } : {});
|
|
1635
|
+
},
|
|
1636
|
+
},
|
|
1637
|
+
{
|
|
1638
|
+
kebab: "list-tests-awaiting-verification",
|
|
1639
|
+
description: "List SmartTests whose latest executions still need human verification before they earn a verified badge " +
|
|
1640
|
+
"(testId, testName, filePath, scenarioId / scenarioOrdinalId / scenarioTitle, verificationStatus NOT_VERIFIED " +
|
|
1641
|
+
"or VERIFIED_STALE, reportedAtMillis, workflowExecutionId). " +
|
|
1642
|
+
"Optional userId narrows to tests authored for that user (defaults to the OAuth token's user); optional limit. " +
|
|
1643
|
+
"Use after writing E2E tests to prompt the user to verify the executions.",
|
|
1644
|
+
inputSchema: S.listTestsAwaitingVerificationInput,
|
|
1645
|
+
execute: async (args, { postMcp }) => {
|
|
1646
|
+
const a = args;
|
|
1647
|
+
const body = {};
|
|
1648
|
+
if (a.userId)
|
|
1649
|
+
body.userId = a.userId.trim();
|
|
1650
|
+
if (a.limit != null)
|
|
1651
|
+
body.limit = a.limit;
|
|
1652
|
+
return postMcp("/api/mcp/list_tests_awaiting_verification", body);
|
|
1653
|
+
},
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
kebab: "get-qa-posture",
|
|
1657
|
+
description: "Project-wide QA posture snapshot: releases (version, lifecycleStatus, due date, passed / failed / blocked / " +
|
|
1658
|
+
"not-attempted counts), issues (active, inProgress, blocked, openBySeverity), activeTestRuns with result " +
|
|
1659
|
+
"counts, and testsAwaitingVerificationCount. Use for weekly " +
|
|
1660
|
+
"QA digests and release-health questions; summarise for the reader's role rather than dumping raw JSON.",
|
|
1661
|
+
inputSchema: S.emptyInput,
|
|
1662
|
+
execute: async (_args, { postMcp }) => postMcp("/api/mcp/get_qa_posture", {}),
|
|
1663
|
+
},
|
|
1664
|
+
{
|
|
1665
|
+
kebab: "get-bot-compat",
|
|
1666
|
+
description: "Return the minimum testchimp skill version, minimum @testchimp/cli version, and the bot event schema " +
|
|
1667
|
+
"version this TestChimp deployment requires from QA bots. Call on bot startup / daily; if the local skill or " +
|
|
1668
|
+
"CLI is older, ask the user to approve an upgrade.",
|
|
1669
|
+
inputSchema: S.emptyInput,
|
|
1670
|
+
execute: async (_args, { postMcp }) => postMcp("/api/mcp/get_bot_compat", {}),
|
|
1671
|
+
},
|
|
1672
|
+
{
|
|
1673
|
+
kebab: "get-bot-profile",
|
|
1674
|
+
description: "Fetch this QA bot's registration: botId, project, the team member it represents (userId), platform, status, " +
|
|
1675
|
+
"role, responsibilities, capabilities, event subscriptions, paused flag, and webhook health. botId defaults to " +
|
|
1676
|
+
"the bot-id header (TESTCHIMP_BOT_ID) or the OAuth token's bot. Use to confirm identity before onboarding and " +
|
|
1677
|
+
"to check paused / capabilities before running routines.",
|
|
1678
|
+
inputSchema: S.getBotProfileInput,
|
|
1679
|
+
execute: async (args, { postMcp }) => {
|
|
1680
|
+
const a = args;
|
|
1681
|
+
return postMcp("/api/mcp/get_bot_profile", a.botId ? { botId: a.botId } : {});
|
|
1682
|
+
},
|
|
1683
|
+
},
|
|
1684
|
+
{
|
|
1685
|
+
kebab: "register-bot-profile",
|
|
1686
|
+
description: "Register or replace this QA bot's profile and event subscriptions atomically (mutating — confirm with the user " +
|
|
1687
|
+
"first). role: QA_LEAD | PM | QA_ENGINEER | DEVELOPER. responsibilities: the user's own words. capabilities: " +
|
|
1688
|
+
"REQUIREMENTS_UPDATE, E2E_AUTHORING, ISSUE_FIX, MANUAL_TEST_COORDINATION, TEST_BATCH_FIX, QA_POSTURE. " +
|
|
1689
|
+
"subscriptions: [{eventType, filters:[{field, op:'eq', value}]}] where value may be 'me' (e.g. git-push " +
|
|
1690
|
+
"author=me, issue-assigned assignee=me, e2e-batch-completed). Derive subscriptions from capabilities per the " +
|
|
1691
|
+
"testchimp skill's bot onboarding guide. botId defaults to the bot-id header / OAuth token.",
|
|
1692
|
+
inputSchema: S.registerBotProfileInput,
|
|
1693
|
+
execute: async (args, { postMcp }) => {
|
|
1694
|
+
const a = args;
|
|
1695
|
+
const body = {
|
|
1696
|
+
role: a.role,
|
|
1697
|
+
responsibilities: a.responsibilities?.trim() ?? "",
|
|
1698
|
+
capabilities: a.capabilities,
|
|
1699
|
+
subscriptions: a.subscriptions.map((s) => ({
|
|
1700
|
+
eventType: s.eventType.trim(),
|
|
1701
|
+
filters: (s.filters ?? []).map((f) => ({ field: f.field.trim(), op: f.op, value: f.value.trim() })),
|
|
1702
|
+
})),
|
|
1703
|
+
};
|
|
1704
|
+
if (a.botId)
|
|
1705
|
+
body.botId = a.botId;
|
|
1706
|
+
return postMcp("/bots/register_profile", body);
|
|
1707
|
+
},
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
kebab: "ack-bot-events",
|
|
1711
|
+
description: "Acknowledge QA-bot webhook events by eventId (1-100 per call) so TestChimp stops redelivering them. Ack every " +
|
|
1712
|
+
"event you handled or decided to ignore (including expired / irrelevant ones); re-acking is safe. Pass the " +
|
|
1713
|
+
"delivery's ackUrl when present (defaults to the ingress /bot/events/ack). Returns per-id status: " +
|
|
1714
|
+
"BOT_ACK_ACCEPTED / BOT_ACK_ACKED / BOT_ACK_ALREADY_ACKED / BOT_ACK_EXPIRED_RECORDED are fine; " +
|
|
1715
|
+
"BOT_ACK_UNKNOWN_EVENT / BOT_ACK_NOT_A_TARGET / BOT_ACK_MISSING_BOT_ID indicate a wrong id or bot identity.",
|
|
1716
|
+
inputSchema: S.ackBotEventsInput,
|
|
1717
|
+
execute: async (args, ctx) => {
|
|
1718
|
+
const a = args;
|
|
1719
|
+
const post = ctx.postIngress ?? postIngress;
|
|
1720
|
+
const eventIds = Array.from(new Set(a.eventIds));
|
|
1721
|
+
try {
|
|
1722
|
+
return await post(a.ackUrl?.trim() || BOT_ACK_PATH, { eventIds });
|
|
1723
|
+
}
|
|
1724
|
+
catch (e) {
|
|
1725
|
+
if (e instanceof TestChimpHttpError && e.status === 404)
|
|
1726
|
+
throw new Error(BOT_ACK_UNSUPPORTED_MESSAGE);
|
|
1727
|
+
throw e;
|
|
1728
|
+
}
|
|
1729
|
+
},
|
|
1730
|
+
},
|
|
1621
1731
|
];
|
|
1622
1732
|
const TOOL_BY_KEBAB = new Map(TOOL_DEFINITIONS.map((t) => [t.kebab, t]));
|
|
1623
1733
|
export function getToolDefinition(kebab) {
|