@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.
Files changed (227) hide show
  1. package/_standalone/.mindos-build-version +1 -1
  2. package/_standalone/.next/BUILD_ID +1 -1
  3. package/_standalone/.next/app-path-routes-manifest.json +27 -27
  4. package/_standalone/.next/build-manifest.json +3 -3
  5. package/_standalone/.next/cache/.previewinfo +1 -1
  6. package/_standalone/.next/cache/.rscinfo +1 -1
  7. package/_standalone/.next/cache/config.json +3 -3
  8. package/_standalone/.next/prerender-manifest.json +3 -3
  9. package/_standalone/.next/react-loadable-manifest.json +4 -4
  10. package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
  11. package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/_standalone/.next/server/app/_global-error.html +2 -2
  13. package/_standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/_standalone/.next/server/app/_not-found/page.js +1 -1
  21. package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
  24. package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
  25. package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
  26. package/_standalone/.next/server/app/agents/page.js +1 -1
  27. package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
  28. package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  29. package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
  30. package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
  31. package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
  32. package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
  33. package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
  34. package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
  35. package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
  36. package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
  37. package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
  38. package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
  39. package/_standalone/.next/server/app/api/agents/copy-skill/route_client-reference-manifest.js +1 -1
  40. package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
  41. package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
  42. package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
  43. package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
  44. package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
  45. package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
  46. package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
  47. package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
  48. package/_standalone/.next/server/app/api/channels/verify/route_client-reference-manifest.js +1 -1
  49. package/_standalone/.next/server/app/api/connect/route_client-reference-manifest.js +1 -1
  50. package/_standalone/.next/server/app/api/embedding/route_client-reference-manifest.js +1 -1
  51. package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
  52. package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
  53. package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
  54. package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
  55. package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
  56. package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  57. package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  58. package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
  59. package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  60. package/_standalone/.next/server/app/api/im/activity/route_client-reference-manifest.js +1 -1
  61. package/_standalone/.next/server/app/api/im/config/route_client-reference-manifest.js +1 -1
  62. package/_standalone/.next/server/app/api/im/status/route_client-reference-manifest.js +1 -1
  63. package/_standalone/.next/server/app/api/im/test/route_client-reference-manifest.js +1 -1
  64. package/_standalone/.next/server/app/api/im/webhook/feishu/route_client-reference-manifest.js +1 -1
  65. package/_standalone/.next/server/app/api/im/webhook-status/route_client-reference-manifest.js +1 -1
  66. package/_standalone/.next/server/app/api/inbox/clip/route_client-reference-manifest.js +1 -1
  67. package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
  68. package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  69. package/_standalone/.next/server/app/api/lint/route_client-reference-manifest.js +1 -1
  70. package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
  71. package/_standalone/.next/server/app/api/mcp/direct-tools/route_client-reference-manifest.js +1 -1
  72. package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
  73. package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
  74. package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
  75. package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
  76. package/_standalone/.next/server/app/api/mcp/tools/route_client-reference-manifest.js +1 -1
  77. package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
  78. package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
  79. package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
  80. package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  81. package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  82. package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
  83. package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
  84. package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  85. package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
  86. package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
  87. package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
  88. package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
  89. package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
  90. package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
  91. package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  92. package/_standalone/.next/server/app/api/space-overview/route_client-reference-manifest.js +1 -1
  93. package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
  94. package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
  95. package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
  96. package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  97. package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
  98. package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
  99. package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  100. package/_standalone/.next/server/app/changelog/page.js +1 -1
  101. package/_standalone/.next/server/app/changelog/page.js.nft.json +1 -1
  102. package/_standalone/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  103. package/_standalone/.next/server/app/changes/page.js +1 -1
  104. package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
  105. package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
  106. package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
  107. package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
  108. package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
  109. package/_standalone/.next/server/app/echo/page.js +1 -1
  110. package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
  111. package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
  112. package/_standalone/.next/server/app/explore/page.js +1 -1
  113. package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
  114. package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
  115. package/_standalone/.next/server/app/help/page.js +1 -1
  116. package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
  117. package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
  118. package/_standalone/.next/server/app/inbox/history/page.js +1 -1
  119. package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
  120. package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
  121. package/_standalone/.next/server/app/login/page.js +1 -1
  122. package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
  123. package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  124. package/_standalone/.next/server/app/page.js +1 -1
  125. package/_standalone/.next/server/app/page.js.nft.json +1 -1
  126. package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  127. package/_standalone/.next/server/app/setup/page.js +1 -1
  128. package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
  129. package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  130. package/_standalone/.next/server/app/trash/page.js +2 -2
  131. package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
  132. package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
  133. package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
  134. package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
  135. package/_standalone/.next/server/app-paths-manifest.json +27 -27
  136. package/_standalone/.next/server/chunks/{3311.js → 2449.js} +2 -2
  137. package/_standalone/.next/server/chunks/5299.js +1 -1
  138. package/_standalone/.next/server/chunks/6022.js +34 -34
  139. package/_standalone/.next/server/middleware-build-manifest.js +1 -1
  140. package/_standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  141. package/_standalone/.next/server/pages/500.html +2 -2
  142. package/_standalone/.next/server/server-reference-manifest.js +1 -1
  143. package/_standalone/.next/server/server-reference-manifest.json +1 -1
  144. package/_standalone/.next/static/chunks/{7143.879daa87569c5b02.js → 4094.09364c01df411380.js} +1 -1
  145. package/_standalone/.next/static/chunks/{5795.d9099a1afecd6047.js → 5331.c89084fd7f67887d.js} +2 -2
  146. package/_standalone/.next/static/chunks/app/{layout-a344709b8447be75.js → layout-fcbde5bee626d21a.js} +63 -63
  147. package/_standalone/.next/static/chunks/app/trash/page-e623ff0ab35de002.js +1 -0
  148. package/_standalone/.next/static/chunks/app/view/[...path]/page-49c4eff6ffdb5168.js +12 -0
  149. package/_standalone/.next/static/chunks/{webpack-2f2787d3469d3df1.js → webpack-dc486b68118d1328.js} +1 -1
  150. package/_standalone/.next/trace +72 -72
  151. package/_standalone/package-lock.json +2 -2
  152. package/_standalone/package.json +1 -1
  153. package/app/package.json +1 -1
  154. package/package.json +1 -1
  155. package/_standalone/.next/static/chunks/app/trash/page-0907fdd06a4467de.js +0 -1
  156. package/_standalone/.next/static/chunks/app/view/[...path]/page-f53ce199b4a4bbb5.js +0 -12
  157. package/browser-extension/README.md +0 -160
  158. package/browser-extension/build.mjs +0 -63
  159. package/browser-extension/extension/background/service-worker.js +0 -1
  160. package/browser-extension/extension/content/extractor.js +0 -2
  161. package/browser-extension/extension/icons/icon-128.png +0 -0
  162. package/browser-extension/extension/icons/icon-128.svg +0 -4
  163. package/browser-extension/extension/icons/icon-16.png +0 -0
  164. package/browser-extension/extension/icons/icon-16.svg +0 -4
  165. package/browser-extension/extension/icons/icon-32.png +0 -0
  166. package/browser-extension/extension/icons/icon-32.svg +0 -4
  167. package/browser-extension/extension/icons/icon-48.png +0 -0
  168. package/browser-extension/extension/icons/icon-48.svg +0 -4
  169. package/browser-extension/extension/manifest.json +0 -47
  170. package/browser-extension/extension/popup/popup.css +0 -510
  171. package/browser-extension/extension/popup/popup.html +0 -128
  172. package/browser-extension/extension/popup/popup.js +0 -73
  173. package/browser-extension/package-lock.json +0 -617
  174. package/browser-extension/package.json +0 -21
  175. package/browser-extension/scripts/gen-icons.sh +0 -38
  176. package/browser-extension/src/background/service-worker.ts +0 -27
  177. package/browser-extension/src/content/extractor.ts +0 -44
  178. package/browser-extension/src/icons/icon-128.png +0 -0
  179. package/browser-extension/src/icons/icon-128.svg +0 -4
  180. package/browser-extension/src/icons/icon-16.png +0 -0
  181. package/browser-extension/src/icons/icon-16.svg +0 -4
  182. package/browser-extension/src/icons/icon-32.png +0 -0
  183. package/browser-extension/src/icons/icon-32.svg +0 -4
  184. package/browser-extension/src/icons/icon-48.png +0 -0
  185. package/browser-extension/src/icons/icon-48.svg +0 -4
  186. package/browser-extension/src/lib/api.ts +0 -146
  187. package/browser-extension/src/lib/markdown.ts +0 -68
  188. package/browser-extension/src/lib/storage.ts +0 -37
  189. package/browser-extension/src/lib/types.ts +0 -42
  190. package/browser-extension/src/manifest.json +0 -47
  191. package/browser-extension/src/popup/popup.css +0 -510
  192. package/browser-extension/src/popup/popup.html +0 -128
  193. package/browser-extension/src/popup/popup.ts +0 -416
  194. package/browser-extension/tsconfig.json +0 -16
  195. package/tests/e2e/README.md +0 -25
  196. package/tests/e2e/navigation.spec.ts +0 -14
  197. package/tests/e2e/playwright.config.ts +0 -14
  198. package/tests/integration/README.md +0 -25
  199. package/tests/integration/mcp-contract.test.ts +0 -57
  200. package/tests/integration/mcp-transport.test.ts +0 -361
  201. package/tests/integration/package-lock.json +0 -1463
  202. package/tests/integration/package.json +0 -8
  203. package/tests/integration/vitest.config.ts +0 -11
  204. package/tests/security-hardening.test.ts +0 -456
  205. package/tests/unit/build-integrity.test.ts +0 -137
  206. package/tests/unit/cli-build.test.ts +0 -180
  207. package/tests/unit/cli-config.test.ts +0 -257
  208. package/tests/unit/cli-mcp-install-toml.test.ts +0 -586
  209. package/tests/unit/cli-mcp-install.test.ts +0 -123
  210. package/tests/unit/cli-mcp-stdio-default.test.ts +0 -180
  211. package/tests/unit/cli-modules-load.test.ts +0 -64
  212. package/tests/unit/cli-port.test.ts +0 -87
  213. package/tests/unit/cli-skill-auto-copy.test.ts +0 -260
  214. package/tests/unit/cli-smoke.test.ts +0 -88
  215. package/tests/unit/cli-uninstall.test.ts +0 -218
  216. package/tests/unit/cli-update-root.test.ts +0 -89
  217. package/tests/unit/cli-user-flow-sim.test.ts +0 -506
  218. package/tests/unit/cli-wait-hint.test.ts +0 -86
  219. package/tests/unit/custom-agents.test.ts +0 -478
  220. package/tests/unit/dep-safety.test.ts +0 -126
  221. package/tests/unit/detect-system-lang.test.ts +0 -122
  222. package/tests/unit/mcp-build.test.ts +0 -162
  223. package/tests/unit/setup-needs-restart.test.ts +0 -139
  224. package/tests/unit/stop-restart.test.ts +0 -393
  225. package/tests/unit/vitest.config.ts +0 -8
  226. /package/_standalone/.next/static/{w5bqzZbd2_vdoPRB0JQ_I → Dn8EHqUedSzanCfrM8WWS}/_buildManifest.js +0 -0
  227. /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 = '&#8250;';
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
- }
@@ -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
- });