agent-nuvira 2.7.0 → 2.7.2
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/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +17 -3
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/app.js +26 -0
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +108 -2
- package/dist/cli/chat.js.map +1 -1
- package/dist/config/auth.js +18 -0
- package/dist/config/jwt.js +24 -0
- package/dist/config/keys.js +14 -0
- package/dist/gateway/chat-store.d.ts +32 -5
- package/dist/gateway/chat-store.d.ts.map +1 -1
- package/dist/gateway/chat-store.js +52 -6
- package/dist/gateway/chat-store.js.map +1 -1
- package/dist/gateway/registry.d.ts.map +1 -1
- package/dist/gateway/registry.js +54 -10
- package/dist/gateway/registry.js.map +1 -1
- package/dist/inference/model-validator.d.ts.map +1 -1
- package/dist/inference/model-validator.js +11 -1
- package/dist/inference/model-validator.js.map +1 -1
- package/dist/inference/tool-call-utils.d.ts +31 -0
- package/dist/inference/tool-call-utils.d.ts.map +1 -1
- package/dist/inference/tool-call-utils.js +41 -0
- package/dist/inference/tool-call-utils.js.map +1 -1
- package/dist/learning/reasoning-trace.d.ts +5 -3
- package/dist/learning/reasoning-trace.d.ts.map +1 -1
- package/dist/learning/reasoning-trace.js +2 -2
- package/dist/learning/reasoning-trace.js.map +1 -1
- package/dist/middleware/auth.js +30 -0
- package/dist/nlu/conversation-gate.js +1 -1
- package/dist/nlu/conversation-gate.js.map +1 -1
- package/dist/nlu/entities.d.ts.map +1 -1
- package/dist/nlu/entities.js +12 -0
- package/dist/nlu/entities.js.map +1 -1
- package/dist/nlu/intent.d.ts.map +1 -1
- package/dist/nlu/intent.js +83 -15
- package/dist/nlu/intent.js.map +1 -1
- package/dist/passport.js +34 -0
- package/dist/routes/auth.js +42 -0
- package/dist/routes/user.js +31 -0
- package/dist/server.js +17 -0
- package/dist/tools/pipeline-tool.d.ts.map +1 -1
- package/dist/tools/pipeline-tool.js +14 -0
- package/dist/tools/pipeline-tool.js.map +1 -1
- package/dist/tools/release-sync.d.ts +52 -16
- package/dist/tools/release-sync.d.ts.map +1 -1
- package/dist/tools/release-sync.js +143 -79
- package/dist/tools/release-sync.js.map +1 -1
- package/dist/web-dashboard/src/App.js +85 -0
- package/dist/web-dashboard/src/admin-auth.test.js +186 -0
- package/dist/web-dashboard/src/ansi.js +23 -0
- package/dist/web-dashboard/src/ansi.test.js +31 -0
- package/dist/web-dashboard/src/api-admin-auth.test.js +172 -0
- package/dist/web-dashboard/src/api-admin.test.js +65 -0
- package/dist/web-dashboard/src/api-hub.test.js +117 -0
- package/dist/web-dashboard/src/api.js +1421 -0
- package/dist/web-dashboard/src/api.test.js +51 -0
- package/dist/web-dashboard/src/artifacts.js +128 -0
- package/dist/web-dashboard/src/artifacts.test.js +139 -0
- package/dist/web-dashboard/src/components/AdminPanel.js +567 -0
- package/dist/web-dashboard/src/components/AdminPanel.test.js +288 -0
- package/dist/web-dashboard/src/components/AgentHub.js +1580 -0
- package/dist/web-dashboard/src/components/AgentHub.test.js +343 -0
- package/dist/web-dashboard/src/components/BedrockOnboarding.js +320 -0
- package/dist/web-dashboard/src/components/BenchmarkCharts.js +228 -0
- package/dist/web-dashboard/src/components/ChatPage.js +1586 -0
- package/dist/web-dashboard/src/components/ChatPage.test.js +899 -0
- package/dist/web-dashboard/src/components/ContactsPage.js +141 -0
- package/dist/web-dashboard/src/components/CostDashboard.js +105 -0
- package/dist/web-dashboard/src/components/DAGView.js +477 -0
- package/dist/web-dashboard/src/components/DAGView.test.js +147 -0
- package/dist/web-dashboard/src/components/EnvVarEditor.js +138 -0
- package/dist/web-dashboard/src/components/EvalsPage.js +73 -0
- package/dist/web-dashboard/src/components/EvalsPage.test.js +120 -0
- package/dist/web-dashboard/src/components/ExecutionHistory.js +201 -0
- package/dist/web-dashboard/src/components/GatewayPage.js +40 -0
- package/dist/web-dashboard/src/components/GatewayPage.test.js +74 -0
- package/dist/web-dashboard/src/components/HealthPanel.js +68 -0
- package/dist/web-dashboard/src/components/HistoryBrowser.js +34 -0
- package/dist/web-dashboard/src/components/Layout.js +50 -0
- package/dist/web-dashboard/src/components/Markdown.js +152 -0
- package/dist/web-dashboard/src/components/Markdown.test.js +126 -0
- package/dist/web-dashboard/src/components/MarkdownZeroDep.js +237 -0
- package/dist/web-dashboard/src/components/MemoryPanel.js +81 -0
- package/dist/web-dashboard/src/components/ModelTimeline.js +298 -0
- package/dist/web-dashboard/src/components/ModelsPanel.js +1484 -0
- package/dist/web-dashboard/src/components/ModelsPanel.test.js +460 -0
- package/dist/web-dashboard/src/components/Overview.js +123 -0
- package/dist/web-dashboard/src/components/PhaseTimeline.js +359 -0
- package/dist/web-dashboard/src/components/PhaseTimeline.test.js +234 -0
- package/dist/web-dashboard/src/components/PlatformConfigSection.js +384 -0
- package/dist/web-dashboard/src/components/PlatformConfigSection.test.js +106 -0
- package/dist/web-dashboard/src/components/PlatformsPage.js +250 -0
- package/dist/web-dashboard/src/components/QuotaPanel.js +159 -0
- package/dist/web-dashboard/src/components/QuotaPanel.test.js +85 -0
- package/dist/web-dashboard/src/components/RequestsPanel.js +229 -0
- package/dist/web-dashboard/src/components/RequestsPanel.test.js +103 -0
- package/dist/web-dashboard/src/components/RoutingInsightsPanel.js +938 -0
- package/dist/web-dashboard/src/components/RoutingInsightsPanel.test.js +339 -0
- package/dist/web-dashboard/src/components/RoutingWalkthrough.js +408 -0
- package/dist/web-dashboard/src/components/RoutingWalkthrough.test.js +209 -0
- package/dist/web-dashboard/src/components/TaskConsole.js +119 -0
- package/dist/web-dashboard/src/components/TaskConsole.test.js +123 -0
- package/dist/web-dashboard/src/components/TasksPage.js +256 -0
- package/dist/web-dashboard/src/components/TasksPage.test.js +103 -0
- package/dist/web-dashboard/src/components/TracePanel.js +306 -0
- package/dist/web-dashboard/src/components/WhatsAppPanel.js +195 -0
- package/dist/web-dashboard/src/components/WhatsAppPanel.test.js +122 -0
- package/dist/web-dashboard/src/jsonOrNull.js +37 -0
- package/dist/web-dashboard/src/main.js +15 -0
- package/dist/web-dashboard/src/mask.js +11 -0
- package/dist/web-dashboard/vite.config.js +23 -0
- package/dist/web-dashboard/vitest.config.js +15 -0
- package/package.json +5 -3
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for DashboardAPI.fetchAll() — the initial-data bootstrap path
|
|
4
|
+
* every dashboard panel falls back to when SSE hasn't delivered a snapshot yet.
|
|
5
|
+
*
|
|
6
|
+
* Regression coverage for the reported "Failed to execute 'json' on
|
|
7
|
+
* 'Response': Unexpected token '<'" crash: a STALE dashboard server (older
|
|
8
|
+
* version missing newer routes) answers /api/all with the SPA index.html
|
|
9
|
+
* (HTTP 200, text/html). fetchAll() must degrade to null (App waits for the
|
|
10
|
+
* next SSE snapshot) instead of throwing.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
const vitest_1 = require("vitest");
|
|
14
|
+
const api_1 = require("./api");
|
|
15
|
+
const jsonResponse = (data) => new Response(JSON.stringify(data), {
|
|
16
|
+
status: 200,
|
|
17
|
+
headers: { 'Content-Type': 'application/json' },
|
|
18
|
+
});
|
|
19
|
+
const htmlResponse = () => new Response('<!DOCTYPE html><html><body>SPA fallback</body></html>', {
|
|
20
|
+
status: 200,
|
|
21
|
+
headers: { 'Content-Type': 'text/html' },
|
|
22
|
+
});
|
|
23
|
+
(0, vitest_1.describe)('DashboardAPI.fetchAll', () => {
|
|
24
|
+
(0, vitest_1.afterEach)(() => {
|
|
25
|
+
vitest_1.vi.restoreAllMocks();
|
|
26
|
+
});
|
|
27
|
+
(0, vitest_1.it)('parses a real JSON /api/all response', async () => {
|
|
28
|
+
const payload = { serverTime: Date.now(), cost: { totalRequests: 0 } };
|
|
29
|
+
vitest_1.vi.spyOn(globalThis, 'fetch').mockResolvedValue(jsonResponse(payload));
|
|
30
|
+
const api = new api_1.DashboardAPI('http://test');
|
|
31
|
+
const data = await api.fetchAll();
|
|
32
|
+
(0, vitest_1.expect)(data).toMatchObject(payload);
|
|
33
|
+
});
|
|
34
|
+
(0, vitest_1.it)('returns null (no crash) when the server answers /api/all with HTML (stale server)', async () => {
|
|
35
|
+
vitest_1.vi.spyOn(globalThis, 'fetch').mockResolvedValue(htmlResponse());
|
|
36
|
+
const api = new api_1.DashboardAPI('http://test');
|
|
37
|
+
// Must NOT throw — previously res.json() threw "Unexpected token '<'".
|
|
38
|
+
const data = await api.fetchAll();
|
|
39
|
+
(0, vitest_1.expect)(data).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
(0, vitest_1.it)('returns null on HTTP non-ok and on malformed JSON bodies', async () => {
|
|
42
|
+
const notOk = new Response('nope', { status: 500, headers: { 'Content-Type': 'application/json' } });
|
|
43
|
+
const malformed = new Response('{broken', { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
44
|
+
vitest_1.vi.spyOn(globalThis, 'fetch')
|
|
45
|
+
.mockResolvedValueOnce(notOk)
|
|
46
|
+
.mockResolvedValueOnce(malformed);
|
|
47
|
+
const api = new api_1.DashboardAPI('http://test');
|
|
48
|
+
(0, vitest_1.expect)(await api.fetchAll()).toBeNull();
|
|
49
|
+
(0, vitest_1.expect)(await api.fetchAll()).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* P2 — artifact extraction from an agent answer's markdown text.
|
|
4
|
+
*
|
|
5
|
+
* The live `plan` / `diff` / `tool` / `skill_draft` SSE events render their own
|
|
6
|
+
* cards during the turn and are snapshotted into the reply. This module covers
|
|
7
|
+
* the OTHER path: structured blocks the model wrote DIRECTLY into the answer
|
|
8
|
+
* text (a ```diff fenced block, test-runner output, a deploy URL) that never
|
|
9
|
+
* went through an event. Without it those render as plain markdown code — the
|
|
10
|
+
* Phase-2 gap (plan → 📋, code change → diff card, build/test → result card,
|
|
11
|
+
* deploy → 🚀 card with URL).
|
|
12
|
+
*
|
|
13
|
+
* Everything here is PURE (string in, plain objects out) so it unit-tests
|
|
14
|
+
* without React or a server. Best-effort: content without recognizable blocks
|
|
15
|
+
* yields an empty result and the reply renders as today.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.parseDiffSections = parseDiffSections;
|
|
19
|
+
exports.extractDeployUrls = extractDeployUrls;
|
|
20
|
+
exports.extractArtifacts = extractArtifacts;
|
|
21
|
+
const FENCED_BLOCK = /```([\w+-]*)\n([\s\S]*?)```/g;
|
|
22
|
+
/** Split a unified diff into per-file sections (git-tool parity). */
|
|
23
|
+
function parseDiffSections(diff) {
|
|
24
|
+
if (!diff.trim())
|
|
25
|
+
return [];
|
|
26
|
+
const sections = [];
|
|
27
|
+
let current = null;
|
|
28
|
+
for (const line of diff.split('\n')) {
|
|
29
|
+
if (line.startsWith('diff --git ')) {
|
|
30
|
+
if (current)
|
|
31
|
+
sections.push(current);
|
|
32
|
+
// "diff --git a/foo.ts b/foo.ts" → the b-side path (may be /dev/null).
|
|
33
|
+
const b = line.split(' b/').slice(1).join(' b/');
|
|
34
|
+
current = { path: b.replace(/^b\//, ''), body: line };
|
|
35
|
+
}
|
|
36
|
+
else if (current) {
|
|
37
|
+
current.body += `\n${line}`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (current)
|
|
41
|
+
sections.push(current);
|
|
42
|
+
return sections;
|
|
43
|
+
}
|
|
44
|
+
/** Does this fenced block look like test/build output worth a result card? */
|
|
45
|
+
function looksLikeResultBlock(body) {
|
|
46
|
+
return (/(^|\n)\s*(PASS|FAIL|✅|❌|✗|✓|\bok\b)/i.test(body) ||
|
|
47
|
+
/\b\d+\s+(passed|failed)\b/i.test(body) ||
|
|
48
|
+
/(tests?|build|suite|checks?)\s+(passed|failed|complete|green|red|ok)/i.test(body) ||
|
|
49
|
+
/(exit code|process exited|finished with)/i.test(body));
|
|
50
|
+
}
|
|
51
|
+
/** The verdict for a result block: any fail marker wins; pass needs a pass marker. */
|
|
52
|
+
function resultVerdict(body) {
|
|
53
|
+
if (/(^|\n)\s*(FAIL|❌|✗)/i.test(body) || /\bfailed\b/i.test(body))
|
|
54
|
+
return 'fail';
|
|
55
|
+
if (/(^|\n)\s*(PASS|✅|✓)/i.test(body) || /\bpassed\b/i.test(body))
|
|
56
|
+
return 'pass';
|
|
57
|
+
return 'unknown';
|
|
58
|
+
}
|
|
59
|
+
/** First non-empty line as a short headline (fallback for unnamed output). */
|
|
60
|
+
function blockTitle(body) {
|
|
61
|
+
for (const line of body.split('\n')) {
|
|
62
|
+
const t = line.trim();
|
|
63
|
+
if (t)
|
|
64
|
+
return t.length > 80 ? `${t.slice(0, 77)}…` : t;
|
|
65
|
+
}
|
|
66
|
+
return 'Command output';
|
|
67
|
+
}
|
|
68
|
+
const URL_RE = /https?:\/\/[^\s)\]}"'<>]+/i;
|
|
69
|
+
const DEPLOY_CONTEXT = /(🚀|deploy(?:ed|ment|ing)?|live at|published|available at|open the)/i;
|
|
70
|
+
function cleanUrl(raw) {
|
|
71
|
+
return raw.replace(/[.,;:!?]+$/, '');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Extract deploy URLs: lines (or line pairs) that carry a deploy signal AND a
|
|
75
|
+
* URL. Dedupes by URL (first caption wins).
|
|
76
|
+
*/
|
|
77
|
+
function extractDeployUrls(content) {
|
|
78
|
+
const lines = content.split('\n');
|
|
79
|
+
const out = [];
|
|
80
|
+
const seen = new Set();
|
|
81
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
82
|
+
const line = lines[i];
|
|
83
|
+
let m = line.match(DEPLOY_CONTEXT);
|
|
84
|
+
// A "Deployed to:" line followed by a bare URL on the next line counts too.
|
|
85
|
+
if (!m && i + 1 < lines.length && /deploy(?:ed|ment|ing)?:\s*$/i.test(line.trim())) {
|
|
86
|
+
m = lines[i + 1].match(DEPLOY_CONTEXT);
|
|
87
|
+
}
|
|
88
|
+
const u = line.match(URL_RE) || (i + 1 < lines.length ? lines[i + 1].match(URL_RE) : null);
|
|
89
|
+
if (!m || !u)
|
|
90
|
+
continue;
|
|
91
|
+
const url = cleanUrl(u[0]);
|
|
92
|
+
if (seen.has(url))
|
|
93
|
+
continue;
|
|
94
|
+
seen.add(url);
|
|
95
|
+
out.push({ url, title: line.trim().slice(0, 80) || 'Deployment' });
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Scan an agent answer's markdown for structured artifacts:
|
|
101
|
+
* - ```diff fenced blocks → DiffArtifact (per-file sections)
|
|
102
|
+
* - fenced output blocks that read like test/build results → ResultArtifact
|
|
103
|
+
* - deploy-context URLs → DeployArtifact
|
|
104
|
+
* Ordering follows the text (diffs first, then results, then deploys).
|
|
105
|
+
*/
|
|
106
|
+
function extractArtifacts(content) {
|
|
107
|
+
const diffs = [];
|
|
108
|
+
const results = [];
|
|
109
|
+
const deploys = [];
|
|
110
|
+
FENCED_BLOCK.lastIndex = 0;
|
|
111
|
+
let match;
|
|
112
|
+
while ((match = FENCED_BLOCK.exec(content)) !== null) {
|
|
113
|
+
const lang = (match[1] || '').toLowerCase();
|
|
114
|
+
const body = match[2];
|
|
115
|
+
if (lang === 'diff' || body.includes('diff --git ')) {
|
|
116
|
+
const files = parseDiffSections(body);
|
|
117
|
+
if (files.length > 0) {
|
|
118
|
+
diffs.push({ files, summary: `${files.length} file${files.length === 1 ? '' : 's'} changed` });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (looksLikeResultBlock(body)) {
|
|
122
|
+
results.push({ verdict: resultVerdict(body), title: blockTitle(body), body });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (const d of extractDeployUrls(content))
|
|
126
|
+
deploys.push(d);
|
|
127
|
+
return { diffs, results, deploys };
|
|
128
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* P2 — artifact extraction unit tests: the pure text→card parsing the chat
|
|
4
|
+
* uses to render ```diff blocks, test/build output, and deploy URLs from an
|
|
5
|
+
* agent answer (beyond the live plan/diff/tool events).
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const vitest_1 = require("vitest");
|
|
9
|
+
const artifacts_1 = require("./artifacts");
|
|
10
|
+
(0, vitest_1.describe)('parseDiffSections', () => {
|
|
11
|
+
(0, vitest_1.it)('splits a unified diff into per-file sections with b-side paths', () => {
|
|
12
|
+
const diff = [
|
|
13
|
+
'diff --git a/src/a.ts b/src/a.ts',
|
|
14
|
+
'index 111..222 100644',
|
|
15
|
+
'--- a/src/a.ts',
|
|
16
|
+
'+++ b/src/a.ts',
|
|
17
|
+
'@@ -1 +1 @@',
|
|
18
|
+
'-old',
|
|
19
|
+
'+new',
|
|
20
|
+
'diff --git a/src/b.ts b/src/b.ts',
|
|
21
|
+
'--- a/src/b.ts',
|
|
22
|
+
'+++ b/src/b.ts',
|
|
23
|
+
'@@ -1 +1 @@',
|
|
24
|
+
'+only',
|
|
25
|
+
].join('\n');
|
|
26
|
+
const sections = (0, artifacts_1.parseDiffSections)(diff);
|
|
27
|
+
(0, vitest_1.expect)(sections).toHaveLength(2);
|
|
28
|
+
(0, vitest_1.expect)(sections[0].path).toBe('src/a.ts');
|
|
29
|
+
(0, vitest_1.expect)(sections[0].body).toContain('-old');
|
|
30
|
+
(0, vitest_1.expect)(sections[1].path).toBe('src/b.ts');
|
|
31
|
+
(0, vitest_1.expect)(sections[1].body).toContain('+only');
|
|
32
|
+
});
|
|
33
|
+
(0, vitest_1.it)('returns [] for empty or non-diff input', () => {
|
|
34
|
+
(0, vitest_1.expect)((0, artifacts_1.parseDiffSections)('')).toEqual([]);
|
|
35
|
+
(0, vitest_1.expect)((0, artifacts_1.parseDiffSections)('just some text')).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
(0, vitest_1.describe)('extractArtifacts — diff blocks', () => {
|
|
39
|
+
(0, vitest_1.it)('turns a ```diff fenced block into a DiffArtifact', () => {
|
|
40
|
+
const content = [
|
|
41
|
+
'Here is the change I made:',
|
|
42
|
+
'```diff',
|
|
43
|
+
'diff --git a/package.json b/package.json',
|
|
44
|
+
'--- a/package.json',
|
|
45
|
+
'+++ b/package.json',
|
|
46
|
+
'@@ -1 +1 @@',
|
|
47
|
+
'-"version": "1.0.0",',
|
|
48
|
+
'+"version": "1.1.0",',
|
|
49
|
+
'```',
|
|
50
|
+
'Now run the tests.',
|
|
51
|
+
].join('\n');
|
|
52
|
+
const { diffs } = (0, artifacts_1.extractArtifacts)(content);
|
|
53
|
+
(0, vitest_1.expect)(diffs).toHaveLength(1);
|
|
54
|
+
(0, vitest_1.expect)(diffs[0].files).toHaveLength(1);
|
|
55
|
+
(0, vitest_1.expect)(diffs[0].files[0].path).toBe('package.json');
|
|
56
|
+
(0, vitest_1.expect)(diffs[0].summary).toBe('1 file changed');
|
|
57
|
+
});
|
|
58
|
+
(0, vitest_1.it)('does not treat a plain code block as a diff', () => {
|
|
59
|
+
const { diffs } = (0, artifacts_1.extractArtifacts)('```ts\nconst x = 1;\n```');
|
|
60
|
+
(0, vitest_1.expect)(diffs).toHaveLength(0);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
(0, vitest_1.describe)('extractArtifacts — result blocks', () => {
|
|
64
|
+
(0, vitest_1.it)('classifies a passing test run', () => {
|
|
65
|
+
const content = ['Test run:', '```', 'PASS src/a.test.ts', 'PASS src/b.test.ts', '2 tests passed', '```'].join('\n');
|
|
66
|
+
const { results } = (0, artifacts_1.extractArtifacts)(content);
|
|
67
|
+
(0, vitest_1.expect)(results).toHaveLength(1);
|
|
68
|
+
(0, vitest_1.expect)(results[0].verdict).toBe('pass');
|
|
69
|
+
(0, vitest_1.expect)(results[0].title).toContain('PASS src/a.test.ts');
|
|
70
|
+
});
|
|
71
|
+
(0, vitest_1.it)('classifies a failing build as fail', () => {
|
|
72
|
+
const content = ['```bash', 'FAIL src/a.test.ts', '1 test failed', 'exit code 1', '```'].join('\n');
|
|
73
|
+
const { results } = (0, artifacts_1.extractArtifacts)(content);
|
|
74
|
+
(0, vitest_1.expect)(results).toHaveLength(1);
|
|
75
|
+
(0, vitest_1.expect)(results[0].verdict).toBe('fail');
|
|
76
|
+
});
|
|
77
|
+
(0, vitest_1.it)('ignores fenced blocks without result markers', () => {
|
|
78
|
+
const content = ['```', 'some prose', '```'].join('\n');
|
|
79
|
+
const { results } = (0, artifacts_1.extractArtifacts)(content);
|
|
80
|
+
(0, vitest_1.expect)(results).toHaveLength(0);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
(0, vitest_1.describe)('extractArtifacts — deploy URLs', () => {
|
|
84
|
+
(0, vitest_1.it)('extracts a URL on a deploy line', () => {
|
|
85
|
+
const { deploys } = (0, artifacts_1.extractArtifacts)('🚀 Deployed to https://app.example.com/landing — enjoy!');
|
|
86
|
+
(0, vitest_1.expect)(deploys).toHaveLength(1);
|
|
87
|
+
(0, vitest_1.expect)(deploys[0].url).toBe('https://app.example.com/landing');
|
|
88
|
+
});
|
|
89
|
+
(0, vitest_1.it)('extracts a URL after a "Deployed:" line', () => {
|
|
90
|
+
const { deploys } = (0, artifacts_1.extractArtifacts)('The build is live.\nDeployed:\nhttps://dash.example.com');
|
|
91
|
+
(0, vitest_1.expect)(deploys.length).toBeGreaterThan(0);
|
|
92
|
+
(0, vitest_1.expect)(deploys[0].url).toBe('https://dash.example.com');
|
|
93
|
+
});
|
|
94
|
+
(0, vitest_1.it)('dedupes repeated URLs and ignores plain links', () => {
|
|
95
|
+
const content = [
|
|
96
|
+
'See https://example.com/docs for details.',
|
|
97
|
+
'🚀 Live at https://app.example.com and also https://app.example.com.',
|
|
98
|
+
].join('\n');
|
|
99
|
+
const { deploys } = (0, artifacts_1.extractArtifacts)(content);
|
|
100
|
+
(0, vitest_1.expect)(deploys).toHaveLength(1);
|
|
101
|
+
(0, vitest_1.expect)(deploys[0].url).toBe('https://app.example.com');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
(0, vitest_1.describe)('extractArtifacts — combined', () => {
|
|
105
|
+
(0, vitest_1.it)('returns all artifact kinds from one answer, in order', () => {
|
|
106
|
+
const content = [
|
|
107
|
+
'I fixed the bug:',
|
|
108
|
+
'```diff',
|
|
109
|
+
'diff --git a/src/fix.ts b/src/fix.ts',
|
|
110
|
+
'--- a/src/fix.ts',
|
|
111
|
+
'+++ b/src/fix.ts',
|
|
112
|
+
'@@ -1 +1 @@',
|
|
113
|
+
'-broken',
|
|
114
|
+
'+fixed',
|
|
115
|
+
'```',
|
|
116
|
+
'```',
|
|
117
|
+
'PASS src/fix.test.ts',
|
|
118
|
+
'```',
|
|
119
|
+
'🚀 Preview: https://preview.example.com',
|
|
120
|
+
].join('\n');
|
|
121
|
+
const { diffs, results, deploys } = (0, artifacts_1.extractArtifacts)(content);
|
|
122
|
+
(0, vitest_1.expect)(diffs).toHaveLength(1);
|
|
123
|
+
(0, vitest_1.expect)(results).toHaveLength(1);
|
|
124
|
+
(0, vitest_1.expect)(results[0].verdict).toBe('pass');
|
|
125
|
+
(0, vitest_1.expect)(deploys).toHaveLength(1);
|
|
126
|
+
(0, vitest_1.expect)(deploys[0].url).toBe('https://preview.example.com');
|
|
127
|
+
});
|
|
128
|
+
(0, vitest_1.it)('returns empty artifacts for plain prose', () => {
|
|
129
|
+
const { diffs, results, deploys } = (0, artifacts_1.extractArtifacts)('Everything is green. Nothing else to do.');
|
|
130
|
+
(0, vitest_1.expect)(diffs).toHaveLength(0);
|
|
131
|
+
(0, vitest_1.expect)(results).toHaveLength(0);
|
|
132
|
+
(0, vitest_1.expect)(deploys).toHaveLength(0);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
(0, vitest_1.describe)('extractDeployUrls', () => {
|
|
136
|
+
(0, vitest_1.it)('is safe on empty input', () => {
|
|
137
|
+
(0, vitest_1.expect)((0, artifacts_1.extractDeployUrls)('')).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
});
|