@jrpool/kilotest 62.0.0 → 63.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/README.md +1 -0
- package/api/awaitTest.test.ts +9 -0
- package/api/getReport.test.ts +9 -0
- package/api/listDiagnoses.test.ts +9 -0
- package/api/listIssues.test.ts +9 -0
- package/api/listPages.test.ts +9 -0
- package/api/listViolators.test.ts +9 -0
- package/api/orderNewTest.test.ts +9 -0
- package/api/orderRetest.test.ts +9 -0
- package/api/reportDisagreement.test.ts +9 -0
- package/api/requestFeature.test.ts +9 -0
- package/api/requestNewTest.test.ts +9 -0
- package/api/requestRetest.test.ts +9 -0
- package/api/schemas.ts +4 -2
- package/api/util.test.ts +9 -0
- package/docs/AI-TOOLS.md +1 -0
- package/index.test.ts +66 -0
- package/index.ts +16 -1
- package/lhm.plugin.json +121 -25
- package/openapi-3.0.yaml +9 -3
- package/openapi.yaml +9 -3
- package/package.json +1 -1
- package/server.json +11 -2
- package/test/setup.ts +4 -0
- package/web/tutorialAI/index.html +29 -1
package/README.md
CHANGED
|
@@ -97,5 +97,6 @@ When you **make an API or tool contract change** in a feature branch:
|
|
|
97
97
|
8. Wait until `npmjs.com` has processed the publication.
|
|
98
98
|
9. Use `git pull`, `npm ci`, and `pm2 restart kilotest` (but **not** `npm audit fix`) to deploy the revised `main` branch on the public server.
|
|
99
99
|
10. Visit the [RapidAPI Studio](https://rapidapi.com/studio) and update the API definition at `Hub Listing` → `Definitions` → `CI/CD` → `Import OpenAPI` by uploading the regenerated `openapi-3.0.yaml`.
|
|
100
|
+
11. Visit the [Claude directory](https://claude.ai/directory/manage/kilotest?tab=edit), edit any changed entries, and submit the changes.
|
|
100
101
|
|
|
101
102
|
If any step fails, see the details in `docs/AI-TOOLS.md` for troubleshooting instructions.
|
package/api/awaitTest.test.ts
CHANGED
|
@@ -29,6 +29,7 @@ before(() => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
import {response} from './awaitTest.ts';
|
|
32
|
+
import {awaitTestResponseSchema} from './schemas.ts';
|
|
32
33
|
|
|
33
34
|
after(() => {
|
|
34
35
|
delete process.env.AWAIT_JOB_POLL_MS;
|
|
@@ -112,3 +113,11 @@ test('awaitTest reports both orderNewTest and orderRetest as possible ancestors'
|
|
|
112
113
|
assert.equal(ancestor.method, 'POST');
|
|
113
114
|
}
|
|
114
115
|
});
|
|
116
|
+
|
|
117
|
+
test('awaitTest response conforms to its response schema', async () => {
|
|
118
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
119
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
120
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
121
|
+
const body = await response(['999999T9999-xyz']);
|
|
122
|
+
assert.deepEqual(awaitTestResponseSchema.parse(body), body);
|
|
123
|
+
});
|
package/api/getReport.test.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import {test, before, after} from 'node:test';
|
|
9
9
|
import assert from 'node:assert/strict';
|
|
10
10
|
import {response} from './getReport.ts';
|
|
11
|
+
import {getReportResponseSchema} from './schemas.ts';
|
|
11
12
|
|
|
12
13
|
// SETUP AND TEARDOWN
|
|
13
14
|
|
|
@@ -59,3 +60,11 @@ test('getReport includes tool name and metadata', async () => {
|
|
|
59
60
|
assert.equal(body['tool name'], 'getReport');
|
|
60
61
|
assert.ok(body['response metadata'].identifier);
|
|
61
62
|
});
|
|
63
|
+
|
|
64
|
+
test('getReport response conforms to its response schema', async () => {
|
|
65
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
66
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
67
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
68
|
+
const body = await response(['260101T0000-mix']);
|
|
69
|
+
assert.deepEqual(getReportResponseSchema.parse(body), body);
|
|
70
|
+
});
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import {test, before, after} from 'node:test';
|
|
9
9
|
import assert from 'node:assert/strict';
|
|
10
10
|
import {response} from './listDiagnoses.ts';
|
|
11
|
+
import {listDiagnosesResponseSchema} from './schemas.ts';
|
|
11
12
|
|
|
12
13
|
// SETUP AND TEARDOWN
|
|
13
14
|
|
|
@@ -129,3 +130,11 @@ test('listDiagnoses returns issue error for a falsy issue ID', async () => {
|
|
|
129
130
|
const issueBasics = body['response content']['basics about the issue'] as any;
|
|
130
131
|
assert.ok(issueBasics.error);
|
|
131
132
|
});
|
|
133
|
+
|
|
134
|
+
test('listDiagnoses response conforms to its response schema', async () => {
|
|
135
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
136
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
137
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
138
|
+
const body = await response(['0', 'linkNoText', '260101T0000-mix']);
|
|
139
|
+
assert.deepEqual(listDiagnosesResponseSchema.parse(body), body);
|
|
140
|
+
});
|
package/api/listIssues.test.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import {test, before, after} from 'node:test';
|
|
9
9
|
import assert from 'node:assert/strict';
|
|
10
10
|
import {response} from './listIssues.ts';
|
|
11
|
+
import {listIssuesResponseSchema} from './schemas.ts';
|
|
11
12
|
|
|
12
13
|
// SETUP AND TEARDOWN
|
|
13
14
|
|
|
@@ -145,3 +146,11 @@ test('listIssues gives no history for a nonexistent report', async () => {
|
|
|
145
146
|
const body = await response(['999999T9999-xyz']);
|
|
146
147
|
assert.equal(body['response content']['history of reports about the page'], null);
|
|
147
148
|
});
|
|
149
|
+
|
|
150
|
+
test('listIssues response conforms to its response schema', async () => {
|
|
151
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
152
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
153
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
154
|
+
const body = await response(['260101T0000-mix']);
|
|
155
|
+
assert.deepEqual(listIssuesResponseSchema.parse(body), body);
|
|
156
|
+
});
|
package/api/listPages.test.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import {test, before, after} from 'node:test';
|
|
9
9
|
import assert from 'node:assert/strict';
|
|
10
10
|
import {response} from './listPages.ts';
|
|
11
|
+
import {listPagesResponseSchema} from './schemas.ts';
|
|
11
12
|
|
|
12
13
|
// SETUP AND TEARDOWN
|
|
13
14
|
|
|
@@ -91,3 +92,11 @@ test('listPages includes tool name and metadata', async () => {
|
|
|
91
92
|
assert.ok(body['response metadata'].identifier);
|
|
92
93
|
assert.ok(body['response metadata']['date and time']);
|
|
93
94
|
});
|
|
95
|
+
|
|
96
|
+
test('listPages response conforms to its response schema', async () => {
|
|
97
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
98
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
99
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
100
|
+
const body = await response();
|
|
101
|
+
assert.deepEqual(listPagesResponseSchema.parse(body), body);
|
|
102
|
+
});
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import {test, before, after} from 'node:test';
|
|
9
9
|
import assert from 'node:assert/strict';
|
|
10
10
|
import {response} from './listViolators.ts';
|
|
11
|
+
import {listViolatorsResponseSchema} from './schemas.ts';
|
|
11
12
|
|
|
12
13
|
// SETUP AND TEARDOWN
|
|
13
14
|
|
|
@@ -105,3 +106,11 @@ test('listViolators returns an error for a falsy issue ID', async () => {
|
|
|
105
106
|
const issueBasics = body['response content']['basics about the issue'] as any;
|
|
106
107
|
assert.ok(issueBasics.error);
|
|
107
108
|
});
|
|
109
|
+
|
|
110
|
+
test('listViolators response conforms to its response schema', async () => {
|
|
111
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
112
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
113
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
114
|
+
const body = await response(['linkNoText', '260101T0000-mix']);
|
|
115
|
+
assert.deepEqual(listViolatorsResponseSchema.parse(body), body);
|
|
116
|
+
});
|
package/api/orderNewTest.test.ts
CHANGED
|
@@ -45,6 +45,7 @@ beforeEach(async () => {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
import {response} from './orderNewTest.ts';
|
|
48
|
+
import {orderNewTestResponseSchema} from './schemas.ts';
|
|
48
49
|
|
|
49
50
|
after(() => {
|
|
50
51
|
console.log = originalLog;
|
|
@@ -210,3 +211,11 @@ test('orderNewTest response omits a web UI URL for this request but includes the
|
|
|
210
211
|
assert.equal(similarWeb['this request'], null);
|
|
211
212
|
assert.ok(similarWeb['closest ancestor request'].includes('listPages.html'));
|
|
212
213
|
});
|
|
214
|
+
|
|
215
|
+
test('orderNewTest response conforms to its response schema', async () => {
|
|
216
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
217
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
218
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
219
|
+
const body = await response(['Schema Conformance Ordered Page', 'https://example.com/schema-ordered', 'A reason that is long enough.']);
|
|
220
|
+
assert.deepEqual(orderNewTestResponseSchema.parse(body), body);
|
|
221
|
+
});
|
package/api/orderRetest.test.ts
CHANGED
|
@@ -48,6 +48,7 @@ beforeEach(async () => {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
import {response} from './orderRetest.ts';
|
|
51
|
+
import {orderRetestResponseSchema} from './schemas.ts';
|
|
51
52
|
|
|
52
53
|
after(async () => {
|
|
53
54
|
await fs.unlink(retestFixturePath);
|
|
@@ -177,3 +178,11 @@ test('orderRetest response omits a web UI URL for this request but includes the
|
|
|
177
178
|
assert.equal(similarWeb['this request'], null);
|
|
178
179
|
assert.ok(similarWeb['closest ancestor request'].includes('listIssues.html'));
|
|
179
180
|
});
|
|
181
|
+
|
|
182
|
+
test('orderRetest response conforms to its response schema', async () => {
|
|
183
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
184
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
185
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
186
|
+
const body = await response(['260101T0004-ord', 'A reason that is long enough.']);
|
|
187
|
+
assert.deepEqual(orderRetestResponseSchema.parse(body), body);
|
|
188
|
+
});
|
|
@@ -42,6 +42,7 @@ beforeEach(async () => {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
import {response} from './reportDisagreement.ts';
|
|
45
|
+
import {reportDisagreementResponseSchema} from './schemas.ts';
|
|
45
46
|
|
|
46
47
|
after(async () => {
|
|
47
48
|
console.log = originalLog;
|
|
@@ -206,3 +207,11 @@ test('reportDisagreement includes tool name and metadata', async () => {
|
|
|
206
207
|
assert.equal(body['tool name'], 'reportDisagreement');
|
|
207
208
|
assert.ok(body['response metadata'].identifier);
|
|
208
209
|
});
|
|
210
|
+
|
|
211
|
+
test('reportDisagreement response conforms to its response schema', async () => {
|
|
212
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
213
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
214
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
215
|
+
const body = await response(['linkNoText', '260101T0000-mix', '0', 'axe', 'r11', 'A reason submitted only for schema conformance.']);
|
|
216
|
+
assert.deepEqual(reportDisagreementResponseSchema.parse(body), body);
|
|
217
|
+
});
|
|
@@ -37,6 +37,7 @@ beforeEach(async () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
import {response} from './requestFeature.ts';
|
|
40
|
+
import {requestFeatureResponseSchema} from './schemas.ts';
|
|
40
41
|
|
|
41
42
|
after(async () => {
|
|
42
43
|
console.log = originalLog;
|
|
@@ -186,3 +187,11 @@ test('requestFeature accepts a feature request that differs from an existing one
|
|
|
186
187
|
const featureRequests = JSON.parse(await fs.readFile(process.env.FEATURE_REQUESTS_PATH!, 'utf8'));
|
|
187
188
|
assert.equal(featureRequests.length, 2);
|
|
188
189
|
});
|
|
190
|
+
|
|
191
|
+
test('requestFeature response conforms to its response schema', async () => {
|
|
192
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
193
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
194
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
195
|
+
const body = await response(['A feature request submitted only for schema conformance.']);
|
|
196
|
+
assert.deepEqual(requestFeatureResponseSchema.parse(body), body);
|
|
197
|
+
});
|
|
@@ -42,6 +42,7 @@ beforeEach(async () => {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
import {response} from './requestNewTest.ts';
|
|
45
|
+
import {requestNewTestResponseSchema} from './schemas.ts';
|
|
45
46
|
|
|
46
47
|
after(() => {
|
|
47
48
|
console.log = originalLog;
|
|
@@ -208,3 +209,11 @@ test('requestNewTest rejects a request with a fallback channel when the queue is
|
|
|
208
209
|
assert.ok(disposition['what happens next'].includes('https://github.com/jrpool/kilotest/issues'));
|
|
209
210
|
assert.ok(disposition['what happens next'].includes('info@kilotest.com'));
|
|
210
211
|
});
|
|
212
|
+
|
|
213
|
+
test('requestNewTest response conforms to its response schema', async () => {
|
|
214
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
215
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
216
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
217
|
+
const body = await response(['Schema Conformance Page', 'https://example.com/schema-conformance', 'A reason that is long enough.']);
|
|
218
|
+
assert.deepEqual(requestNewTestResponseSchema.parse(body), body);
|
|
219
|
+
});
|
|
@@ -56,6 +56,7 @@ beforeEach(async () => {
|
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
import {response} from './requestRetest.ts';
|
|
59
|
+
import {requestRetestResponseSchema} from './schemas.ts';
|
|
59
60
|
|
|
60
61
|
after(async () => {
|
|
61
62
|
console.log = originalLog;
|
|
@@ -193,3 +194,11 @@ test('requestRetest rejects a request with a fallback channel when the queue is
|
|
|
193
194
|
assert.ok(disposition['what happens next'].includes('https://github.com/jrpool/kilotest/issues'));
|
|
194
195
|
assert.ok(disposition['what happens next'].includes('info@kilotest.com'));
|
|
195
196
|
});
|
|
197
|
+
|
|
198
|
+
test('requestRetest response conforms to its response schema', async () => {
|
|
199
|
+
// parse() catches missing or mistyped keys; comparing the parsed result to the
|
|
200
|
+
// original catches extra keys that zod strips but that the strict JSON schema
|
|
201
|
+
// advertised to MCP clients (additionalProperties: false) rejects.
|
|
202
|
+
const body = await response(['260101T0003-ret', 'A reason that is long enough.']);
|
|
203
|
+
assert.deepEqual(requestRetestResponseSchema.parse(body), body);
|
|
204
|
+
});
|
package/api/schemas.ts
CHANGED
|
@@ -95,7 +95,7 @@ const ruleEngineFactsSchema = z.object({
|
|
|
95
95
|
}).meta({id: 'RuleEngineFacts'});
|
|
96
96
|
|
|
97
97
|
// Facts about the Kilotest tool collection, included in every response.
|
|
98
|
-
const toolsFactsSchema = z.object({
|
|
98
|
+
export const toolsFactsSchema = z.object({
|
|
99
99
|
name: z.string(),
|
|
100
100
|
description: z.object({
|
|
101
101
|
'what Kilotest does': z.string(),
|
|
@@ -108,8 +108,10 @@ const toolsFactsSchema = z.object({
|
|
|
108
108
|
'how to generate more findings': z.object({
|
|
109
109
|
'new testing': z.string(),
|
|
110
110
|
'retesting': z.string(),
|
|
111
|
+
'approval': z.string(),
|
|
111
112
|
'latency': z.string(),
|
|
112
|
-
'confirmation': z.string()
|
|
113
|
+
'request confirmation': z.string(),
|
|
114
|
+
'order confirmation': z.string()
|
|
113
115
|
})
|
|
114
116
|
}).describe('Explanation of what Kilotest does and how to use its tools.'),
|
|
115
117
|
URL: z.string().describe('URL of the Kilotest MCP server.'),
|
package/api/util.test.ts
CHANGED
|
@@ -17,6 +17,7 @@ for (const key of ['MANAGER_EMAIL', 'ALERT_API_HOST', 'ALERT_API_PATH', 'ALERT_A
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
import {getReportBasics, getResponseMetadata, getRuleEngineFacts, getRuleEnginesFacts, getIssueSpec, getToolsFacts, getRequestFailureReason} from './util.ts';
|
|
20
|
+
import {toolsFactsSchema} from './schemas.ts';
|
|
20
21
|
import {processTestRequest} from '../util.ts';
|
|
21
22
|
|
|
22
23
|
// SETUP
|
|
@@ -137,6 +138,14 @@ test('getToolsFacts returns facts about the Kilotest tool collection', () => {
|
|
|
137
138
|
assert.ok(facts.description['how to generate more findings']);
|
|
138
139
|
});
|
|
139
140
|
|
|
141
|
+
test('getToolsFacts output conforms to toolsFactsSchema', () => {
|
|
142
|
+
// parse() catches missing or mistyped keys, and comparing the parsed result to
|
|
143
|
+
// the original catches extra keys that zod strips but that the strict JSON
|
|
144
|
+
// schema advertised to MCP clients (additionalProperties: false) rejects.
|
|
145
|
+
const facts = getToolsFacts();
|
|
146
|
+
assert.deepEqual(toolsFactsSchema.parse(facts), facts);
|
|
147
|
+
});
|
|
148
|
+
|
|
140
149
|
test('getRequestFailureReason returns the URL-length reason for a urlLength result', () => {
|
|
141
150
|
// processTestRequest can return urlLength for a new-test request whose URL is
|
|
142
151
|
// outside the 12-to-300-character bound. No current caller reaches this
|
package/docs/AI-TOOLS.md
CHANGED
|
@@ -49,6 +49,7 @@ The external features that support the use of Kilotest as a collection of AI too
|
|
|
49
49
|
- Registration of Kilotest with [Awesome MCP Servers](https://www.tensorblock.co/mcp/servers/github-jrpool-kilotest-f518ba36).
|
|
50
50
|
- Registration of Kilotest with the [RapidAPI](https://rapidapi.com/jrpool/api/kilotest/playground/apiendpoint_0f03577a-ff9a-472a-a0ed-533bd198981a) Hub.
|
|
51
51
|
- Registration of Kilotest with the [LobeHub MCP Market](https://lobehub.com/mcp) (identifier: `jrpool-kilotest`), enabled with an `lhm.plugin.json` file.
|
|
52
|
+
- Registration of Kilotest with [MCPTry](https://mcptry.com/servers/io.github.jrpool/kilotest).
|
|
52
53
|
- Deployment of an MCP server in HTTP mode on the Kilotest service host.
|
|
53
54
|
- Submission of the MCP server to the [Claude directory of plugins and connectors](https://claude.ai/directory/manage).
|
|
54
55
|
- Configuration of Claude Desktop on the local development host and the `claude.ai` web application to connect Claude Desktop models to the Kilotest MCP server. The configuration was performed in the UI of each platform with the addition of Kilotest as a _connector_. The user used the `Customize/Connectors/Add connector/Add custom connector` interface, providing these data before activating the `Add` button:
|
package/index.test.ts
CHANGED
|
@@ -2334,6 +2334,50 @@ test('startServer starts an HTTPS server when protocol is https', {timeout: 2000
|
|
|
2334
2334
|
}
|
|
2335
2335
|
});
|
|
2336
2336
|
|
|
2337
|
+
test('startServer throws when THIS_KILOTEST_HOST is unset', async () => {
|
|
2338
|
+
const savedHost = process.env.THIS_KILOTEST_HOST;
|
|
2339
|
+
const savedProtocol = process.env.PROTOCOL;
|
|
2340
|
+
delete process.env.THIS_KILOTEST_HOST;
|
|
2341
|
+
delete process.env.PROTOCOL;
|
|
2342
|
+
try {
|
|
2343
|
+
await assert.rejects(startServer(), /THIS_KILOTEST_HOST/);
|
|
2344
|
+
}
|
|
2345
|
+
finally {
|
|
2346
|
+
if (savedHost !== undefined) {
|
|
2347
|
+
process.env.THIS_KILOTEST_HOST = savedHost;
|
|
2348
|
+
}
|
|
2349
|
+
if (savedProtocol !== undefined) {
|
|
2350
|
+
process.env.PROTOCOL = savedProtocol;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
});
|
|
2354
|
+
|
|
2355
|
+
test('startServer throws when PROTOCOL is https and KEY and CERT are unset', async () => {
|
|
2356
|
+
const savedProtocol = process.env.PROTOCOL;
|
|
2357
|
+
const savedKey = process.env.KEY;
|
|
2358
|
+
const savedCert = process.env.CERT;
|
|
2359
|
+
process.env.PROTOCOL = 'https';
|
|
2360
|
+
delete process.env.KEY;
|
|
2361
|
+
delete process.env.CERT;
|
|
2362
|
+
try {
|
|
2363
|
+
await assert.rejects(startServer(), /KEY.*CERT|CERT.*KEY/);
|
|
2364
|
+
}
|
|
2365
|
+
finally {
|
|
2366
|
+
if (savedProtocol !== undefined) {
|
|
2367
|
+
process.env.PROTOCOL = savedProtocol;
|
|
2368
|
+
}
|
|
2369
|
+
else {
|
|
2370
|
+
delete process.env.PROTOCOL;
|
|
2371
|
+
}
|
|
2372
|
+
if (savedKey !== undefined) {
|
|
2373
|
+
process.env.KEY = savedKey;
|
|
2374
|
+
}
|
|
2375
|
+
if (savedCert !== undefined) {
|
|
2376
|
+
process.env.CERT = savedCert;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
});
|
|
2380
|
+
|
|
2337
2381
|
test('runIfMain calls startServer when mainModule matches currentModule', async () => {
|
|
2338
2382
|
let called = false;
|
|
2339
2383
|
const starter = async () => {
|
|
@@ -2358,3 +2402,25 @@ test('runIfMain does not call startServer when mainModule differs from currentMo
|
|
|
2358
2402
|
await new Promise<void>(resolve => setImmediate(resolve));
|
|
2359
2403
|
assert.equal(called, false);
|
|
2360
2404
|
});
|
|
2405
|
+
|
|
2406
|
+
test('runIfMain logs the error and sets a failing exit code when the starter rejects', async () => {
|
|
2407
|
+
const savedExitCode = process.exitCode;
|
|
2408
|
+
const logged: string[] = [];
|
|
2409
|
+
const originalLog = console.log;
|
|
2410
|
+
console.log = (line: string) => logged.push(line);
|
|
2411
|
+
const starter = async () => {
|
|
2412
|
+
throw new Error('injected startup failure');
|
|
2413
|
+
};
|
|
2414
|
+
const fakeModule = {};
|
|
2415
|
+
try {
|
|
2416
|
+
runIfMain(fakeModule, fakeModule, starter);
|
|
2417
|
+
// Wait for the rejection handler's microtasks to run.
|
|
2418
|
+
await new Promise<void>(resolve => setImmediate(resolve));
|
|
2419
|
+
assert.ok(logged.some(line => line.includes('injected startup failure')));
|
|
2420
|
+
assert.equal(process.exitCode, 1);
|
|
2421
|
+
}
|
|
2422
|
+
finally {
|
|
2423
|
+
console.log = originalLog;
|
|
2424
|
+
process.exitCode = savedExitCode;
|
|
2425
|
+
}
|
|
2426
|
+
});
|
package/index.ts
CHANGED
|
@@ -1529,7 +1529,19 @@ export {serve};
|
|
|
1529
1529
|
|
|
1530
1530
|
// Starts the server using the configured protocol and credentials.
|
|
1531
1531
|
export const startServer = async () => {
|
|
1532
|
+
// Fail fast on missing required environment variables. THIS_KILOTEST_HOST is read raw
|
|
1533
|
+
// by getThisHost and interpolated into URLs in every response; KEY and CERT are
|
|
1534
|
+
// required only for HTTPS. Other env vars have documented defaults or designed
|
|
1535
|
+
// degradation (see env.example and docs/SERVICE.md) and are deliberately not required.
|
|
1536
|
+
const requiredEnvVars = ['THIS_KILOTEST_HOST'];
|
|
1532
1537
|
const startProtocol = process.env.PROTOCOL || 'http';
|
|
1538
|
+
if (startProtocol === 'https') {
|
|
1539
|
+
requiredEnvVars.push('KEY', 'CERT');
|
|
1540
|
+
}
|
|
1541
|
+
const missingEnvVars = requiredEnvVars.filter(name => !process.env[name]);
|
|
1542
|
+
if (missingEnvVars.length) {
|
|
1543
|
+
throw new Error(`Missing required environment variable(s): ${missingEnvVars.join(', ')}`);
|
|
1544
|
+
}
|
|
1533
1545
|
if (startProtocol === 'http') {
|
|
1534
1546
|
console.log('Starting HTTP server');
|
|
1535
1547
|
return serve(http, {});
|
|
@@ -1545,7 +1557,10 @@ export const startServer = async () => {
|
|
|
1545
1557
|
// Runs the server if the module was loaded directly (not required by a test). The starter is a parameter so tests can inject a spy, since ESM module exports cannot be monkey-patched.
|
|
1546
1558
|
export const runIfMain = (mainModule: unknown, currentModule: unknown, starter: () => Promise<unknown> = startServer) => {
|
|
1547
1559
|
if (mainModule === currentModule) {
|
|
1548
|
-
starter().catch(error =>
|
|
1560
|
+
starter().catch(error => {
|
|
1561
|
+
console.log(errorMessage(error));
|
|
1562
|
+
process.exitCode = 1;
|
|
1563
|
+
});
|
|
1549
1564
|
}
|
|
1550
1565
|
};
|
|
1551
1566
|
|
package/lhm.plugin.json
CHANGED
|
@@ -263,18 +263,26 @@
|
|
|
263
263
|
"retesting": {
|
|
264
264
|
"type": "string"
|
|
265
265
|
},
|
|
266
|
+
"approval": {
|
|
267
|
+
"type": "string"
|
|
268
|
+
},
|
|
266
269
|
"latency": {
|
|
267
270
|
"type": "string"
|
|
268
271
|
},
|
|
269
|
-
"confirmation": {
|
|
272
|
+
"request confirmation": {
|
|
273
|
+
"type": "string"
|
|
274
|
+
},
|
|
275
|
+
"order confirmation": {
|
|
270
276
|
"type": "string"
|
|
271
277
|
}
|
|
272
278
|
},
|
|
273
279
|
"required": [
|
|
274
280
|
"new testing",
|
|
275
281
|
"retesting",
|
|
282
|
+
"approval",
|
|
276
283
|
"latency",
|
|
277
|
-
"confirmation"
|
|
284
|
+
"request confirmation",
|
|
285
|
+
"order confirmation"
|
|
278
286
|
],
|
|
279
287
|
"additionalProperties": false
|
|
280
288
|
}
|
|
@@ -907,18 +915,26 @@
|
|
|
907
915
|
"retesting": {
|
|
908
916
|
"type": "string"
|
|
909
917
|
},
|
|
918
|
+
"approval": {
|
|
919
|
+
"type": "string"
|
|
920
|
+
},
|
|
910
921
|
"latency": {
|
|
911
922
|
"type": "string"
|
|
912
923
|
},
|
|
913
|
-
"confirmation": {
|
|
924
|
+
"request confirmation": {
|
|
925
|
+
"type": "string"
|
|
926
|
+
},
|
|
927
|
+
"order confirmation": {
|
|
914
928
|
"type": "string"
|
|
915
929
|
}
|
|
916
930
|
},
|
|
917
931
|
"required": [
|
|
918
932
|
"new testing",
|
|
919
933
|
"retesting",
|
|
934
|
+
"approval",
|
|
920
935
|
"latency",
|
|
921
|
-
"confirmation"
|
|
936
|
+
"request confirmation",
|
|
937
|
+
"order confirmation"
|
|
922
938
|
],
|
|
923
939
|
"additionalProperties": false
|
|
924
940
|
}
|
|
@@ -1341,18 +1357,26 @@
|
|
|
1341
1357
|
"retesting": {
|
|
1342
1358
|
"type": "string"
|
|
1343
1359
|
},
|
|
1360
|
+
"approval": {
|
|
1361
|
+
"type": "string"
|
|
1362
|
+
},
|
|
1344
1363
|
"latency": {
|
|
1345
1364
|
"type": "string"
|
|
1346
1365
|
},
|
|
1347
|
-
"confirmation": {
|
|
1366
|
+
"request confirmation": {
|
|
1367
|
+
"type": "string"
|
|
1368
|
+
},
|
|
1369
|
+
"order confirmation": {
|
|
1348
1370
|
"type": "string"
|
|
1349
1371
|
}
|
|
1350
1372
|
},
|
|
1351
1373
|
"required": [
|
|
1352
1374
|
"new testing",
|
|
1353
1375
|
"retesting",
|
|
1376
|
+
"approval",
|
|
1354
1377
|
"latency",
|
|
1355
|
-
"confirmation"
|
|
1378
|
+
"request confirmation",
|
|
1379
|
+
"order confirmation"
|
|
1356
1380
|
],
|
|
1357
1381
|
"additionalProperties": false
|
|
1358
1382
|
}
|
|
@@ -1886,18 +1910,26 @@
|
|
|
1886
1910
|
"retesting": {
|
|
1887
1911
|
"type": "string"
|
|
1888
1912
|
},
|
|
1913
|
+
"approval": {
|
|
1914
|
+
"type": "string"
|
|
1915
|
+
},
|
|
1889
1916
|
"latency": {
|
|
1890
1917
|
"type": "string"
|
|
1891
1918
|
},
|
|
1892
|
-
"confirmation": {
|
|
1919
|
+
"request confirmation": {
|
|
1920
|
+
"type": "string"
|
|
1921
|
+
},
|
|
1922
|
+
"order confirmation": {
|
|
1893
1923
|
"type": "string"
|
|
1894
1924
|
}
|
|
1895
1925
|
},
|
|
1896
1926
|
"required": [
|
|
1897
1927
|
"new testing",
|
|
1898
1928
|
"retesting",
|
|
1929
|
+
"approval",
|
|
1899
1930
|
"latency",
|
|
1900
|
-
"confirmation"
|
|
1931
|
+
"request confirmation",
|
|
1932
|
+
"order confirmation"
|
|
1901
1933
|
],
|
|
1902
1934
|
"additionalProperties": false
|
|
1903
1935
|
}
|
|
@@ -2275,18 +2307,26 @@
|
|
|
2275
2307
|
"retesting": {
|
|
2276
2308
|
"type": "string"
|
|
2277
2309
|
},
|
|
2310
|
+
"approval": {
|
|
2311
|
+
"type": "string"
|
|
2312
|
+
},
|
|
2278
2313
|
"latency": {
|
|
2279
2314
|
"type": "string"
|
|
2280
2315
|
},
|
|
2281
|
-
"confirmation": {
|
|
2316
|
+
"request confirmation": {
|
|
2317
|
+
"type": "string"
|
|
2318
|
+
},
|
|
2319
|
+
"order confirmation": {
|
|
2282
2320
|
"type": "string"
|
|
2283
2321
|
}
|
|
2284
2322
|
},
|
|
2285
2323
|
"required": [
|
|
2286
2324
|
"new testing",
|
|
2287
2325
|
"retesting",
|
|
2326
|
+
"approval",
|
|
2288
2327
|
"latency",
|
|
2289
|
-
"confirmation"
|
|
2328
|
+
"request confirmation",
|
|
2329
|
+
"order confirmation"
|
|
2290
2330
|
],
|
|
2291
2331
|
"additionalProperties": false
|
|
2292
2332
|
}
|
|
@@ -2634,18 +2674,26 @@
|
|
|
2634
2674
|
"retesting": {
|
|
2635
2675
|
"type": "string"
|
|
2636
2676
|
},
|
|
2677
|
+
"approval": {
|
|
2678
|
+
"type": "string"
|
|
2679
|
+
},
|
|
2637
2680
|
"latency": {
|
|
2638
2681
|
"type": "string"
|
|
2639
2682
|
},
|
|
2640
|
-
"confirmation": {
|
|
2683
|
+
"request confirmation": {
|
|
2684
|
+
"type": "string"
|
|
2685
|
+
},
|
|
2686
|
+
"order confirmation": {
|
|
2641
2687
|
"type": "string"
|
|
2642
2688
|
}
|
|
2643
2689
|
},
|
|
2644
2690
|
"required": [
|
|
2645
2691
|
"new testing",
|
|
2646
2692
|
"retesting",
|
|
2693
|
+
"approval",
|
|
2647
2694
|
"latency",
|
|
2648
|
-
"confirmation"
|
|
2695
|
+
"request confirmation",
|
|
2696
|
+
"order confirmation"
|
|
2649
2697
|
],
|
|
2650
2698
|
"additionalProperties": false
|
|
2651
2699
|
}
|
|
@@ -2978,18 +3026,26 @@
|
|
|
2978
3026
|
"retesting": {
|
|
2979
3027
|
"type": "string"
|
|
2980
3028
|
},
|
|
3029
|
+
"approval": {
|
|
3030
|
+
"type": "string"
|
|
3031
|
+
},
|
|
2981
3032
|
"latency": {
|
|
2982
3033
|
"type": "string"
|
|
2983
3034
|
},
|
|
2984
|
-
"confirmation": {
|
|
3035
|
+
"request confirmation": {
|
|
3036
|
+
"type": "string"
|
|
3037
|
+
},
|
|
3038
|
+
"order confirmation": {
|
|
2985
3039
|
"type": "string"
|
|
2986
3040
|
}
|
|
2987
3041
|
},
|
|
2988
3042
|
"required": [
|
|
2989
3043
|
"new testing",
|
|
2990
3044
|
"retesting",
|
|
3045
|
+
"approval",
|
|
2991
3046
|
"latency",
|
|
2992
|
-
"confirmation"
|
|
3047
|
+
"request confirmation",
|
|
3048
|
+
"order confirmation"
|
|
2993
3049
|
],
|
|
2994
3050
|
"additionalProperties": false
|
|
2995
3051
|
}
|
|
@@ -3272,18 +3328,26 @@
|
|
|
3272
3328
|
"retesting": {
|
|
3273
3329
|
"type": "string"
|
|
3274
3330
|
},
|
|
3331
|
+
"approval": {
|
|
3332
|
+
"type": "string"
|
|
3333
|
+
},
|
|
3275
3334
|
"latency": {
|
|
3276
3335
|
"type": "string"
|
|
3277
3336
|
},
|
|
3278
|
-
"confirmation": {
|
|
3337
|
+
"request confirmation": {
|
|
3338
|
+
"type": "string"
|
|
3339
|
+
},
|
|
3340
|
+
"order confirmation": {
|
|
3279
3341
|
"type": "string"
|
|
3280
3342
|
}
|
|
3281
3343
|
},
|
|
3282
3344
|
"required": [
|
|
3283
3345
|
"new testing",
|
|
3284
3346
|
"retesting",
|
|
3347
|
+
"approval",
|
|
3285
3348
|
"latency",
|
|
3286
|
-
"confirmation"
|
|
3349
|
+
"request confirmation",
|
|
3350
|
+
"order confirmation"
|
|
3287
3351
|
],
|
|
3288
3352
|
"additionalProperties": false
|
|
3289
3353
|
}
|
|
@@ -3602,18 +3666,26 @@
|
|
|
3602
3666
|
"retesting": {
|
|
3603
3667
|
"type": "string"
|
|
3604
3668
|
},
|
|
3669
|
+
"approval": {
|
|
3670
|
+
"type": "string"
|
|
3671
|
+
},
|
|
3605
3672
|
"latency": {
|
|
3606
3673
|
"type": "string"
|
|
3607
3674
|
},
|
|
3608
|
-
"confirmation": {
|
|
3675
|
+
"request confirmation": {
|
|
3676
|
+
"type": "string"
|
|
3677
|
+
},
|
|
3678
|
+
"order confirmation": {
|
|
3609
3679
|
"type": "string"
|
|
3610
3680
|
}
|
|
3611
3681
|
},
|
|
3612
3682
|
"required": [
|
|
3613
3683
|
"new testing",
|
|
3614
3684
|
"retesting",
|
|
3685
|
+
"approval",
|
|
3615
3686
|
"latency",
|
|
3616
|
-
"confirmation"
|
|
3687
|
+
"request confirmation",
|
|
3688
|
+
"order confirmation"
|
|
3617
3689
|
],
|
|
3618
3690
|
"additionalProperties": false
|
|
3619
3691
|
}
|
|
@@ -3969,18 +4041,26 @@
|
|
|
3969
4041
|
"retesting": {
|
|
3970
4042
|
"type": "string"
|
|
3971
4043
|
},
|
|
4044
|
+
"approval": {
|
|
4045
|
+
"type": "string"
|
|
4046
|
+
},
|
|
3972
4047
|
"latency": {
|
|
3973
4048
|
"type": "string"
|
|
3974
4049
|
},
|
|
3975
|
-
"confirmation": {
|
|
4050
|
+
"request confirmation": {
|
|
4051
|
+
"type": "string"
|
|
4052
|
+
},
|
|
4053
|
+
"order confirmation": {
|
|
3976
4054
|
"type": "string"
|
|
3977
4055
|
}
|
|
3978
4056
|
},
|
|
3979
4057
|
"required": [
|
|
3980
4058
|
"new testing",
|
|
3981
4059
|
"retesting",
|
|
4060
|
+
"approval",
|
|
3982
4061
|
"latency",
|
|
3983
|
-
"confirmation"
|
|
4062
|
+
"request confirmation",
|
|
4063
|
+
"order confirmation"
|
|
3984
4064
|
],
|
|
3985
4065
|
"additionalProperties": false
|
|
3986
4066
|
}
|
|
@@ -4321,18 +4401,26 @@
|
|
|
4321
4401
|
"retesting": {
|
|
4322
4402
|
"type": "string"
|
|
4323
4403
|
},
|
|
4404
|
+
"approval": {
|
|
4405
|
+
"type": "string"
|
|
4406
|
+
},
|
|
4324
4407
|
"latency": {
|
|
4325
4408
|
"type": "string"
|
|
4326
4409
|
},
|
|
4327
|
-
"confirmation": {
|
|
4410
|
+
"request confirmation": {
|
|
4411
|
+
"type": "string"
|
|
4412
|
+
},
|
|
4413
|
+
"order confirmation": {
|
|
4328
4414
|
"type": "string"
|
|
4329
4415
|
}
|
|
4330
4416
|
},
|
|
4331
4417
|
"required": [
|
|
4332
4418
|
"new testing",
|
|
4333
4419
|
"retesting",
|
|
4420
|
+
"approval",
|
|
4334
4421
|
"latency",
|
|
4335
|
-
"confirmation"
|
|
4422
|
+
"request confirmation",
|
|
4423
|
+
"order confirmation"
|
|
4336
4424
|
],
|
|
4337
4425
|
"additionalProperties": false
|
|
4338
4426
|
}
|
|
@@ -4622,18 +4710,26 @@
|
|
|
4622
4710
|
"retesting": {
|
|
4623
4711
|
"type": "string"
|
|
4624
4712
|
},
|
|
4713
|
+
"approval": {
|
|
4714
|
+
"type": "string"
|
|
4715
|
+
},
|
|
4625
4716
|
"latency": {
|
|
4626
4717
|
"type": "string"
|
|
4627
4718
|
},
|
|
4628
|
-
"confirmation": {
|
|
4719
|
+
"request confirmation": {
|
|
4720
|
+
"type": "string"
|
|
4721
|
+
},
|
|
4722
|
+
"order confirmation": {
|
|
4629
4723
|
"type": "string"
|
|
4630
4724
|
}
|
|
4631
4725
|
},
|
|
4632
4726
|
"required": [
|
|
4633
4727
|
"new testing",
|
|
4634
4728
|
"retesting",
|
|
4729
|
+
"approval",
|
|
4635
4730
|
"latency",
|
|
4636
|
-
"confirmation"
|
|
4731
|
+
"request confirmation",
|
|
4732
|
+
"order confirmation"
|
|
4637
4733
|
],
|
|
4638
4734
|
"additionalProperties": false
|
|
4639
4735
|
}
|
|
@@ -4722,5 +4818,5 @@
|
|
|
4722
4818
|
}
|
|
4723
4819
|
}
|
|
4724
4820
|
],
|
|
4725
|
-
"version": "
|
|
4821
|
+
"version": "63.1.0"
|
|
4726
4822
|
}
|
package/openapi-3.0.yaml
CHANGED
|
@@ -2,7 +2,7 @@ openapi: 3.0.3
|
|
|
2
2
|
info:
|
|
3
3
|
title: Kilotest API
|
|
4
4
|
description: 'Kilotest is an ensemble testing and reporting service for front-end web quality. It uses an ensemble of 12 rule engines to test web pages for front-end quality (accessibility, usability, and web standards, including HTML, CSS, ARIA, and WCAG). Kilotest acts as a web server for human users, an MCP server for AI platforms, and an API for programmatic access. With this API you can request that a page be tested or retested, discover available reports, incrementally retrieve facts from a report: from what reports are available, to what issues were reported in one report, to what elements violated one issue, to what diagnoses rule engines gave for one violation.'
|
|
5
|
-
version:
|
|
5
|
+
version: 63.1.0
|
|
6
6
|
license:
|
|
7
7
|
name: MIT
|
|
8
8
|
url: https://github.com/jrpool/kilotest/blob/main/LICENSE
|
|
@@ -1832,15 +1832,21 @@ components:
|
|
|
1832
1832
|
type: string
|
|
1833
1833
|
retesting:
|
|
1834
1834
|
type: string
|
|
1835
|
+
approval:
|
|
1836
|
+
type: string
|
|
1835
1837
|
latency:
|
|
1836
1838
|
type: string
|
|
1837
|
-
confirmation:
|
|
1839
|
+
request confirmation:
|
|
1840
|
+
type: string
|
|
1841
|
+
order confirmation:
|
|
1838
1842
|
type: string
|
|
1839
1843
|
required:
|
|
1840
1844
|
- new testing
|
|
1841
1845
|
- retesting
|
|
1846
|
+
- approval
|
|
1842
1847
|
- latency
|
|
1843
|
-
- confirmation
|
|
1848
|
+
- request confirmation
|
|
1849
|
+
- order confirmation
|
|
1844
1850
|
additionalProperties: false
|
|
1845
1851
|
required:
|
|
1846
1852
|
- what Kilotest does
|
package/openapi.yaml
CHANGED
|
@@ -2,7 +2,7 @@ openapi: 3.1.0
|
|
|
2
2
|
info:
|
|
3
3
|
title: Kilotest API
|
|
4
4
|
description: 'Kilotest is an ensemble testing and reporting service for front-end web quality. It uses an ensemble of 12 rule engines to test web pages for front-end quality (accessibility, usability, and web standards, including HTML, CSS, ARIA, and WCAG). Kilotest acts as a web server for human users, an MCP server for AI platforms, and an API for programmatic access. With this API you can request that a page be tested or retested, discover available reports, incrementally retrieve facts from a report: from what reports are available, to what issues were reported in one report, to what elements violated one issue, to what diagnoses rule engines gave for one violation.'
|
|
5
|
-
version:
|
|
5
|
+
version: 63.1.0
|
|
6
6
|
license:
|
|
7
7
|
name: MIT
|
|
8
8
|
identifier: MIT
|
|
@@ -1814,15 +1814,21 @@ components:
|
|
|
1814
1814
|
type: string
|
|
1815
1815
|
retesting:
|
|
1816
1816
|
type: string
|
|
1817
|
+
approval:
|
|
1818
|
+
type: string
|
|
1817
1819
|
latency:
|
|
1818
1820
|
type: string
|
|
1819
|
-
confirmation:
|
|
1821
|
+
request confirmation:
|
|
1822
|
+
type: string
|
|
1823
|
+
order confirmation:
|
|
1820
1824
|
type: string
|
|
1821
1825
|
required:
|
|
1822
1826
|
- new testing
|
|
1823
1827
|
- retesting
|
|
1828
|
+
- approval
|
|
1824
1829
|
- latency
|
|
1825
|
-
- confirmation
|
|
1830
|
+
- request confirmation
|
|
1831
|
+
- order confirmation
|
|
1826
1832
|
additionalProperties: false
|
|
1827
1833
|
required:
|
|
1828
1834
|
- what Kilotest does
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "https://github.com/jrpool/kilotest",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "63.1.0",
|
|
10
10
|
"remotes": [
|
|
11
11
|
{
|
|
12
12
|
"type": "streamable-http",
|
|
@@ -14,5 +14,14 @@
|
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
16
|
"title": "Kilotest",
|
|
17
|
-
"websiteUrl": "https://kilotest.com"
|
|
17
|
+
"websiteUrl": "https://kilotest.com",
|
|
18
|
+
"icons": [
|
|
19
|
+
{
|
|
20
|
+
"src": "https://kilotest.com/favicon.svg",
|
|
21
|
+
"mimeType": "image/svg+xml",
|
|
22
|
+
"sizes": [
|
|
23
|
+
"any"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
18
27
|
}
|
package/test/setup.ts
CHANGED
|
@@ -17,3 +17,7 @@ for (const key of ['MANAGER_EMAIL', 'ALERT_API_HOST', 'ALERT_API_PATH', 'ALERT_A
|
|
|
17
17
|
// to pass the resolution check that isAllowedTarget performs. A test of that check
|
|
18
18
|
// itself sets this env var back to unset (or false) around the specific call being tested.
|
|
19
19
|
process.env.ALLOW_INTERNAL_TARGETS = 'true';
|
|
20
|
+
// Give every response a deterministic host. Without this, getThisHost returns
|
|
21
|
+
// undefined, so URL-valued response fields fail schema validation; setting it here
|
|
22
|
+
// rather than relying on a developer's .env keeps tests independent of the environment.
|
|
23
|
+
process.env.THIS_KILOTEST_HOST = 'https://kilotest.example.com';
|
|
@@ -58,6 +58,19 @@
|
|
|
58
58
|
</ol>
|
|
59
59
|
<p>Once you add Kilotest, it also becomes available in the Gemini app on your mobile device. To make Gemini use Kilotest for a prompt, enter <q>@</q> and select Kilotest.</p>
|
|
60
60
|
</details>
|
|
61
|
+
<details id="github-copilot">
|
|
62
|
+
<summary>GitHub Copilot</summary>
|
|
63
|
+
<p>These instructions apply to <a href="https://github.com/features/copilot">GitHub Copilot</a> in the <a href="https://code.visualstudio.com">Visual Studio Code</a> application, version 1.99 or later, on any Copilot plan, including the Copilot Free plan. As prerequisites, you must have <a href="https://code.visualstudio.com/docs/setup/copilot">enabled Copilot</a> in the application, and you must be signed in to your GitHub account.</p>
|
|
64
|
+
<ol>
|
|
65
|
+
<li>Open the Command Palette and run <q>MCP: Add Server</q>.</li>
|
|
66
|
+
<li>Select <q>HTTP (HTTP or Server-Sent Events)</q>.</li>
|
|
67
|
+
<li>Enter <q>https://kilotest.com/mcp</q> as the server URL.</li>
|
|
68
|
+
<li>Enter <q>kilotest</q> as the server ID.</li>
|
|
69
|
+
<li>Select <q>Global</q> (and <q>Copilot Global</q>, if prompted) to make Kilotest available in all your workspaces.</li>
|
|
70
|
+
<li>Accept the prompt to trust and start the server.</li>
|
|
71
|
+
</ol>
|
|
72
|
+
<p>In the Copilot Chat view, select the <q>Agent</q> mode; the Kilotest tools then become available to Copilot. In Visual Studio 2022 version 17.14 or later, you can instead open the chat tool picker, select the <q>+</q> button, and choose <q>Add custom MCP server</q> to enter the same name and URL.</p>
|
|
73
|
+
</details>
|
|
61
74
|
<details id="grok">
|
|
62
75
|
<summary>Grok</summary>
|
|
63
76
|
<p>These instructions apply to <a href="https://grok.com">Grok</a> on any plan, including the free plan.</p>
|
|
@@ -82,6 +95,21 @@
|
|
|
82
95
|
<li>Save the connector.</li>
|
|
83
96
|
</ol>
|
|
84
97
|
</details>
|
|
98
|
+
<details id="microsoft-foundry">
|
|
99
|
+
<summary>Microsoft Foundry</summary>
|
|
100
|
+
<p>These instructions apply to <a href="https://ai.azure.com">Microsoft Foundry</a> (formerly Azure AI Foundry), where agents are billed pay-as-you-go through an Azure subscription. A new Azure account currently includes a US$200 credit. Before connecting Kilotest, you need a Foundry project with a deployed model and an agent.</p>
|
|
101
|
+
<ol>
|
|
102
|
+
<li>In the Foundry portal, select the <q>Build</q> tab and select your agent.</li>
|
|
103
|
+
<li>In the <q>Playground</q> tab, expand the <q>Tools</q> dropdown and select <q>Add</q>.</li>
|
|
104
|
+
<li>In the <q>Custom</q> tab, select <q>Model Context Protocol (MCP)</q>, then <q>Create</q>.</li>
|
|
105
|
+
<li>Enter <q>Kilotest</q> as the <q>Name</q>.</li>
|
|
106
|
+
<li>Enter <q>https://kilotest.com/mcp</q> as the <q>Remote MCP Server endpoint</q>.</li>
|
|
107
|
+
<li>Set the <q>Authentication</q> to <q>Unauthenticated</q>.</li>
|
|
108
|
+
<li>Select <q>Connect</q>.</li>
|
|
109
|
+
<li>Select <q>Save</q> to save the tool configuration in your agent.</li>
|
|
110
|
+
</ol>
|
|
111
|
+
<p>When your agent wants to call a Kilotest tool, review the tool name and arguments and select <q>Approve</q> to allow the call.</p>
|
|
112
|
+
</details>
|
|
85
113
|
<details id="perplexity">
|
|
86
114
|
<summary>Perplexity</summary>
|
|
87
115
|
<p>These instructions apply to <a href="https://www.perplexity.ai/">Perplexity</a> Pro in a browser.</p>
|
|
@@ -114,7 +142,7 @@
|
|
|
114
142
|
<h2>Demonstration</h2>
|
|
115
143
|
<p>A <a href="/demo-chatgpt.html">step-through demonstration</a> shows ChatGPT using Kilotest to discover tested pages, inspect reported issues, and request tests and retests.</p>
|
|
116
144
|
<h2 id="other">Other AI platforms</h2>
|
|
117
|
-
<p>You can connect Kilotest to some other AI platforms, too. The instructions above are limited to
|
|
145
|
+
<p>You can connect Kilotest to some other AI platforms, too. The instructions above are limited to platforms available to individuals that are free, relatively inexpensive (US$20 per month or less), or billed pay-as-you-go. Business platforms such as ChatGPT Business and Microsoft Copilot Studio, and developer platforms such as Devin Desktop, also permit you to enable Kilotest.</p>
|
|
118
146
|
<p>Feel free to submit a comment below about empowering any AI platform to use Kilotest.</p>
|
|
119
147
|
<p>You can also submit an issue on the <a href="https://github.com/jrpool/kilotest/issues">Kilotest GitHub repository</a>.</p>
|
|
120
148
|
<section id="suggest-improvement">
|