@geminilight/mindos 0.6.71 → 0.6.73
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/_standalone/.mindos-build-version +1 -1
- package/_standalone/.next/BUILD_ID +1 -1
- package/_standalone/.next/app-path-routes-manifest.json +27 -27
- package/_standalone/.next/build-manifest.json +3 -3
- package/_standalone/.next/cache/.previewinfo +1 -1
- package/_standalone/.next/cache/.rscinfo +1 -1
- package/_standalone/.next/cache/config.json +3 -3
- package/_standalone/.next/prerender-manifest.json +3 -3
- package/_standalone/.next/react-loadable-manifest.json +4 -4
- package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error.html +2 -2
- package/_standalone/.next/server/app/_global-error.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/_standalone/.next/server/app/_not-found/page.js +1 -1
- package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/page.js +1 -1
- package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/copy-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/channels/verify/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/connect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/embedding/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/test/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/webhook/feishu/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/im/webhook-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/inbox/clip/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/lint/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/direct-tools/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/tools/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/space-overview/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changelog/page.js +1 -1
- package/_standalone/.next/server/app/changelog/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changes/page.js +1 -1
- package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/page.js +1 -1
- package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/explore/page.js +1 -1
- package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/help/page.js +1 -1
- package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/login/page.js +1 -1
- package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/page.js +1 -1
- package/_standalone/.next/server/app/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/setup/page.js +1 -1
- package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/trash/page.js +2 -2
- package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
- package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app-paths-manifest.json +27 -27
- package/_standalone/.next/server/chunks/{3311.js → 2449.js} +2 -2
- package/_standalone/.next/server/chunks/5299.js +1 -1
- package/_standalone/.next/server/chunks/6022.js +34 -34
- package/_standalone/.next/server/middleware-build-manifest.js +1 -1
- package/_standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/_standalone/.next/server/pages/500.html +2 -2
- package/_standalone/.next/server/server-reference-manifest.js +1 -1
- package/_standalone/.next/server/server-reference-manifest.json +1 -1
- package/_standalone/.next/static/chunks/{7143.879daa87569c5b02.js → 4094.09364c01df411380.js} +1 -1
- package/_standalone/.next/static/chunks/{5795.d9099a1afecd6047.js → 5331.c89084fd7f67887d.js} +2 -2
- package/_standalone/.next/static/chunks/app/{layout-a344709b8447be75.js → layout-fcbde5bee626d21a.js} +63 -63
- package/_standalone/.next/static/chunks/app/trash/page-e623ff0ab35de002.js +1 -0
- package/_standalone/.next/static/chunks/app/view/[...path]/page-49c4eff6ffdb5168.js +12 -0
- package/_standalone/.next/static/chunks/{webpack-2f2787d3469d3df1.js → webpack-dc486b68118d1328.js} +1 -1
- package/_standalone/.next/trace +72 -72
- package/_standalone/package-lock.json +2 -2
- package/_standalone/package.json +1 -1
- package/app/package.json +1 -1
- package/package.json +1 -1
- package/_standalone/.next/static/chunks/app/trash/page-0907fdd06a4467de.js +0 -1
- package/_standalone/.next/static/chunks/app/view/[...path]/page-f53ce199b4a4bbb5.js +0 -12
- package/browser-extension/README.md +0 -160
- package/browser-extension/build.mjs +0 -63
- package/browser-extension/extension/background/service-worker.js +0 -1
- package/browser-extension/extension/content/extractor.js +0 -2
- package/browser-extension/extension/icons/icon-128.png +0 -0
- package/browser-extension/extension/icons/icon-128.svg +0 -4
- package/browser-extension/extension/icons/icon-16.png +0 -0
- package/browser-extension/extension/icons/icon-16.svg +0 -4
- package/browser-extension/extension/icons/icon-32.png +0 -0
- package/browser-extension/extension/icons/icon-32.svg +0 -4
- package/browser-extension/extension/icons/icon-48.png +0 -0
- package/browser-extension/extension/icons/icon-48.svg +0 -4
- package/browser-extension/extension/manifest.json +0 -47
- package/browser-extension/extension/popup/popup.css +0 -510
- package/browser-extension/extension/popup/popup.html +0 -128
- package/browser-extension/extension/popup/popup.js +0 -73
- package/browser-extension/package-lock.json +0 -617
- package/browser-extension/package.json +0 -21
- package/browser-extension/scripts/gen-icons.sh +0 -38
- package/browser-extension/src/background/service-worker.ts +0 -27
- package/browser-extension/src/content/extractor.ts +0 -44
- package/browser-extension/src/icons/icon-128.png +0 -0
- package/browser-extension/src/icons/icon-128.svg +0 -4
- package/browser-extension/src/icons/icon-16.png +0 -0
- package/browser-extension/src/icons/icon-16.svg +0 -4
- package/browser-extension/src/icons/icon-32.png +0 -0
- package/browser-extension/src/icons/icon-32.svg +0 -4
- package/browser-extension/src/icons/icon-48.png +0 -0
- package/browser-extension/src/icons/icon-48.svg +0 -4
- package/browser-extension/src/lib/api.ts +0 -146
- package/browser-extension/src/lib/markdown.ts +0 -68
- package/browser-extension/src/lib/storage.ts +0 -37
- package/browser-extension/src/lib/types.ts +0 -42
- package/browser-extension/src/manifest.json +0 -47
- package/browser-extension/src/popup/popup.css +0 -510
- package/browser-extension/src/popup/popup.html +0 -128
- package/browser-extension/src/popup/popup.ts +0 -416
- package/browser-extension/tsconfig.json +0 -16
- package/tests/e2e/README.md +0 -25
- package/tests/e2e/navigation.spec.ts +0 -14
- package/tests/e2e/playwright.config.ts +0 -14
- package/tests/integration/README.md +0 -25
- package/tests/integration/mcp-contract.test.ts +0 -57
- package/tests/integration/mcp-transport.test.ts +0 -361
- package/tests/integration/package-lock.json +0 -1463
- package/tests/integration/package.json +0 -8
- package/tests/integration/vitest.config.ts +0 -11
- package/tests/security-hardening.test.ts +0 -456
- package/tests/unit/build-integrity.test.ts +0 -137
- package/tests/unit/cli-build.test.ts +0 -180
- package/tests/unit/cli-config.test.ts +0 -257
- package/tests/unit/cli-mcp-install-toml.test.ts +0 -586
- package/tests/unit/cli-mcp-install.test.ts +0 -123
- package/tests/unit/cli-mcp-stdio-default.test.ts +0 -180
- package/tests/unit/cli-modules-load.test.ts +0 -64
- package/tests/unit/cli-port.test.ts +0 -87
- package/tests/unit/cli-skill-auto-copy.test.ts +0 -260
- package/tests/unit/cli-smoke.test.ts +0 -88
- package/tests/unit/cli-uninstall.test.ts +0 -218
- package/tests/unit/cli-update-root.test.ts +0 -89
- package/tests/unit/cli-user-flow-sim.test.ts +0 -506
- package/tests/unit/cli-wait-hint.test.ts +0 -86
- package/tests/unit/custom-agents.test.ts +0 -478
- package/tests/unit/dep-safety.test.ts +0 -126
- package/tests/unit/detect-system-lang.test.ts +0 -122
- package/tests/unit/mcp-build.test.ts +0 -162
- package/tests/unit/setup-needs-restart.test.ts +0 -139
- package/tests/unit/stop-restart.test.ts +0 -393
- package/tests/unit/vitest.config.ts +0 -8
- /package/_standalone/.next/static/{w5bqzZbd2_vdoPRB0JQ_I → Dn8EHqUedSzanCfrM8WWS}/_buildManifest.js +0 -0
- /package/_standalone/.next/static/{w5bqzZbd2_vdoPRB0JQ_I → Dn8EHqUedSzanCfrM8WWS}/_ssgManifest.js +0 -0
|
@@ -1,506 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* End-to-end user flow simulation for all agent types.
|
|
8
|
-
*
|
|
9
|
-
* Simulates the real code paths that run when a user does:
|
|
10
|
-
* 1. `mindos mcp install <agent>` (CLI)
|
|
11
|
-
* 2. Frontend "Install Selected" button (API)
|
|
12
|
-
* 3. `mindos setup` wizard (setup.js)
|
|
13
|
-
* 4. Frontend snippet display (mcp-snippets)
|
|
14
|
-
*
|
|
15
|
-
* Each test creates real temp directories and files, calls the same logic
|
|
16
|
-
* the production code uses, and verifies the output files.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
let tempDir: string;
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mindos-userflow-'));
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
afterEach(() => {
|
|
26
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// ── Helpers (mirrors production code exactly) ────────────────────────────
|
|
30
|
-
|
|
31
|
-
/** Pure-Node.js recursive copy (from bin/lib/mcp-install.js) */
|
|
32
|
-
function copyDirSync(src: string, dst: string) {
|
|
33
|
-
fs.mkdirSync(dst, { recursive: true });
|
|
34
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
35
|
-
const s = path.join(src, entry.name);
|
|
36
|
-
const d = path.join(dst, entry.name);
|
|
37
|
-
if (entry.isDirectory()) {
|
|
38
|
-
copyDirSync(s, d);
|
|
39
|
-
} else {
|
|
40
|
-
fs.copyFileSync(s, d);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Build MCP entry (from bin/lib/mcp-install.js:340-344) */
|
|
46
|
-
function buildEntry(transport: string, url?: string, token?: string) {
|
|
47
|
-
return transport === 'stdio'
|
|
48
|
-
? { type: 'stdio', command: 'mindos', args: ['mcp'], env: { MCP_TRANSPORT: 'stdio' } }
|
|
49
|
-
: token
|
|
50
|
-
? { url, headers: { Authorization: `Bearer ${token}` } }
|
|
51
|
-
: { url };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** TOML builder (from bin/lib/toml.js) */
|
|
55
|
-
function buildTomlEntry(sectionKey: string, serverName: string, entry: Record<string, unknown>): string {
|
|
56
|
-
const lines: string[] = [];
|
|
57
|
-
lines.push(`[${sectionKey}.${serverName}]`);
|
|
58
|
-
if (entry.type != null) lines.push(`type = "${entry.type}"`);
|
|
59
|
-
if (entry.command != null) lines.push(`command = "${entry.command}"`);
|
|
60
|
-
if (entry.url != null) lines.push(`url = "${entry.url}"`);
|
|
61
|
-
if (Array.isArray(entry.args)) {
|
|
62
|
-
lines.push(`args = [${entry.args.map((a: string) => `"${a}"`).join(', ')}]`);
|
|
63
|
-
}
|
|
64
|
-
if (entry.env && typeof entry.env === 'object') {
|
|
65
|
-
lines.push('');
|
|
66
|
-
lines.push(`[${sectionKey}.${serverName}.env]`);
|
|
67
|
-
for (const [k, v] of Object.entries(entry.env)) {
|
|
68
|
-
lines.push(`${k} = "${v}"`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (entry.headers && typeof entry.headers === 'object') {
|
|
72
|
-
lines.push('');
|
|
73
|
-
lines.push(`[${sectionKey}.${serverName}.headers]`);
|
|
74
|
-
for (const [k, v] of Object.entries(entry.headers)) {
|
|
75
|
-
lines.push(`${k} = "${v}"`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return lines.join('\n');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function mergeTomlEntry(existing: string, sectionKey: string, serverName: string, entry: Record<string, unknown>): string {
|
|
82
|
-
const sectionHeader = `[${sectionKey}.${serverName}]`;
|
|
83
|
-
const envHeader = `[${sectionKey}.${serverName}.env]`;
|
|
84
|
-
const headersHeader = `[${sectionKey}.${serverName}.headers]`;
|
|
85
|
-
const newBlock = buildTomlEntry(sectionKey, serverName, entry);
|
|
86
|
-
const lines = existing.split('\n');
|
|
87
|
-
const result: string[] = [];
|
|
88
|
-
let skipping = false;
|
|
89
|
-
for (const line of lines) {
|
|
90
|
-
const trimmed = line.trim();
|
|
91
|
-
if (trimmed === sectionHeader || trimmed === envHeader || trimmed === headersHeader) {
|
|
92
|
-
skipping = true; continue;
|
|
93
|
-
}
|
|
94
|
-
if (skipping && trimmed.startsWith('[')) skipping = false;
|
|
95
|
-
if (!skipping) result.push(line);
|
|
96
|
-
}
|
|
97
|
-
while (result.length > 0 && result[result.length - 1].trim() === '') result.pop();
|
|
98
|
-
result.push('');
|
|
99
|
-
result.push(newBlock);
|
|
100
|
-
result.push('');
|
|
101
|
-
return result.join('\n');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** Create a realistic skill source directory */
|
|
105
|
-
function createSkillSource(skillName: string): string {
|
|
106
|
-
const dir = path.join(tempDir, 'project', 'skills', skillName);
|
|
107
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
108
|
-
fs.writeFileSync(path.join(dir, 'SKILL.md'), `---\nname: ${skillName}\n---\n# ${skillName} Skill`);
|
|
109
|
-
fs.writeFileSync(path.join(dir, 'README.md'), `# ${skillName}`);
|
|
110
|
-
return dir;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ── Agent configs (mirrors MCP_AGENTS) ───────────────────────────────────
|
|
114
|
-
|
|
115
|
-
const AGENTS = {
|
|
116
|
-
'workbuddy': { name: 'WorkBuddy', global: '.workbuddy/mcp.json', key: 'mcpServers', format: 'json' as const, skillMode: 'unsupported' },
|
|
117
|
-
'qclaw': { name: 'QClaw', global: '.qclaw/mcp.json', key: 'mcpServers', format: 'json' as const, skillMode: 'unsupported' },
|
|
118
|
-
'lingma': { name: 'Lingma', global: '.lingma/mcp.json', key: 'mcpServers', format: 'json' as const, skillMode: 'unsupported' },
|
|
119
|
-
'codex': { name: 'Codex', global: '.codex/config.toml', key: 'mcp_servers', format: 'toml' as const, skillMode: 'universal' },
|
|
120
|
-
'claude-code': { name: 'Claude Code', global: '.claude.json', key: 'mcpServers', format: 'json' as const, skillMode: 'additional' },
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
124
|
-
// Flow 1: `mindos mcp install workbuddy` (CLI, unsupported agent)
|
|
125
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
126
|
-
|
|
127
|
-
describe('Flow 1: CLI — mindos mcp install workbuddy', () => {
|
|
128
|
-
it('Step 1: writes MCP config to ~/.workbuddy/mcp.json', () => {
|
|
129
|
-
const agent = AGENTS['workbuddy'];
|
|
130
|
-
const configPath = path.join(tempDir, agent.global);
|
|
131
|
-
const entry = buildEntry('stdio');
|
|
132
|
-
|
|
133
|
-
// Simulate CLI write (mcp-install.js:387-399)
|
|
134
|
-
const dir = path.dirname(configPath);
|
|
135
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
136
|
-
const config: Record<string, Record<string, unknown>> = {};
|
|
137
|
-
config[agent.key] = { mindos: entry };
|
|
138
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
139
|
-
|
|
140
|
-
// Verify
|
|
141
|
-
const written = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
142
|
-
expect(written.mcpServers.mindos.type).toBe('stdio');
|
|
143
|
-
expect(written.mcpServers.mindos.command).toBe('mindos');
|
|
144
|
-
expect(written.mcpServers.mindos.args).toEqual(['mcp']);
|
|
145
|
-
expect(written.mcpServers.mindos.env.MCP_TRANSPORT).toBe('stdio');
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('Step 2: auto-copies skill to ~/.workbuddy/skills/mindos/', () => {
|
|
149
|
-
const skillSrc = createSkillSource('mindos');
|
|
150
|
-
const agentDir = path.join(tempDir, '.workbuddy');
|
|
151
|
-
fs.mkdirSync(agentDir, { recursive: true });
|
|
152
|
-
|
|
153
|
-
// Simulate autoInstallSkillForAgent (mcp-install.js:31-59)
|
|
154
|
-
const targetDir = path.join(agentDir, 'skills', 'mindos');
|
|
155
|
-
expect(fs.existsSync(targetDir)).toBe(false);
|
|
156
|
-
copyDirSync(skillSrc, targetDir);
|
|
157
|
-
|
|
158
|
-
// Verify
|
|
159
|
-
expect(fs.existsSync(path.join(targetDir, 'SKILL.md'))).toBe(true);
|
|
160
|
-
expect(fs.readFileSync(path.join(targetDir, 'SKILL.md'), 'utf-8')).toContain('mindos Skill');
|
|
161
|
-
expect(fs.existsSync(path.join(targetDir, 'README.md'))).toBe(true);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('Step 3: skips copy if skill already exists', () => {
|
|
165
|
-
const skillSrc = createSkillSource('mindos');
|
|
166
|
-
const targetDir = path.join(tempDir, '.workbuddy', 'skills', 'mindos');
|
|
167
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
168
|
-
fs.writeFileSync(path.join(targetDir, 'SKILL.md'), 'user-customized');
|
|
169
|
-
|
|
170
|
-
// Guard check (mcp-install.js:51)
|
|
171
|
-
if (!fs.existsSync(targetDir)) {
|
|
172
|
-
copyDirSync(skillSrc, targetDir);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
expect(fs.readFileSync(path.join(targetDir, 'SKILL.md'), 'utf-8')).toBe('user-customized');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('End-to-end: MCP config + skill copy in one flow', () => {
|
|
179
|
-
const skillSrc = createSkillSource('mindos');
|
|
180
|
-
const agentDir = path.join(tempDir, '.workbuddy');
|
|
181
|
-
fs.mkdirSync(agentDir, { recursive: true });
|
|
182
|
-
|
|
183
|
-
// Step A: write MCP config
|
|
184
|
-
const configPath = path.join(agentDir, 'mcp.json');
|
|
185
|
-
const entry = buildEntry('stdio');
|
|
186
|
-
fs.writeFileSync(configPath, JSON.stringify({ mcpServers: { mindos: entry } }, null, 2) + '\n');
|
|
187
|
-
|
|
188
|
-
// Step B: auto-copy skill
|
|
189
|
-
const targetSkill = path.join(agentDir, 'skills', 'mindos');
|
|
190
|
-
copyDirSync(skillSrc, targetSkill);
|
|
191
|
-
|
|
192
|
-
// Verify both exist
|
|
193
|
-
expect(fs.existsSync(configPath)).toBe(true);
|
|
194
|
-
expect(fs.existsSync(path.join(targetSkill, 'SKILL.md'))).toBe(true);
|
|
195
|
-
|
|
196
|
-
// Verify MCP config is valid JSON
|
|
197
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
198
|
-
expect(config.mcpServers.mindos.type).toBe('stdio');
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
203
|
-
// Flow 2: `mindos mcp install codex` (CLI, TOML agent)
|
|
204
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
205
|
-
|
|
206
|
-
describe('Flow 2: CLI — mindos mcp install codex (TOML)', () => {
|
|
207
|
-
it('writes valid TOML to ~/.codex/config.toml', () => {
|
|
208
|
-
const configPath = path.join(tempDir, '.codex', 'config.toml');
|
|
209
|
-
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
210
|
-
|
|
211
|
-
const entry = buildEntry('stdio');
|
|
212
|
-
const merged = mergeTomlEntry('', 'mcp_servers', 'mindos', entry);
|
|
213
|
-
fs.writeFileSync(configPath, merged, 'utf-8');
|
|
214
|
-
|
|
215
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
216
|
-
expect(content).toContain('[mcp_servers.mindos]');
|
|
217
|
-
expect(content).toContain('type = "stdio"');
|
|
218
|
-
expect(content).toContain('command = "mindos"');
|
|
219
|
-
expect(content).toContain('[mcp_servers.mindos.env]');
|
|
220
|
-
expect(content).toContain('MCP_TRANSPORT = "stdio"');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('preserves existing codex config when adding MindOS', () => {
|
|
224
|
-
const configPath = path.join(tempDir, '.codex', 'config.toml');
|
|
225
|
-
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
226
|
-
|
|
227
|
-
const existing = 'model = "o3"\napproval_mode = "suggest"\n\n[mcp_servers.filesystem]\ntype = "stdio"\ncommand = "npx"\n';
|
|
228
|
-
fs.writeFileSync(configPath, existing);
|
|
229
|
-
|
|
230
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
231
|
-
const entry = buildEntry('stdio');
|
|
232
|
-
const merged = mergeTomlEntry(content, 'mcp_servers', 'mindos', entry);
|
|
233
|
-
fs.writeFileSync(configPath, merged, 'utf-8');
|
|
234
|
-
|
|
235
|
-
const final = fs.readFileSync(configPath, 'utf-8');
|
|
236
|
-
expect(final).toContain('model = "o3"');
|
|
237
|
-
expect(final).toContain('[mcp_servers.filesystem]');
|
|
238
|
-
expect(final).toContain('[mcp_servers.mindos]');
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('does NOT auto-copy skill (codex is universal, not unsupported)', () => {
|
|
242
|
-
// codex has mode: 'universal', so autoInstallSkillForAgent should return null
|
|
243
|
-
const agent = AGENTS['codex'];
|
|
244
|
-
expect(agent.skillMode).toBe('universal');
|
|
245
|
-
// The check: reg.mode !== 'unsupported' → return null
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
250
|
-
// Flow 3: Frontend "Install Selected" (API route, unsupported agent)
|
|
251
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
252
|
-
|
|
253
|
-
describe('Flow 3: Frontend API — install unsupported agent', () => {
|
|
254
|
-
it('writes MCP config + copies skill in one API call', () => {
|
|
255
|
-
const skillSrc = createSkillSource('mindos');
|
|
256
|
-
|
|
257
|
-
for (const agentKey of ['workbuddy', 'qclaw', 'lingma'] as const) {
|
|
258
|
-
const agent = AGENTS[agentKey];
|
|
259
|
-
const agentHome = path.join(tempDir, path.dirname(agent.global));
|
|
260
|
-
fs.mkdirSync(agentHome, { recursive: true });
|
|
261
|
-
|
|
262
|
-
// Step A: write MCP config (API route.ts:187-203)
|
|
263
|
-
const configPath = path.join(tempDir, agent.global);
|
|
264
|
-
const entry = buildEntry('stdio');
|
|
265
|
-
const config: Record<string, Record<string, unknown>> = {};
|
|
266
|
-
config[agent.key] = { mindos: entry };
|
|
267
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
268
|
-
|
|
269
|
-
// Step B: auto-copy skill (API route.ts:218-231)
|
|
270
|
-
const skillTarget = path.join(agentHome, 'skills', 'mindos');
|
|
271
|
-
if (!fs.existsSync(skillTarget)) {
|
|
272
|
-
copyDirSync(skillSrc, skillTarget);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Verify MCP config
|
|
276
|
-
const written = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
277
|
-
expect(written.mcpServers.mindos.type).toBe('stdio');
|
|
278
|
-
|
|
279
|
-
// Verify skill
|
|
280
|
-
expect(fs.existsSync(path.join(skillTarget, 'SKILL.md'))).toBe(true);
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
it('MCP config for workbuddy is valid and complete', () => {
|
|
285
|
-
const configPath = path.join(tempDir, '.workbuddy', 'mcp.json');
|
|
286
|
-
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
287
|
-
|
|
288
|
-
const entry = buildEntry('stdio');
|
|
289
|
-
fs.writeFileSync(configPath, JSON.stringify({ mcpServers: { mindos: entry } }, null, 2) + '\n');
|
|
290
|
-
|
|
291
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
292
|
-
|
|
293
|
-
// Full struct validation
|
|
294
|
-
expect(Object.keys(config)).toEqual(['mcpServers']);
|
|
295
|
-
expect(Object.keys(config.mcpServers)).toEqual(['mindos']);
|
|
296
|
-
expect(config.mcpServers.mindos).toEqual({
|
|
297
|
-
type: 'stdio',
|
|
298
|
-
command: 'mindos',
|
|
299
|
-
args: ['mcp'],
|
|
300
|
-
env: { MCP_TRANSPORT: 'stdio' },
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
it('HTTP transport with token works for unsupported agents', () => {
|
|
305
|
-
const configPath = path.join(tempDir, '.qclaw', 'mcp.json');
|
|
306
|
-
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
307
|
-
|
|
308
|
-
const entry = buildEntry('http', 'http://192.168.1.100:8781/mcp', 'secret-token');
|
|
309
|
-
fs.writeFileSync(configPath, JSON.stringify({ mcpServers: { mindos: entry } }, null, 2) + '\n');
|
|
310
|
-
|
|
311
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
312
|
-
expect(config.mcpServers.mindos.url).toBe('http://192.168.1.100:8781/mcp');
|
|
313
|
-
expect(config.mcpServers.mindos.headers.Authorization).toBe('Bearer secret-token');
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
318
|
-
// Flow 4: Frontend snippet display
|
|
319
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
320
|
-
|
|
321
|
-
describe('Flow 4: Frontend — config snippet generation', () => {
|
|
322
|
-
it('WorkBuddy stdio snippet is valid JSON', () => {
|
|
323
|
-
// Simulate generateStdioSnippet for WorkBuddy (format: json, key: mcpServers)
|
|
324
|
-
const stdioEntry = { type: 'stdio', command: 'mindos', args: ['mcp'] };
|
|
325
|
-
const snippet = JSON.stringify({ mcpServers: { mindos: stdioEntry } }, null, 2);
|
|
326
|
-
|
|
327
|
-
// Verify it's parseable and correct
|
|
328
|
-
const parsed = JSON.parse(snippet);
|
|
329
|
-
expect(parsed.mcpServers.mindos.type).toBe('stdio');
|
|
330
|
-
expect(parsed.mcpServers.mindos.command).toBe('mindos');
|
|
331
|
-
expect(snippet).toContain('"mcpServers"');
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
it('Codex stdio snippet is valid TOML', () => {
|
|
335
|
-
// Simulate generateStdioSnippet for Codex (format: toml, key: mcp_servers)
|
|
336
|
-
const lines = [
|
|
337
|
-
'[mcp_servers.mindos]',
|
|
338
|
-
'command = "mindos"',
|
|
339
|
-
'args = ["mcp"]',
|
|
340
|
-
'',
|
|
341
|
-
'[mcp_servers.mindos.env]',
|
|
342
|
-
'MCP_TRANSPORT = "stdio"',
|
|
343
|
-
];
|
|
344
|
-
const snippet = lines.join('\n');
|
|
345
|
-
|
|
346
|
-
expect(snippet).toContain('[mcp_servers.mindos]');
|
|
347
|
-
expect(snippet).toContain('command = "mindos"');
|
|
348
|
-
expect(snippet).toContain('MCP_TRANSPORT = "stdio"');
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it('WorkBuddy HTTP snippet includes auth header', () => {
|
|
352
|
-
const httpEntry = { url: 'http://192.168.1.100:8781/mcp', headers: { Authorization: 'Bearer tok-123' } };
|
|
353
|
-
const snippet = JSON.stringify({ mcpServers: { mindos: httpEntry } }, null, 2);
|
|
354
|
-
|
|
355
|
-
expect(snippet).toContain('"url"');
|
|
356
|
-
expect(snippet).toContain('192.168.1.100');
|
|
357
|
-
expect(snippet).toContain('Bearer tok-123');
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
362
|
-
// Flow 5: setup.js wizard — cpSync skill copy
|
|
363
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
364
|
-
|
|
365
|
-
describe('Flow 5: Setup wizard — cpSync skill install', () => {
|
|
366
|
-
it('copies skill via cpSync for unsupported agents', () => {
|
|
367
|
-
const skillSrc = createSkillSource('mindos');
|
|
368
|
-
|
|
369
|
-
for (const agent of ['.workbuddy', '.qclaw', '.lingma']) {
|
|
370
|
-
const agentDir = path.join(tempDir, agent);
|
|
371
|
-
fs.mkdirSync(agentDir, { recursive: true });
|
|
372
|
-
const targetSkillDir = path.join(agentDir, 'skills', 'mindos');
|
|
373
|
-
|
|
374
|
-
// Simulate setup.js: cpSync(skillSourceDir, targetSkillDir, { recursive: true })
|
|
375
|
-
fs.cpSync(skillSrc, targetSkillDir, { recursive: true });
|
|
376
|
-
|
|
377
|
-
expect(fs.existsSync(path.join(targetSkillDir, 'SKILL.md'))).toBe(true);
|
|
378
|
-
expect(fs.readFileSync(path.join(targetSkillDir, 'SKILL.md'), 'utf-8')).toContain('mindos Skill');
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
it('skips if target already exists', () => {
|
|
383
|
-
const skillSrc = createSkillSource('mindos');
|
|
384
|
-
const targetSkillDir = path.join(tempDir, '.workbuddy', 'skills', 'mindos');
|
|
385
|
-
fs.mkdirSync(targetSkillDir, { recursive: true });
|
|
386
|
-
fs.writeFileSync(path.join(targetSkillDir, 'SKILL.md'), 'custom');
|
|
387
|
-
|
|
388
|
-
// Guard check (setup.js): if (!existsSync(targetSkillDir))
|
|
389
|
-
if (!fs.existsSync(targetSkillDir)) {
|
|
390
|
-
fs.cpSync(skillSrc, targetSkillDir, { recursive: true });
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
expect(fs.readFileSync(path.join(targetSkillDir, 'SKILL.md'), 'utf-8')).toBe('custom');
|
|
394
|
-
});
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
398
|
-
// Flow 6: Update existing config (re-install scenario)
|
|
399
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
400
|
-
|
|
401
|
-
describe('Flow 6: Re-install — update existing config', () => {
|
|
402
|
-
it('WorkBuddy: updates MCP config without losing other servers', () => {
|
|
403
|
-
const configPath = path.join(tempDir, '.workbuddy', 'mcp.json');
|
|
404
|
-
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
405
|
-
|
|
406
|
-
// Pre-existing config with another MCP server
|
|
407
|
-
fs.writeFileSync(configPath, JSON.stringify({
|
|
408
|
-
mcpServers: {
|
|
409
|
-
'other-server': { url: 'http://other:9000' },
|
|
410
|
-
mindos: { url: 'http://old:8000' },
|
|
411
|
-
},
|
|
412
|
-
}, null, 2));
|
|
413
|
-
|
|
414
|
-
// Re-install with stdio
|
|
415
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
416
|
-
config.mcpServers.mindos = buildEntry('stdio');
|
|
417
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
418
|
-
|
|
419
|
-
const final = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
420
|
-
expect(final.mcpServers.mindos.type).toBe('stdio');
|
|
421
|
-
expect(final.mcpServers['other-server'].url).toBe('http://other:9000');
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
it('Codex: updates TOML config without losing other sections', () => {
|
|
425
|
-
const configPath = path.join(tempDir, '.codex', 'config.toml');
|
|
426
|
-
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
427
|
-
|
|
428
|
-
const existing = [
|
|
429
|
-
'model = "o3"',
|
|
430
|
-
'',
|
|
431
|
-
'[mcp_servers.mindos]',
|
|
432
|
-
'type = "http"',
|
|
433
|
-
'url = "http://old:8000"',
|
|
434
|
-
'',
|
|
435
|
-
'[mcp_servers.other]',
|
|
436
|
-
'type = "stdio"',
|
|
437
|
-
'',
|
|
438
|
-
].join('\n');
|
|
439
|
-
fs.writeFileSync(configPath, existing);
|
|
440
|
-
|
|
441
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
442
|
-
const merged = mergeTomlEntry(content, 'mcp_servers', 'mindos', buildEntry('stdio'));
|
|
443
|
-
fs.writeFileSync(configPath, merged);
|
|
444
|
-
|
|
445
|
-
const final = fs.readFileSync(configPath, 'utf-8');
|
|
446
|
-
expect(final).toContain('model = "o3"');
|
|
447
|
-
expect(final).toContain('[mcp_servers.other]');
|
|
448
|
-
expect(final).toContain('[mcp_servers.mindos]');
|
|
449
|
-
expect(final).toContain('type = "stdio"');
|
|
450
|
-
expect(final).not.toContain('http://old:8000');
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
it('WorkBuddy: skill not overwritten on re-install', () => {
|
|
454
|
-
const skillSrc = createSkillSource('mindos');
|
|
455
|
-
const targetSkill = path.join(tempDir, '.workbuddy', 'skills', 'mindos');
|
|
456
|
-
|
|
457
|
-
// First install
|
|
458
|
-
copyDirSync(skillSrc, targetSkill);
|
|
459
|
-
// User customizes
|
|
460
|
-
fs.writeFileSync(path.join(targetSkill, 'SKILL.md'), 'user-patched');
|
|
461
|
-
|
|
462
|
-
// Re-install: guard prevents overwrite
|
|
463
|
-
if (!fs.existsSync(targetSkill)) {
|
|
464
|
-
copyDirSync(skillSrc, targetSkill);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
expect(fs.readFileSync(path.join(targetSkill, 'SKILL.md'), 'utf-8')).toBe('user-patched');
|
|
468
|
-
});
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
472
|
-
// Flow 7: Cross-agent consistency check
|
|
473
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
474
|
-
|
|
475
|
-
describe('Flow 7: Cross-agent consistency — all agents produce valid configs', () => {
|
|
476
|
-
it('all JSON agents produce identical MCP entry structure', () => {
|
|
477
|
-
const entry = buildEntry('stdio');
|
|
478
|
-
|
|
479
|
-
for (const [key, agent] of Object.entries(AGENTS)) {
|
|
480
|
-
if (agent.format === 'toml') continue;
|
|
481
|
-
|
|
482
|
-
const configPath = path.join(tempDir, `test-${key}.json`);
|
|
483
|
-
const config: Record<string, Record<string, unknown>> = {};
|
|
484
|
-
config[agent.key] = { mindos: entry };
|
|
485
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
486
|
-
|
|
487
|
-
const parsed = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
488
|
-
expect(parsed[agent.key].mindos.type).toBe('stdio');
|
|
489
|
-
expect(parsed[agent.key].mindos.command).toBe('mindos');
|
|
490
|
-
expect(parsed[agent.key].mindos.args).toEqual(['mcp']);
|
|
491
|
-
}
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
it('TOML agents produce valid TOML entry', () => {
|
|
495
|
-
const entry = buildEntry('stdio');
|
|
496
|
-
|
|
497
|
-
for (const [key, agent] of Object.entries(AGENTS)) {
|
|
498
|
-
if (agent.format !== 'toml') continue;
|
|
499
|
-
|
|
500
|
-
const merged = mergeTomlEntry('', agent.key, 'mindos', entry);
|
|
501
|
-
expect(merged).toContain(`[${agent.key}.mindos]`);
|
|
502
|
-
expect(merged).toContain('type = "stdio"');
|
|
503
|
-
expect(merged).toContain('command = "mindos"');
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
});
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { parseLogHint } from '../../bin/lib/gateway.js';
|
|
3
|
-
|
|
4
|
-
describe('parseLogHint', () => {
|
|
5
|
-
// ── npm install lines ──
|
|
6
|
-
it('detects "Installing app dependencies"', () => {
|
|
7
|
-
expect(parseLogHint('Installing app dependencies (first run)...\n')).toBe('installing dependencies…');
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('detects "Updating app dependencies"', () => {
|
|
11
|
-
expect(parseLogHint('Updating app dependencies (package-lock.json changed)...\n')).toBe('updating dependencies…');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('detects "added N packages"', () => {
|
|
15
|
-
expect(parseLogHint('added 387 packages in 12s')).toBe('dependencies installed');
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('detects "Installing MCP dependencies"', () => {
|
|
19
|
-
expect(parseLogHint('Installing MCP dependencies (first run)...\n')).toBe('installing MCP…');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// ── next build lines ──
|
|
23
|
-
it('detects "Building MindOS"', () => {
|
|
24
|
-
expect(parseLogHint('Building MindOS (first run or new version detected)...\n')).toBe('building app…');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('detects "Creating an optimized production build"', () => {
|
|
28
|
-
expect(parseLogHint(' Creating an optimized production build ...')).toBe('building app…');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('detects "Compiling"', () => {
|
|
32
|
-
expect(parseLogHint(' ✓ Compiled /api/health in 320ms')).toBe('compiling…');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('detects "Collecting page data"', () => {
|
|
36
|
-
expect(parseLogHint(' Collecting page data ...')).toBe('collecting page data…');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('detects "Generating static pages"', () => {
|
|
40
|
-
expect(parseLogHint(' Generating static pages (0/8) ...')).toBe('generating pages…');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('detects "Finalizing page optimization"', () => {
|
|
44
|
-
expect(parseLogHint(' Finalizing page optimization ...')).toBe('optimizing…');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('detects route bundle lines', () => {
|
|
48
|
-
expect(parseLogHint('├ ○ /api/health 0.3 kB')).toBe('bundling routes…');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// ── next start lines ──
|
|
52
|
-
it('detects Next.js banner', () => {
|
|
53
|
-
expect(parseLogHint('▲ Next.js 15.2.4')).toBe('starting server…');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('detects "Ready in"', () => {
|
|
57
|
-
expect(parseLogHint(' Ready in 1.8s')).toBe('starting server…');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// ── should NOT match ──
|
|
61
|
-
it('returns null for empty lines', () => {
|
|
62
|
-
expect(parseLogHint('')).toBeNull();
|
|
63
|
-
expect(parseLogHint(' ')).toBeNull();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('returns null for npm warn/error lines', () => {
|
|
67
|
-
// These don't match any pattern since we removed the error catch-all
|
|
68
|
-
expect(parseLogHint('npm warn deprecated inflight@1.0.6')).toBeNull();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('returns null for stack trace lines', () => {
|
|
72
|
-
expect(parseLogHint(' at Function._resolveFilename (node:internal/modules/cjs/loader:1383:15)')).toBeNull();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('returns null for random log lines', () => {
|
|
76
|
-
expect(parseLogHint('Server listening on port 3456')).toBeNull();
|
|
77
|
-
expect(parseLogHint('[MCP] Request: tools/list')).toBeNull();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// "Installing MCP dependencies" should match MCP, not generic dependencies
|
|
81
|
-
it('prioritizes MCP match for MCP dependencies line', () => {
|
|
82
|
-
// The line contains both "Installing" and "MCP dependencies"
|
|
83
|
-
// "Installing MCP dependencies" should match the MCP-specific rule
|
|
84
|
-
expect(parseLogHint('Installing MCP dependencies (first run)...')).toBe('installing MCP…');
|
|
85
|
-
});
|
|
86
|
-
});
|