@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,260 +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
|
-
* Tests for auto-copy skill logic used by unsupported agents.
|
|
8
|
-
*
|
|
9
|
-
* Validates:
|
|
10
|
-
* 1. copyDirSync — recursive pure-Node.js directory copy (cross-platform)
|
|
11
|
-
* 2. autoInstallSkillForAgent — end-to-end skill copy for unsupported agents
|
|
12
|
-
* 3. Edge cases: existing target, missing source, symlinks, nested dirs
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
let tempDir: string;
|
|
16
|
-
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mindos-skill-copy-test-'));
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// ── copyDirSync (extracted from bin/lib/mcp-install.js for testing) ──────
|
|
26
|
-
|
|
27
|
-
/** Pure-Node.js recursive directory copy — mirrors the CLI implementation. */
|
|
28
|
-
function copyDirSync(src: string, dst: string) {
|
|
29
|
-
fs.mkdirSync(dst, { recursive: true });
|
|
30
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
31
|
-
const s = path.join(src, entry.name);
|
|
32
|
-
const d = path.join(dst, entry.name);
|
|
33
|
-
if (entry.isDirectory()) {
|
|
34
|
-
copyDirSync(s, d);
|
|
35
|
-
} else {
|
|
36
|
-
fs.copyFileSync(s, d);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
describe('copyDirSync (cross-platform recursive copy)', () => {
|
|
42
|
-
it('copies a flat directory with files', () => {
|
|
43
|
-
const src = path.join(tempDir, 'src');
|
|
44
|
-
const dst = path.join(tempDir, 'dst');
|
|
45
|
-
fs.mkdirSync(src);
|
|
46
|
-
fs.writeFileSync(path.join(src, 'SKILL.md'), '# Test Skill\n');
|
|
47
|
-
fs.writeFileSync(path.join(src, 'config.json'), '{"key":"val"}');
|
|
48
|
-
|
|
49
|
-
copyDirSync(src, dst);
|
|
50
|
-
|
|
51
|
-
expect(fs.existsSync(path.join(dst, 'SKILL.md'))).toBe(true);
|
|
52
|
-
expect(fs.existsSync(path.join(dst, 'config.json'))).toBe(true);
|
|
53
|
-
expect(fs.readFileSync(path.join(dst, 'SKILL.md'), 'utf-8')).toBe('# Test Skill\n');
|
|
54
|
-
expect(fs.readFileSync(path.join(dst, 'config.json'), 'utf-8')).toBe('{"key":"val"}');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('copies nested directories recursively', () => {
|
|
58
|
-
const src = path.join(tempDir, 'src');
|
|
59
|
-
fs.mkdirSync(path.join(src, 'sub', 'deep'), { recursive: true });
|
|
60
|
-
fs.writeFileSync(path.join(src, 'top.txt'), 'top');
|
|
61
|
-
fs.writeFileSync(path.join(src, 'sub', 'mid.txt'), 'mid');
|
|
62
|
-
fs.writeFileSync(path.join(src, 'sub', 'deep', 'bottom.txt'), 'bottom');
|
|
63
|
-
|
|
64
|
-
const dst = path.join(tempDir, 'dst');
|
|
65
|
-
copyDirSync(src, dst);
|
|
66
|
-
|
|
67
|
-
expect(fs.readFileSync(path.join(dst, 'top.txt'), 'utf-8')).toBe('top');
|
|
68
|
-
expect(fs.readFileSync(path.join(dst, 'sub', 'mid.txt'), 'utf-8')).toBe('mid');
|
|
69
|
-
expect(fs.readFileSync(path.join(dst, 'sub', 'deep', 'bottom.txt'), 'utf-8')).toBe('bottom');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('creates destination directory if it does not exist', () => {
|
|
73
|
-
const src = path.join(tempDir, 'src');
|
|
74
|
-
fs.mkdirSync(src);
|
|
75
|
-
fs.writeFileSync(path.join(src, 'file.txt'), 'content');
|
|
76
|
-
|
|
77
|
-
const dst = path.join(tempDir, 'non', 'existent', 'dst');
|
|
78
|
-
copyDirSync(src, dst);
|
|
79
|
-
|
|
80
|
-
expect(fs.existsSync(path.join(dst, 'file.txt'))).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('copies empty directories', () => {
|
|
84
|
-
const src = path.join(tempDir, 'src');
|
|
85
|
-
fs.mkdirSync(path.join(src, 'empty-sub'), { recursive: true });
|
|
86
|
-
|
|
87
|
-
const dst = path.join(tempDir, 'dst');
|
|
88
|
-
copyDirSync(src, dst);
|
|
89
|
-
|
|
90
|
-
expect(fs.statSync(path.join(dst, 'empty-sub')).isDirectory()).toBe(true);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('preserves file content for binary-like files', () => {
|
|
94
|
-
const src = path.join(tempDir, 'src');
|
|
95
|
-
fs.mkdirSync(src);
|
|
96
|
-
// Write a buffer with non-UTF8 bytes
|
|
97
|
-
const buf = Buffer.from([0x00, 0x01, 0x02, 0xFF, 0xFE]);
|
|
98
|
-
fs.writeFileSync(path.join(src, 'binary.bin'), buf);
|
|
99
|
-
|
|
100
|
-
const dst = path.join(tempDir, 'dst');
|
|
101
|
-
copyDirSync(src, dst);
|
|
102
|
-
|
|
103
|
-
const copied = fs.readFileSync(path.join(dst, 'binary.bin'));
|
|
104
|
-
expect(Buffer.compare(copied, buf)).toBe(0);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('handles files with special characters in names', () => {
|
|
108
|
-
const src = path.join(tempDir, 'src');
|
|
109
|
-
fs.mkdirSync(src);
|
|
110
|
-
// Spaces, dashes, dots are common in skill files
|
|
111
|
-
fs.writeFileSync(path.join(src, 'my-skill file.v2.md'), 'content');
|
|
112
|
-
|
|
113
|
-
const dst = path.join(tempDir, 'dst');
|
|
114
|
-
copyDirSync(src, dst);
|
|
115
|
-
|
|
116
|
-
expect(fs.readFileSync(path.join(dst, 'my-skill file.v2.md'), 'utf-8')).toBe('content');
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// ── Realistic skill directory copy ───────────────────────────────────────
|
|
121
|
-
|
|
122
|
-
describe('Skill directory copy (realistic structure)', () => {
|
|
123
|
-
it('copies a typical MindOS skill directory', () => {
|
|
124
|
-
// Create a realistic skill directory
|
|
125
|
-
const skillSrc = path.join(tempDir, 'skills', 'mindos');
|
|
126
|
-
fs.mkdirSync(skillSrc, { recursive: true });
|
|
127
|
-
fs.writeFileSync(path.join(skillSrc, 'SKILL.md'), '---\nname: mindos\n---\n# MindOS Skill');
|
|
128
|
-
fs.writeFileSync(path.join(skillSrc, 'README.md'), '# README');
|
|
129
|
-
|
|
130
|
-
// Simulate copying to a WorkBuddy-like agent dir
|
|
131
|
-
const agentDir = path.join(tempDir, '.workbuddy');
|
|
132
|
-
fs.mkdirSync(agentDir);
|
|
133
|
-
const targetSkillDir = path.join(agentDir, 'skills', 'mindos');
|
|
134
|
-
|
|
135
|
-
copyDirSync(skillSrc, targetSkillDir);
|
|
136
|
-
|
|
137
|
-
expect(fs.existsSync(path.join(targetSkillDir, 'SKILL.md'))).toBe(true);
|
|
138
|
-
expect(fs.readFileSync(path.join(targetSkillDir, 'SKILL.md'), 'utf-8')).toContain('MindOS Skill');
|
|
139
|
-
expect(fs.existsSync(path.join(targetSkillDir, 'README.md'))).toBe(true);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('does not overwrite if target already exists (guard check)', () => {
|
|
143
|
-
const skillSrc = path.join(tempDir, 'skills', 'mindos');
|
|
144
|
-
fs.mkdirSync(skillSrc, { recursive: true });
|
|
145
|
-
fs.writeFileSync(path.join(skillSrc, 'SKILL.md'), 'new content');
|
|
146
|
-
|
|
147
|
-
const targetSkillDir = path.join(tempDir, '.agent', 'skills', 'mindos');
|
|
148
|
-
fs.mkdirSync(targetSkillDir, { recursive: true });
|
|
149
|
-
fs.writeFileSync(path.join(targetSkillDir, 'SKILL.md'), 'existing content');
|
|
150
|
-
|
|
151
|
-
// Simulate the guard from autoInstallSkillForAgent: skip if exists
|
|
152
|
-
if (!fs.existsSync(targetSkillDir)) {
|
|
153
|
-
copyDirSync(skillSrc, targetSkillDir);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Existing content should NOT be overwritten
|
|
157
|
-
expect(fs.readFileSync(path.join(targetSkillDir, 'SKILL.md'), 'utf-8')).toBe('existing content');
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('copies to multiple agent directories independently', () => {
|
|
161
|
-
const skillSrc = path.join(tempDir, 'skills', 'mindos');
|
|
162
|
-
fs.mkdirSync(skillSrc, { recursive: true });
|
|
163
|
-
fs.writeFileSync(path.join(skillSrc, 'SKILL.md'), 'shared skill');
|
|
164
|
-
|
|
165
|
-
// Copy to 3 different unsupported agent dirs
|
|
166
|
-
for (const agent of ['workbuddy', 'qclaw', 'lingma']) {
|
|
167
|
-
const target = path.join(tempDir, `.${agent}`, 'skills', 'mindos');
|
|
168
|
-
fs.mkdirSync(path.join(tempDir, `.${agent}`), { recursive: true });
|
|
169
|
-
copyDirSync(skillSrc, target);
|
|
170
|
-
expect(fs.readFileSync(path.join(target, 'SKILL.md'), 'utf-8')).toBe('shared skill');
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('copies Chinese skill variant (mindos-zh) when available', () => {
|
|
175
|
-
// Create both English and Chinese skill variants
|
|
176
|
-
const skillSrcEn = path.join(tempDir, 'skills', 'mindos');
|
|
177
|
-
fs.mkdirSync(skillSrcEn, { recursive: true });
|
|
178
|
-
fs.writeFileSync(path.join(skillSrcEn, 'SKILL.md'), '---\nname: mindos\n---\n# MindOS (English)');
|
|
179
|
-
|
|
180
|
-
const skillSrcZh = path.join(tempDir, 'skills', 'mindos-zh');
|
|
181
|
-
fs.mkdirSync(skillSrcZh, { recursive: true });
|
|
182
|
-
fs.writeFileSync(path.join(skillSrcZh, 'SKILL.md'), '---\nname: mindos-zh\n---\n# MindOS (中文)');
|
|
183
|
-
|
|
184
|
-
// Simulate copying Chinese variant to agent
|
|
185
|
-
const agentDir = path.join(tempDir, '.copaw');
|
|
186
|
-
fs.mkdirSync(agentDir);
|
|
187
|
-
const targetSkillZh = path.join(agentDir, 'skills', 'mindos-zh');
|
|
188
|
-
|
|
189
|
-
copyDirSync(skillSrcZh, targetSkillZh);
|
|
190
|
-
|
|
191
|
-
expect(fs.existsSync(path.join(targetSkillZh, 'SKILL.md'))).toBe(true);
|
|
192
|
-
expect(fs.readFileSync(path.join(targetSkillZh, 'SKILL.md'), 'utf-8')).toContain('中文');
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('supports both English and Chinese skill variants for all unsupported agents', () => {
|
|
196
|
-
// Create both skill variants
|
|
197
|
-
const skillSrcEn = path.join(tempDir, 'skills', 'mindos');
|
|
198
|
-
fs.mkdirSync(skillSrcEn, { recursive: true });
|
|
199
|
-
fs.writeFileSync(path.join(skillSrcEn, 'SKILL.md'), 'English version');
|
|
200
|
-
|
|
201
|
-
const skillSrcZh = path.join(tempDir, 'skills', 'mindos-zh');
|
|
202
|
-
fs.mkdirSync(skillSrcZh, { recursive: true });
|
|
203
|
-
fs.writeFileSync(path.join(skillSrcZh, 'SKILL.md'), '中文版本');
|
|
204
|
-
|
|
205
|
-
// Test all unsupported agents
|
|
206
|
-
const agents = ['workbuddy', 'qclaw', 'lingma', 'copaw'];
|
|
207
|
-
for (const agent of agents) {
|
|
208
|
-
const agentDir = path.join(tempDir, `.${agent}`);
|
|
209
|
-
fs.mkdirSync(agentDir, { recursive: true });
|
|
210
|
-
|
|
211
|
-
// English variant
|
|
212
|
-
const targetEn = path.join(agentDir, 'skills', 'mindos');
|
|
213
|
-
copyDirSync(skillSrcEn, targetEn);
|
|
214
|
-
expect(fs.readFileSync(path.join(targetEn, 'SKILL.md'), 'utf-8')).toBe('English version');
|
|
215
|
-
|
|
216
|
-
// Chinese variant (reset for next agent)
|
|
217
|
-
fs.rmSync(agentDir, { recursive: true });
|
|
218
|
-
fs.mkdirSync(agentDir, { recursive: true });
|
|
219
|
-
const targetZh = path.join(agentDir, 'skills', 'mindos-zh');
|
|
220
|
-
copyDirSync(skillSrcZh, targetZh);
|
|
221
|
-
expect(fs.readFileSync(path.join(targetZh, 'SKILL.md'), 'utf-8')).toBe('中文版本');
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// ── cpSync cross-platform equivalence ────────────────────────────────────
|
|
227
|
-
|
|
228
|
-
describe('Node.js cpSync vs copyDirSync equivalence', () => {
|
|
229
|
-
it('produces identical output to cpSync recursive', () => {
|
|
230
|
-
const src = path.join(tempDir, 'src');
|
|
231
|
-
fs.mkdirSync(path.join(src, 'sub'), { recursive: true });
|
|
232
|
-
fs.writeFileSync(path.join(src, 'a.txt'), 'aaa');
|
|
233
|
-
fs.writeFileSync(path.join(src, 'sub', 'b.txt'), 'bbb');
|
|
234
|
-
|
|
235
|
-
const dst1 = path.join(tempDir, 'dst-cpsync');
|
|
236
|
-
const dst2 = path.join(tempDir, 'dst-manual');
|
|
237
|
-
|
|
238
|
-
// cpSync (used in setup.js)
|
|
239
|
-
fs.cpSync(src, dst1, { recursive: true });
|
|
240
|
-
// copyDirSync (used in mcp-install.js)
|
|
241
|
-
copyDirSync(src, dst2);
|
|
242
|
-
|
|
243
|
-
// Both should produce identical file trees
|
|
244
|
-
const walk = (dir: string): Record<string, string> => {
|
|
245
|
-
const result: Record<string, string> = {};
|
|
246
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
247
|
-
const rel = entry.name;
|
|
248
|
-
const abs = path.join(dir, rel);
|
|
249
|
-
if (entry.isDirectory()) {
|
|
250
|
-
Object.entries(walk(abs)).forEach(([k, v]) => { result[`${rel}/${k}`] = v; });
|
|
251
|
-
} else {
|
|
252
|
-
result[rel] = fs.readFileSync(abs, 'utf-8');
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return result;
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
expect(walk(dst1)).toEqual(walk(dst2));
|
|
259
|
-
});
|
|
260
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { execFileSync } from 'child_process';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* CLI smoke tests — verify core commands don't crash on invocation.
|
|
9
|
-
* Runs `node bin/cli.js` in a subprocess with HOME set to an empty temp dir
|
|
10
|
-
* so no real config interferes.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const ROOT = path.resolve(__dirname, '..', '..');
|
|
14
|
-
const CLI = path.join(ROOT, 'bin', 'cli.js');
|
|
15
|
-
|
|
16
|
-
let tempHome: string;
|
|
17
|
-
let savedHome: string | undefined;
|
|
18
|
-
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'mindos-smoke-'));
|
|
21
|
-
savedHome = process.env.HOME;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
process.env.HOME = savedHome;
|
|
26
|
-
fs.rmSync(tempHome, { recursive: true, force: true });
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
function run(args: string[]): { stdout: string; stderr: string; exitCode: number } {
|
|
30
|
-
try {
|
|
31
|
-
const stdout = execFileSync(process.execPath, [CLI, ...args], {
|
|
32
|
-
encoding: 'utf-8',
|
|
33
|
-
env: { ...process.env, HOME: tempHome, NODE_ENV: 'test' },
|
|
34
|
-
timeout: 15000,
|
|
35
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
36
|
-
});
|
|
37
|
-
return { stdout, stderr: '', exitCode: 0 };
|
|
38
|
-
} catch (err: unknown) {
|
|
39
|
-
const e = err as { stdout?: string; stderr?: string; status?: number };
|
|
40
|
-
return {
|
|
41
|
-
stdout: e.stdout || '',
|
|
42
|
-
stderr: e.stderr || '',
|
|
43
|
-
exitCode: e.status ?? 1,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
describe('CLI smoke tests', () => {
|
|
49
|
-
it('mindos --version exits 0 and outputs version', () => {
|
|
50
|
-
const { stdout, exitCode } = run(['--version']);
|
|
51
|
-
expect(exitCode).toBe(0);
|
|
52
|
-
expect(stdout).toMatch(/mindos\/\d+\.\d+\.\d+/);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('mindos --help exits 0 and outputs help text', () => {
|
|
56
|
-
const { stdout, exitCode } = run(['--help']);
|
|
57
|
-
expect(exitCode).toBe(0);
|
|
58
|
-
expect(stdout).toContain('MindOS CLI');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('mindos doctor without config exits 1 and suggests onboard', () => {
|
|
62
|
-
const { stdout, stderr, exitCode } = run(['doctor']);
|
|
63
|
-
expect(exitCode).toBe(1);
|
|
64
|
-
const output = stdout + stderr;
|
|
65
|
-
expect(output).toMatch(/onboard/i);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('mindos config show without config exits 1', () => {
|
|
69
|
-
const { exitCode } = run(['config', 'show']);
|
|
70
|
-
expect(exitCode).toBe(1);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('mindos config validate without config exits 1', () => {
|
|
74
|
-
const { exitCode } = run(['config', 'validate']);
|
|
75
|
-
expect(exitCode).toBe(1);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('mindos sync without config exits 0 and shows not configured', () => {
|
|
79
|
-
const { stdout, exitCode } = run(['sync']);
|
|
80
|
-
expect(exitCode).toBe(0);
|
|
81
|
-
expect(stdout).toMatch(/not configured/i);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('mindos nonexistent exits 1', () => {
|
|
85
|
-
const { exitCode } = run(['nonexistent']);
|
|
86
|
-
expect(exitCode).toBe(1);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { execFileSync } from 'child_process';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Tests for `mindos uninstall` command.
|
|
9
|
-
*
|
|
10
|
-
* Key invariants:
|
|
11
|
-
* 1. Default answer is N — empty/piped stdin must abort without deleting anything
|
|
12
|
-
* 2. Config is read BEFORE ~/.mindos/ is deleted (ordering bug regression)
|
|
13
|
-
* 3. Knowledge base deletion requires triple protection (confirm → YES → password)
|
|
14
|
-
* 4. Help text lists the uninstall command
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const ROOT = path.resolve(__dirname, '..', '..');
|
|
18
|
-
const CLI = path.join(ROOT, 'bin', 'cli.js');
|
|
19
|
-
|
|
20
|
-
let tempHome: string;
|
|
21
|
-
let savedHome: string | undefined;
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'mindos-uninstall-'));
|
|
25
|
-
savedHome = process.env.HOME;
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
afterEach(() => {
|
|
29
|
-
process.env.HOME = savedHome;
|
|
30
|
-
fs.rmSync(tempHome, { recursive: true, force: true });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
function run(
|
|
34
|
-
args: string[],
|
|
35
|
-
opts: { input?: string; home?: string } = {},
|
|
36
|
-
): { stdout: string; stderr: string; exitCode: number } {
|
|
37
|
-
const home = opts.home ?? tempHome;
|
|
38
|
-
try {
|
|
39
|
-
const stdout = execFileSync(process.execPath, [CLI, ...args], {
|
|
40
|
-
encoding: 'utf-8',
|
|
41
|
-
env: { ...process.env, HOME: home, NODE_ENV: 'test' },
|
|
42
|
-
input: opts.input ?? '',
|
|
43
|
-
timeout: 15000,
|
|
44
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
45
|
-
});
|
|
46
|
-
return { stdout, stderr: '', exitCode: 0 };
|
|
47
|
-
} catch (err: unknown) {
|
|
48
|
-
const e = err as { stdout?: string; stderr?: string; status?: number };
|
|
49
|
-
return {
|
|
50
|
-
stdout: e.stdout || '',
|
|
51
|
-
stderr: e.stderr || '',
|
|
52
|
-
exitCode: e.status ?? 1,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Write a minimal config.json in the temp home's .mindos/ */
|
|
58
|
-
function writeConfig(
|
|
59
|
-
config: Record<string, unknown>,
|
|
60
|
-
home: string = tempHome,
|
|
61
|
-
): void {
|
|
62
|
-
const dir = path.join(home, '.mindos');
|
|
63
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
64
|
-
fs.writeFileSync(path.join(dir, 'config.json'), JSON.stringify(config));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
describe('mindos uninstall — smoke', () => {
|
|
68
|
-
it('aborts on empty enter (default N) and exits 0', () => {
|
|
69
|
-
const { stdout, exitCode } = run(['uninstall'], { input: '\n' });
|
|
70
|
-
expect(exitCode).toBe(0);
|
|
71
|
-
expect(stdout).toContain('Aborted');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('aborts when user types n', () => {
|
|
75
|
-
const { stdout, exitCode } = run(['uninstall'], { input: 'n\n' });
|
|
76
|
-
expect(exitCode).toBe(0);
|
|
77
|
-
expect(stdout).toContain('Aborted');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('shows operation list before asking for confirmation', () => {
|
|
81
|
-
const { stdout } = run(['uninstall'], { input: '\n' });
|
|
82
|
-
expect(stdout).toContain('Stop running MindOS processes');
|
|
83
|
-
expect(stdout).toContain('Remove background service');
|
|
84
|
-
expect(stdout).toContain('Uninstall npm package');
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('mindos uninstall — help text', () => {
|
|
89
|
-
it('--all lists the uninstall command', () => {
|
|
90
|
-
const { stdout, exitCode } = run(['--all']);
|
|
91
|
-
expect(exitCode).toBe(0);
|
|
92
|
-
expect(stdout).toContain('uninstall');
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe('mindos uninstall — does not delete on N answers', () => {
|
|
97
|
-
it('keeps ~/.mindos/ when user says Y to proceed but N to remove config', () => {
|
|
98
|
-
const mindRoot = path.join(tempHome, 'MindOS');
|
|
99
|
-
fs.mkdirSync(mindRoot, { recursive: true });
|
|
100
|
-
fs.writeFileSync(path.join(mindRoot, 'test.md'), 'hello');
|
|
101
|
-
writeConfig({ mindRoot });
|
|
102
|
-
|
|
103
|
-
// Y to proceed, N to remove config, N to remove knowledge base
|
|
104
|
-
const { stdout } = run(['uninstall'], { input: 'y\nn\nn\n' });
|
|
105
|
-
expect(stdout).toContain('Kept');
|
|
106
|
-
expect(fs.existsSync(path.join(tempHome, '.mindos'))).toBe(true);
|
|
107
|
-
expect(fs.existsSync(mindRoot)).toBe(true);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('keeps knowledge base when user says Y to proceed, Y to config, N to kb', () => {
|
|
111
|
-
const mindRoot = path.join(tempHome, 'MindOS');
|
|
112
|
-
fs.mkdirSync(mindRoot, { recursive: true });
|
|
113
|
-
fs.writeFileSync(path.join(mindRoot, 'test.md'), 'hello');
|
|
114
|
-
writeConfig({ mindRoot });
|
|
115
|
-
|
|
116
|
-
// Y proceed, Y remove config, N remove kb
|
|
117
|
-
const { stdout } = run(['uninstall'], { input: 'y\ny\nn\n' });
|
|
118
|
-
expect(fs.existsSync(path.join(tempHome, '.mindos'))).toBe(false);
|
|
119
|
-
expect(fs.existsSync(mindRoot)).toBe(true);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe('mindos uninstall — knowledge base triple protection', () => {
|
|
124
|
-
it('keeps knowledge base when user types wrong confirmation (not YES)', () => {
|
|
125
|
-
const mindRoot = path.join(tempHome, 'MindOS');
|
|
126
|
-
fs.mkdirSync(mindRoot, { recursive: true });
|
|
127
|
-
writeConfig({ mindRoot });
|
|
128
|
-
|
|
129
|
-
// Y proceed, N config, Y kb, type "yes" (wrong — must be uppercase YES)
|
|
130
|
-
const { stdout } = run(['uninstall'], { input: 'y\nn\ny\nyes\n' });
|
|
131
|
-
expect(stdout).toContain('Knowledge base kept');
|
|
132
|
-
expect(fs.existsSync(mindRoot)).toBe(true);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('keeps knowledge base when password is wrong', () => {
|
|
136
|
-
const mindRoot = path.join(tempHome, 'MindOS');
|
|
137
|
-
fs.mkdirSync(mindRoot, { recursive: true });
|
|
138
|
-
writeConfig({ mindRoot, webPassword: 'secret123' });
|
|
139
|
-
|
|
140
|
-
// Y proceed, N config, Y kb, YES confirm, wrong password
|
|
141
|
-
const { stdout } = run(['uninstall'], { input: 'y\nn\ny\nYES\nwrongpw\n' });
|
|
142
|
-
expect(stdout).toContain('Wrong password');
|
|
143
|
-
expect(fs.existsSync(mindRoot)).toBe(true);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('deletes knowledge base when all protections pass (no password)', () => {
|
|
147
|
-
const mindRoot = path.join(tempHome, 'MindOS');
|
|
148
|
-
fs.mkdirSync(mindRoot, { recursive: true });
|
|
149
|
-
fs.writeFileSync(path.join(mindRoot, 'note.md'), 'data');
|
|
150
|
-
writeConfig({ mindRoot });
|
|
151
|
-
|
|
152
|
-
// Y proceed, N config, Y kb, YES confirm
|
|
153
|
-
const { stdout } = run(['uninstall'], { input: 'y\nn\ny\nYES\n' });
|
|
154
|
-
expect(stdout).toContain('Removed');
|
|
155
|
-
expect(fs.existsSync(mindRoot)).toBe(false);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('deletes knowledge base when all protections pass (correct password)', () => {
|
|
159
|
-
const mindRoot = path.join(tempHome, 'MindOS');
|
|
160
|
-
fs.mkdirSync(mindRoot, { recursive: true });
|
|
161
|
-
writeConfig({ mindRoot, webPassword: 'mypass' });
|
|
162
|
-
|
|
163
|
-
// Y proceed, N config, Y kb, YES confirm, correct password
|
|
164
|
-
const { stdout } = run(['uninstall'], { input: 'y\nn\ny\nYES\nmypass\n' });
|
|
165
|
-
expect(fs.existsSync(mindRoot)).toBe(false);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe('mindos uninstall — config read ordering (regression)', () => {
|
|
170
|
-
/**
|
|
171
|
-
* Regression: config must be read BEFORE ~/.mindos/ is deleted.
|
|
172
|
-
* Otherwise mindRoot and webPassword are lost, and the knowledge base
|
|
173
|
-
* question is silently skipped.
|
|
174
|
-
*
|
|
175
|
-
* Test: user says Y to proceed, Y to delete config, then expects the
|
|
176
|
-
* knowledge base question to still appear (config was read beforehand).
|
|
177
|
-
*/
|
|
178
|
-
it('still asks about knowledge base even after deleting ~/.mindos/', () => {
|
|
179
|
-
const mindRoot = path.join(tempHome, 'MindOS');
|
|
180
|
-
fs.mkdirSync(mindRoot, { recursive: true });
|
|
181
|
-
fs.writeFileSync(path.join(mindRoot, 'note.md'), 'data');
|
|
182
|
-
writeConfig({ mindRoot });
|
|
183
|
-
|
|
184
|
-
// Y proceed, Y remove config (deletes ~/.mindos/), Y remove kb, YES confirm
|
|
185
|
-
const { stdout } = run(['uninstall'], { input: 'y\ny\ny\nYES\n' });
|
|
186
|
-
// Config dir should be gone
|
|
187
|
-
expect(fs.existsSync(path.join(tempHome, '.mindos'))).toBe(false);
|
|
188
|
-
// Knowledge base should also be gone — proves config was read before deletion
|
|
189
|
-
expect(fs.existsSync(mindRoot)).toBe(false);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('password check works even after config dir is deleted', () => {
|
|
193
|
-
const mindRoot = path.join(tempHome, 'MindOS');
|
|
194
|
-
fs.mkdirSync(mindRoot, { recursive: true });
|
|
195
|
-
writeConfig({ mindRoot, webPassword: 'pw123' });
|
|
196
|
-
|
|
197
|
-
// Y proceed, Y remove config, Y remove kb, YES, wrong password
|
|
198
|
-
const { stdout } = run(['uninstall'], { input: 'y\ny\ny\nYES\nwrong\n' });
|
|
199
|
-
expect(fs.existsSync(path.join(tempHome, '.mindos'))).toBe(false);
|
|
200
|
-
expect(stdout).toContain('Wrong password');
|
|
201
|
-
// Knowledge base must survive — password was read from config before deletion
|
|
202
|
-
expect(fs.existsSync(mindRoot)).toBe(true);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe('mindos uninstall — tilde expansion in mindRoot', () => {
|
|
207
|
-
it('expands ~ in mindRoot to HOME', () => {
|
|
208
|
-
const mindRoot = path.join(tempHome, 'MyNotes');
|
|
209
|
-
fs.mkdirSync(mindRoot, { recursive: true });
|
|
210
|
-
fs.writeFileSync(path.join(mindRoot, 'test.md'), 'data');
|
|
211
|
-
// Store with ~ prefix — the code should expand it
|
|
212
|
-
writeConfig({ mindRoot: '~/MyNotes' });
|
|
213
|
-
|
|
214
|
-
// Y proceed, N config, Y kb, YES
|
|
215
|
-
const { stdout } = run(['uninstall'], { input: 'y\nn\ny\nYES\n' });
|
|
216
|
-
expect(fs.existsSync(mindRoot)).toBe(false);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { execFileSync } from 'node:child_process';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
|
|
7
|
-
const ROOT = path.resolve(__dirname, '..', '..');
|
|
8
|
-
const CLI = path.join(ROOT, 'bin', 'cli.js');
|
|
9
|
-
const CURRENT_VERSION = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf-8')).version;
|
|
10
|
-
|
|
11
|
-
let tempDir: string;
|
|
12
|
-
let fakeBinDir: string;
|
|
13
|
-
let fakeInstallRoot: string;
|
|
14
|
-
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mindos-update-root-'));
|
|
17
|
-
fakeBinDir = path.join(tempDir, 'fake-bin');
|
|
18
|
-
fakeInstallRoot = path.join(tempDir, 'new-root');
|
|
19
|
-
|
|
20
|
-
fs.mkdirSync(fakeBinDir, { recursive: true });
|
|
21
|
-
fs.mkdirSync(path.join(fakeInstallRoot, 'bin'), { recursive: true });
|
|
22
|
-
fs.mkdirSync(path.join(fakeInstallRoot, 'app', '.next'), { recursive: true });
|
|
23
|
-
|
|
24
|
-
fs.writeFileSync(path.join(fakeBinDir, 'npm'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
25
|
-
fs.writeFileSync(path.join(fakeInstallRoot, 'bin', 'cli.js'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
26
|
-
fs.symlinkSync(path.join(fakeInstallRoot, 'bin', 'cli.js'), path.join(fakeBinDir, 'mindos'));
|
|
27
|
-
fs.writeFileSync(
|
|
28
|
-
path.join(fakeInstallRoot, 'package.json'),
|
|
29
|
-
JSON.stringify({ name: '@geminilight/mindos', version: '9.9.9' }),
|
|
30
|
-
);
|
|
31
|
-
fs.writeFileSync(
|
|
32
|
-
path.join(fakeInstallRoot, 'app', '.next', '.mindos-build-version'),
|
|
33
|
-
'9.9.9',
|
|
34
|
-
);
|
|
35
|
-
fs.writeFileSync(
|
|
36
|
-
path.join(fakeInstallRoot, 'app', 'package.json'),
|
|
37
|
-
JSON.stringify({ name: 'wiki-app', version: '0.1.0' }),
|
|
38
|
-
);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
afterEach(() => {
|
|
42
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe('mindos update root resolution', () => {
|
|
46
|
-
it('uses the resolved installed CLI path instead of falling back to the current repo root', () => {
|
|
47
|
-
const stdout = execFileSync(process.execPath, [CLI, 'update'], {
|
|
48
|
-
cwd: ROOT,
|
|
49
|
-
encoding: 'utf-8',
|
|
50
|
-
env: {
|
|
51
|
-
...process.env,
|
|
52
|
-
PATH: `${fakeBinDir}:${process.env.PATH}`,
|
|
53
|
-
},
|
|
54
|
-
timeout: 30_000,
|
|
55
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
expect(stdout).toContain(`Updated: ${CURRENT_VERSION} → 9.9.9`);
|
|
59
|
-
expect(stdout).not.toContain('Already on the latest version');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('skips the shell shim under ~/.mindos/bin/ and falls back to current ROOT', () => {
|
|
63
|
-
const fakeHome = path.join(tempDir, 'home');
|
|
64
|
-
const shimDir = path.join(fakeHome, '.mindos', 'bin');
|
|
65
|
-
fs.mkdirSync(shimDir, { recursive: true });
|
|
66
|
-
fs.writeFileSync(
|
|
67
|
-
path.join(shimDir, 'mindos'),
|
|
68
|
-
'#!/bin/sh\nexec node "$(dirname "$0")/../cli.js" "$@"\n',
|
|
69
|
-
{ mode: 0o755 },
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const stdout = execFileSync(process.execPath, [CLI, 'update'], {
|
|
73
|
-
cwd: ROOT,
|
|
74
|
-
encoding: 'utf-8',
|
|
75
|
-
env: {
|
|
76
|
-
...process.env,
|
|
77
|
-
HOME: fakeHome,
|
|
78
|
-
PATH: `${shimDir}:${fakeBinDir}:${process.env.PATH}`,
|
|
79
|
-
},
|
|
80
|
-
timeout: 30_000,
|
|
81
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// `which mindos` finds shimDir/mindos first, but getUpdatedRoot() skips it
|
|
85
|
-
// (dirname matches $HOME/.mindos/bin). Falls back to ROOT where
|
|
86
|
-
// package.json has CURRENT_VERSION → reports "already on the latest".
|
|
87
|
-
expect(stdout).toContain('Already on the latest version');
|
|
88
|
-
});
|
|
89
|
-
});
|