@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,416 +0,0 @@
|
|
|
1
|
-
/* ── Popup Controller — Orchestrates Setup / Clip / Save flows ── */
|
|
2
|
-
|
|
3
|
-
import TurndownService from 'turndown';
|
|
4
|
-
import { loadConfig, saveConfig, isConfigured } from '../lib/storage';
|
|
5
|
-
import { testConnection, listDirs, saveToInbox, createFile } from '../lib/api';
|
|
6
|
-
import { toClipDocument } from '../lib/markdown';
|
|
7
|
-
import type { ClipperConfig, PageContent } from '../lib/types';
|
|
8
|
-
|
|
9
|
-
const INBOX_VALUE = '__inbox__';
|
|
10
|
-
|
|
11
|
-
/* ── DOM refs ── */
|
|
12
|
-
|
|
13
|
-
const $ = <T extends HTMLElement>(id: string) => document.getElementById(id) as T;
|
|
14
|
-
|
|
15
|
-
const viewSetup = $<HTMLDivElement>('view-setup');
|
|
16
|
-
const viewClip = $<HTMLDivElement>('view-clip');
|
|
17
|
-
const viewSuccess = $<HTMLDivElement>('view-success');
|
|
18
|
-
const viewLoading = $<HTMLDivElement>('view-loading');
|
|
19
|
-
|
|
20
|
-
// Setup
|
|
21
|
-
const setupUrl = $<HTMLInputElement>('setup-url');
|
|
22
|
-
const setupToken = $<HTMLInputElement>('setup-token');
|
|
23
|
-
const setupError = $<HTMLDivElement>('setup-error');
|
|
24
|
-
const btnConnect = $<HTMLButtonElement>('btn-connect');
|
|
25
|
-
|
|
26
|
-
// Clip
|
|
27
|
-
const clipTitle = $<HTMLInputElement>('clip-title');
|
|
28
|
-
const clipSiteBadge = $<HTMLSpanElement>('clip-site');
|
|
29
|
-
const clipSiteText = $<HTMLSpanElement>('clip-site-text');
|
|
30
|
-
const clipWordsBadge = $<HTMLSpanElement>('clip-words');
|
|
31
|
-
const clipWordsText = $<HTMLSpanElement>('clip-words-text');
|
|
32
|
-
const dirTrigger = $<HTMLButtonElement>('dir-trigger');
|
|
33
|
-
const dirLabel = $<HTMLSpanElement>('dir-label');
|
|
34
|
-
const dirPanel = $<HTMLDivElement>('dir-panel');
|
|
35
|
-
const dirBreadcrumb = $<HTMLDivElement>('dir-breadcrumb');
|
|
36
|
-
const dirList = $<HTMLDivElement>('dir-list');
|
|
37
|
-
const dirConfirm = $<HTMLButtonElement>('dir-confirm');
|
|
38
|
-
const clipError = $<HTMLDivElement>('clip-error');
|
|
39
|
-
const btnSave = $<HTMLButtonElement>('btn-save');
|
|
40
|
-
const btnSettings = $<HTMLButtonElement>('btn-settings');
|
|
41
|
-
|
|
42
|
-
// Success
|
|
43
|
-
const successDetail = $<HTMLParagraphElement>('success-detail');
|
|
44
|
-
const btnDone = $<HTMLButtonElement>('btn-done');
|
|
45
|
-
const btnClipAnother = $<HTMLButtonElement>('btn-clip-another');
|
|
46
|
-
|
|
47
|
-
/* ── State ── */
|
|
48
|
-
|
|
49
|
-
let config: ClipperConfig;
|
|
50
|
-
let extractedContent: PageContent | null = null;
|
|
51
|
-
let allDirs: string[] = [];
|
|
52
|
-
let selectedPath = INBOX_VALUE; // '__inbox__' or a dir path
|
|
53
|
-
let browsingPath = ''; // current level being viewed in picker
|
|
54
|
-
|
|
55
|
-
/* ── View switching ── */
|
|
56
|
-
|
|
57
|
-
function showView(view: HTMLElement) {
|
|
58
|
-
[viewSetup, viewClip, viewSuccess, viewLoading].forEach(v => v.hidden = true);
|
|
59
|
-
view.hidden = false;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/* ── Button loading state ── */
|
|
63
|
-
|
|
64
|
-
function setButtonLoading(btn: HTMLButtonElement, loading: boolean) {
|
|
65
|
-
const text = btn.querySelector('.btn-text') as HTMLElement;
|
|
66
|
-
const spinner = btn.querySelector('.btn-loading') as HTMLElement;
|
|
67
|
-
if (text) text.hidden = loading;
|
|
68
|
-
if (spinner) spinner.hidden = !loading;
|
|
69
|
-
btn.disabled = loading;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/* ── Turndown instance ── */
|
|
73
|
-
|
|
74
|
-
const turndown = new TurndownService({
|
|
75
|
-
headingStyle: 'atx',
|
|
76
|
-
codeBlockStyle: 'fenced',
|
|
77
|
-
bulletListMarker: '-',
|
|
78
|
-
emDelimiter: '*',
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// Preserve code blocks
|
|
82
|
-
turndown.addRule('pre-code', {
|
|
83
|
-
filter: (node) => node.nodeName === 'PRE' && !!node.querySelector('code'),
|
|
84
|
-
replacement: (_content, node) => {
|
|
85
|
-
const code = (node as Element).querySelector('code');
|
|
86
|
-
const lang = code?.className?.match(/language-(\w+)/)?.[1] || '';
|
|
87
|
-
const text = code?.textContent || '';
|
|
88
|
-
return `\n\`\`\`${lang}\n${text}\n\`\`\`\n`;
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
/* ── Extract content from active tab ── */
|
|
93
|
-
|
|
94
|
-
async function extractContent(): Promise<PageContent> {
|
|
95
|
-
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
96
|
-
if (!tab?.id) throw new Error('No active tab');
|
|
97
|
-
|
|
98
|
-
// Content scripts can't run on chrome://, edge://, about:, or extension pages
|
|
99
|
-
const url = tab.url ?? '';
|
|
100
|
-
if (url.startsWith('chrome') || url.startsWith('edge') || url.startsWith('about:') || url.startsWith('moz-extension')) {
|
|
101
|
-
throw new Error('Cannot clip browser internal pages');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Inject content script on demand (not always-on — saves memory on every page)
|
|
105
|
-
// Step 1: inject Readability + extractor (IIFE, sets window.__mindosClipResult)
|
|
106
|
-
try {
|
|
107
|
-
await chrome.scripting.executeScript({
|
|
108
|
-
target: { tabId: tab.id },
|
|
109
|
-
files: ['content/extractor.js'],
|
|
110
|
-
});
|
|
111
|
-
} catch {
|
|
112
|
-
throw new Error('Cannot read this page — try refreshing first');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Step 2: read the result back (executeScript with func can return values)
|
|
116
|
-
let results: chrome.scripting.InjectionResult[];
|
|
117
|
-
try {
|
|
118
|
-
results = await chrome.scripting.executeScript({
|
|
119
|
-
target: { tabId: tab.id },
|
|
120
|
-
func: () => (window as any).__mindosClipResult,
|
|
121
|
-
});
|
|
122
|
-
} catch {
|
|
123
|
-
throw new Error('Cannot read extraction result');
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const result = results?.[0]?.result;
|
|
127
|
-
if (!result || typeof result !== 'object') {
|
|
128
|
-
throw new Error('Content extraction returned empty result');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return result as PageContent;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/* ── Init ── */
|
|
135
|
-
|
|
136
|
-
async function init() {
|
|
137
|
-
config = await loadConfig();
|
|
138
|
-
|
|
139
|
-
if (!isConfigured(config)) {
|
|
140
|
-
showView(viewSetup);
|
|
141
|
-
setupUrl.value = config.mindosUrl;
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Configured — extract content
|
|
146
|
-
showView(viewLoading);
|
|
147
|
-
|
|
148
|
-
let extractionError = '';
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
[extractedContent, allDirs] = await Promise.all([
|
|
152
|
-
extractContent(),
|
|
153
|
-
listDirs(config),
|
|
154
|
-
]);
|
|
155
|
-
} catch (err) {
|
|
156
|
-
extractionError = err instanceof Error ? err.message : 'Cannot read this page';
|
|
157
|
-
extractedContent = null;
|
|
158
|
-
allDirs = await listDirs(config).catch(() => []);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
showClipView(extractionError);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function showClipView(errorMsg?: string) {
|
|
165
|
-
showView(viewClip);
|
|
166
|
-
|
|
167
|
-
if (errorMsg) {
|
|
168
|
-
showError(clipError, errorMsg);
|
|
169
|
-
btnSave.disabled = true;
|
|
170
|
-
} else {
|
|
171
|
-
hideError(clipError);
|
|
172
|
-
btnSave.disabled = false;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (extractedContent) {
|
|
176
|
-
clipTitle.value = extractedContent.title;
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
const host = new URL(extractedContent.url).hostname.replace(/^www\./, '');
|
|
180
|
-
clipSiteText.textContent = host;
|
|
181
|
-
clipSiteBadge.style.display = '';
|
|
182
|
-
} catch {
|
|
183
|
-
clipSiteBadge.style.display = 'none';
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
clipWordsText.textContent = `${extractedContent.wordCount.toLocaleString()} words`;
|
|
187
|
-
clipWordsBadge.style.display = '';
|
|
188
|
-
} else {
|
|
189
|
-
clipTitle.value = '';
|
|
190
|
-
clipSiteBadge.style.display = 'none';
|
|
191
|
-
clipWordsBadge.style.display = 'none';
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Reset dir picker state
|
|
195
|
-
selectedPath = INBOX_VALUE;
|
|
196
|
-
browsingPath = '';
|
|
197
|
-
updateDirLabel();
|
|
198
|
-
toggleDirPanel(false);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/** Render the hierarchical directory picker at the current browsing level */
|
|
202
|
-
function renderDirPicker() {
|
|
203
|
-
// Breadcrumb
|
|
204
|
-
const segments = browsingPath ? browsingPath.split('/') : [];
|
|
205
|
-
dirBreadcrumb.innerHTML = '';
|
|
206
|
-
|
|
207
|
-
// Root / Inbox button
|
|
208
|
-
const rootBtn = document.createElement('button');
|
|
209
|
-
rootBtn.type = 'button';
|
|
210
|
-
rootBtn.textContent = '/ Inbox';
|
|
211
|
-
rootBtn.className = selectedPath === INBOX_VALUE && !browsingPath ? 'active' : '';
|
|
212
|
-
rootBtn.addEventListener('click', () => {
|
|
213
|
-
browsingPath = '';
|
|
214
|
-
selectedPath = INBOX_VALUE;
|
|
215
|
-
updateDirLabel();
|
|
216
|
-
renderDirPicker();
|
|
217
|
-
});
|
|
218
|
-
dirBreadcrumb.appendChild(rootBtn);
|
|
219
|
-
|
|
220
|
-
segments.forEach((seg, i) => {
|
|
221
|
-
const sep = document.createElement('span');
|
|
222
|
-
sep.className = 'crumb-sep';
|
|
223
|
-
sep.innerHTML = '›';
|
|
224
|
-
dirBreadcrumb.appendChild(sep);
|
|
225
|
-
|
|
226
|
-
const btn = document.createElement('button');
|
|
227
|
-
btn.type = 'button';
|
|
228
|
-
btn.textContent = seg;
|
|
229
|
-
const path = segments.slice(0, i + 1).join('/');
|
|
230
|
-
btn.className = i === segments.length - 1 ? 'active' : '';
|
|
231
|
-
btn.addEventListener('click', () => {
|
|
232
|
-
browsingPath = path;
|
|
233
|
-
selectedPath = path;
|
|
234
|
-
updateDirLabel();
|
|
235
|
-
renderDirPicker();
|
|
236
|
-
});
|
|
237
|
-
dirBreadcrumb.appendChild(btn);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// Child directories at current level
|
|
241
|
-
const prefix = browsingPath ? browsingPath + '/' : '';
|
|
242
|
-
const children = allDirs
|
|
243
|
-
.filter(p => {
|
|
244
|
-
if (!p.startsWith(prefix)) return false;
|
|
245
|
-
const rest = p.slice(prefix.length);
|
|
246
|
-
return rest.length > 0 && !rest.includes('/');
|
|
247
|
-
})
|
|
248
|
-
.sort();
|
|
249
|
-
|
|
250
|
-
dirList.innerHTML = '';
|
|
251
|
-
for (const childPath of children) {
|
|
252
|
-
const childName = childPath.split('/').pop() || childPath;
|
|
253
|
-
const hasChildren = allDirs.some(p => p.startsWith(childPath + '/'));
|
|
254
|
-
|
|
255
|
-
const btn = document.createElement('button');
|
|
256
|
-
btn.type = 'button';
|
|
257
|
-
btn.className = 'dir-item';
|
|
258
|
-
btn.innerHTML = `
|
|
259
|
-
<svg class="dir-item-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
|
260
|
-
<span class="dir-item-name">${childName}</span>
|
|
261
|
-
${hasChildren ? '<svg class="dir-item-arrow" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>' : ''}
|
|
262
|
-
`;
|
|
263
|
-
btn.addEventListener('click', () => {
|
|
264
|
-
browsingPath = childPath;
|
|
265
|
-
selectedPath = childPath;
|
|
266
|
-
updateDirLabel();
|
|
267
|
-
renderDirPicker();
|
|
268
|
-
});
|
|
269
|
-
dirList.appendChild(btn);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function updateDirLabel() {
|
|
274
|
-
if (selectedPath === INBOX_VALUE) {
|
|
275
|
-
dirLabel.textContent = 'Inbox';
|
|
276
|
-
} else {
|
|
277
|
-
dirLabel.textContent = selectedPath.split('/').join(' / ');
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function toggleDirPanel(show?: boolean) {
|
|
282
|
-
const isOpen = show ?? dirPanel.hidden;
|
|
283
|
-
dirPanel.hidden = !isOpen;
|
|
284
|
-
dirTrigger.classList.toggle('active', isOpen);
|
|
285
|
-
dirTrigger.setAttribute('aria-expanded', String(isOpen));
|
|
286
|
-
if (isOpen) renderDirPicker();
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/* ── Event Handlers ── */
|
|
290
|
-
|
|
291
|
-
// Connect button
|
|
292
|
-
btnConnect.addEventListener('click', async () => {
|
|
293
|
-
const url = setupUrl.value.trim().replace(/\/+$/, '');
|
|
294
|
-
const token = setupToken.value.trim();
|
|
295
|
-
|
|
296
|
-
if (!url) { showError(setupError, 'Please enter your MindOS URL'); return; }
|
|
297
|
-
if (!token) { showError(setupError, 'Please paste your auth token'); return; }
|
|
298
|
-
|
|
299
|
-
hideError(setupError);
|
|
300
|
-
setButtonLoading(btnConnect, true);
|
|
301
|
-
|
|
302
|
-
const testConfig: ClipperConfig = {
|
|
303
|
-
mindosUrl: url,
|
|
304
|
-
authToken: token,
|
|
305
|
-
defaultSpace: 'Inbox',
|
|
306
|
-
connected: false,
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
const result = await testConnection(testConfig);
|
|
310
|
-
|
|
311
|
-
if (!result.ok) {
|
|
312
|
-
setButtonLoading(btnConnect, false);
|
|
313
|
-
showError(setupError, result.error || 'Connection failed');
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Save and proceed
|
|
318
|
-
config = await saveConfig({ ...testConfig, connected: true });
|
|
319
|
-
setButtonLoading(btnConnect, false);
|
|
320
|
-
|
|
321
|
-
// Now extract content
|
|
322
|
-
showView(viewLoading);
|
|
323
|
-
|
|
324
|
-
try {
|
|
325
|
-
[extractedContent, allDirs] = await Promise.all([
|
|
326
|
-
extractContent(),
|
|
327
|
-
listDirs(config),
|
|
328
|
-
]);
|
|
329
|
-
} catch (err) {
|
|
330
|
-
extractedContent = null;
|
|
331
|
-
allDirs = [];
|
|
332
|
-
showClipView(err instanceof Error ? err.message : 'Cannot read this page');
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
showClipView();
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// Save button
|
|
340
|
-
btnSave.addEventListener('click', async () => {
|
|
341
|
-
if (!extractedContent) {
|
|
342
|
-
showError(clipError, 'No content extracted from this page');
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
hideError(clipError);
|
|
347
|
-
setButtonLoading(btnSave, true);
|
|
348
|
-
|
|
349
|
-
// Override title if user edited
|
|
350
|
-
const content = { ...extractedContent, title: clipTitle.value.trim() || extractedContent.title };
|
|
351
|
-
const isInbox = selectedPath === INBOX_VALUE;
|
|
352
|
-
|
|
353
|
-
const doc = toClipDocument(content, isInbox ? '' : selectedPath, (html) => turndown.turndown(html));
|
|
354
|
-
|
|
355
|
-
// Route to Inbox API or File API based on user choice
|
|
356
|
-
const result = isInbox
|
|
357
|
-
? await saveToInbox(config, doc.fileName, doc.markdown)
|
|
358
|
-
: await createFile(config, selectedPath, doc.fileName, doc.markdown);
|
|
359
|
-
|
|
360
|
-
setButtonLoading(btnSave, false);
|
|
361
|
-
|
|
362
|
-
if (result.error) {
|
|
363
|
-
showError(clipError, result.error);
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Success!
|
|
368
|
-
const displayPath = isInbox ? `Inbox/${doc.fileName}` : `${selectedPath}/${doc.fileName}`;
|
|
369
|
-
successDetail.textContent = displayPath;
|
|
370
|
-
showView(viewSuccess);
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
// Settings button — go back to setup
|
|
374
|
-
btnSettings.addEventListener('click', () => {
|
|
375
|
-
setupUrl.value = config.mindosUrl;
|
|
376
|
-
setupToken.value = config.authToken;
|
|
377
|
-
showView(viewSetup);
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
// Done button — close popup
|
|
381
|
-
btnDone.addEventListener('click', () => {
|
|
382
|
-
window.close();
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
// Clip Again — go back to clip view for same page
|
|
386
|
-
btnClipAnother.addEventListener('click', () => {
|
|
387
|
-
showClipView();
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
// DirPicker — toggle panel
|
|
391
|
-
dirTrigger.addEventListener('click', () => toggleDirPanel());
|
|
392
|
-
|
|
393
|
-
// DirPicker — confirm selection
|
|
394
|
-
dirConfirm.addEventListener('click', () => toggleDirPanel(false));
|
|
395
|
-
|
|
396
|
-
// DirPicker — Esc to close panel
|
|
397
|
-
document.addEventListener('keydown', (e) => {
|
|
398
|
-
if (e.key === 'Escape' && !dirPanel.hidden) {
|
|
399
|
-
e.preventDefault();
|
|
400
|
-
toggleDirPanel(false);
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
/* ── Error display helpers ── */
|
|
405
|
-
|
|
406
|
-
function showError(el: HTMLElement, msg: string) {
|
|
407
|
-
el.textContent = msg;
|
|
408
|
-
el.hidden = false;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function hideError(el: HTMLElement) {
|
|
412
|
-
el.hidden = true;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/* ── Boot ── */
|
|
416
|
-
init();
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ES2022",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"strict": true,
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"outDir": "dist",
|
|
10
|
-
"rootDir": "src",
|
|
11
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
12
|
-
"types": ["chrome"]
|
|
13
|
-
},
|
|
14
|
-
"include": ["src/**/*.ts"],
|
|
15
|
-
"exclude": ["node_modules", "dist"]
|
|
16
|
-
}
|
package/tests/e2e/README.md
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# E2E Tests
|
|
2
|
-
|
|
3
|
-
Browser-based end-to-end tests using Playwright.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
cd app && npx playwright install
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Running
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
# Start the dev server first
|
|
15
|
-
npm run dev
|
|
16
|
-
|
|
17
|
-
# Run E2E tests
|
|
18
|
-
npx playwright test --config tests/e2e/playwright.config.ts
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Writing tests
|
|
22
|
-
|
|
23
|
-
- Each test file should cover a user-facing workflow (e.g. file navigation, search, settings)
|
|
24
|
-
- Use `test.describe` to group related scenarios
|
|
25
|
-
- Screenshots on failure are saved to `tests/e2e/results/`
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { test, expect, Page } from 'playwright/test';
|
|
2
|
-
|
|
3
|
-
test.describe('Navigation', () => {
|
|
4
|
-
test('homepage loads and shows sidebar', async ({ page }: { page: Page }) => {
|
|
5
|
-
await page.goto('/');
|
|
6
|
-
await expect(page.locator('[data-testid="sidebar"]').or(page.locator('nav'))).toBeVisible();
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test('search modal opens with Cmd+K', async ({ page }: { page: Page }) => {
|
|
10
|
-
await page.goto('/');
|
|
11
|
-
await page.keyboard.press('Meta+k');
|
|
12
|
-
await expect(page.locator('[role="dialog"]').or(page.locator('input[placeholder]'))).toBeVisible();
|
|
13
|
-
});
|
|
14
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'playwright/test';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
testDir: '.',
|
|
5
|
-
testMatch: '**/*.spec.ts',
|
|
6
|
-
timeout: 30_000,
|
|
7
|
-
retries: 0,
|
|
8
|
-
use: {
|
|
9
|
-
baseURL: process.env.MINDOS_URL ?? 'http://localhost:3456',
|
|
10
|
-
screenshot: 'only-on-failure',
|
|
11
|
-
},
|
|
12
|
-
outputDir: './results',
|
|
13
|
-
webServer: undefined, // assume dev server is already running
|
|
14
|
-
});
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# Integration Tests
|
|
2
|
-
|
|
3
|
-
Cross-service tests that verify the MCP server correctly communicates with the App REST API.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
cd mcp && npm install
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Running
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
# Start the app server first
|
|
15
|
-
npm run dev
|
|
16
|
-
|
|
17
|
-
# Run integration tests (requires app to be running)
|
|
18
|
-
npx vitest run --config tests/integration/vitest.config.ts
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Writing tests
|
|
22
|
-
|
|
23
|
-
- Tests should verify the MCP → App API contract (request/response shapes)
|
|
24
|
-
- Each MCP tool should have at least one integration test
|
|
25
|
-
- Use a temporary MIND_ROOT directory to avoid polluting real data
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { tmpdir } from 'os';
|
|
5
|
-
|
|
6
|
-
const BASE_URL = process.env.MINDOS_URL ?? 'http://localhost:3456';
|
|
7
|
-
|
|
8
|
-
// Helper: call an App API endpoint
|
|
9
|
-
async function api(path: string, init?: RequestInit) {
|
|
10
|
-
const res = await fetch(`${BASE_URL}${path}`, init);
|
|
11
|
-
return { status: res.status, json: await res.json().catch(() => null) };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe('MCP ↔ App API contract', () => {
|
|
15
|
-
let tempRoot: string;
|
|
16
|
-
|
|
17
|
-
beforeAll(() => {
|
|
18
|
-
// NOTE: These tests assume the app server is running and using a test MIND_ROOT.
|
|
19
|
-
// In CI, set MIND_ROOT env var to a temp directory before starting the app.
|
|
20
|
-
tempRoot = mkdtempSync(join(tmpdir(), 'mindos-integration-'));
|
|
21
|
-
writeFileSync(join(tempRoot, 'README.md'), '# Test KB');
|
|
22
|
-
mkdirSync(join(tempRoot, 'Notes'), { recursive: true });
|
|
23
|
-
writeFileSync(join(tempRoot, 'Notes', 'hello.md'), '# Hello\nWorld');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
afterAll(() => {
|
|
27
|
-
rmSync(tempRoot, { recursive: true, force: true });
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('GET /api/files returns array', async () => {
|
|
31
|
-
const { status, json } = await api('/api/files');
|
|
32
|
-
expect(status).toBe(200);
|
|
33
|
-
expect(Array.isArray(json)).toBe(true);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('GET /api/search?q=... returns results', async () => {
|
|
37
|
-
const { status, json } = await api('/api/search?q=test');
|
|
38
|
-
expect(status).toBe(200);
|
|
39
|
-
expect(Array.isArray(json)).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('GET /api/bootstrap returns startup context', async () => {
|
|
43
|
-
const { status, json } = await api('/api/bootstrap');
|
|
44
|
-
expect(status).toBe(200);
|
|
45
|
-
expect(typeof json).toBe('object');
|
|
46
|
-
// instruction and index may be undefined if files don't exist in MIND_ROOT
|
|
47
|
-
expect(['string', 'undefined']).toContain(typeof json.instruction);
|
|
48
|
-
expect(['string', 'undefined']).toContain(typeof json.index);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('GET /api/git?op=is_repo returns boolean', async () => {
|
|
52
|
-
const { status, json } = await api('/api/git?op=is_repo');
|
|
53
|
-
expect(status).toBe(200);
|
|
54
|
-
expect(json).toHaveProperty('isRepo');
|
|
55
|
-
expect(typeof json.isRepo).toBe('boolean');
|
|
56
|
-
});
|
|
57
|
-
});
|