fss-link 1.0.40 → 1.0.45

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 (428) hide show
  1. package/dist/commands/mcp/add.test.ts +122 -0
  2. package/dist/commands/mcp/add.ts +222 -0
  3. package/dist/commands/mcp/list.test.ts +154 -0
  4. package/dist/commands/mcp/list.ts +139 -0
  5. package/dist/commands/mcp/remove.test.ts +69 -0
  6. package/dist/commands/mcp/remove.ts +60 -0
  7. package/dist/commands/mcp.test.ts +55 -0
  8. package/dist/commands/mcp.ts +27 -0
  9. package/dist/config/apiValidation.test.ts +118 -0
  10. package/dist/config/auth.test.ts +79 -0
  11. package/dist/config/auth.ts +100 -0
  12. package/dist/config/config.integration.test.ts +407 -0
  13. package/dist/config/config.test.ts +1952 -0
  14. package/dist/config/config.ts +690 -0
  15. package/dist/config/database.test.ts +96 -0
  16. package/dist/config/database.ts +752 -0
  17. package/dist/config/extension.test.ts +236 -0
  18. package/dist/config/extension.ts +180 -0
  19. package/dist/config/keyBindings.test.ts +62 -0
  20. package/dist/config/keyBindings.ts +184 -0
  21. package/dist/config/modelManager.ts +275 -0
  22. package/dist/config/providerManager.ts +244 -0
  23. package/dist/config/providerPersistence.test.ts +377 -0
  24. package/dist/config/providerPersistence.ts +105 -0
  25. package/dist/config/sandboxConfig.ts +107 -0
  26. package/dist/config/settings.test.ts +1424 -0
  27. package/dist/config/settings.ts +517 -0
  28. package/dist/config/settingsSchema.test.ts +252 -0
  29. package/dist/config/settingsSchema.ts +728 -0
  30. package/dist/config/trustedFolders.test.ts +208 -0
  31. package/dist/config/trustedFolders.ts +167 -0
  32. package/dist/gemini.test.tsx +252 -0
  33. package/dist/gemini.tsx +357 -0
  34. package/dist/generated/git-commit.ts +10 -0
  35. package/dist/index.ts +21 -0
  36. package/dist/nonInteractiveCli.test.ts +276 -0
  37. package/dist/nonInteractiveCli.ts +143 -0
  38. package/dist/package.json +87 -87
  39. package/dist/patches/is-in-ci.ts +17 -0
  40. package/dist/services/BuiltinCommandLoader.test.ts +127 -0
  41. package/dist/services/BuiltinCommandLoader.ts +95 -0
  42. package/dist/services/CommandService.test.ts +352 -0
  43. package/dist/services/CommandService.ts +103 -0
  44. package/dist/services/FileCommandLoader.test.ts +1002 -0
  45. package/dist/services/FileCommandLoader.ts +289 -0
  46. package/dist/services/McpPromptLoader.ts +231 -0
  47. package/dist/services/SearchEngineConfigProvider.ts +100 -0
  48. package/dist/services/prompt-processors/argumentProcessor.test.ts +41 -0
  49. package/dist/services/prompt-processors/argumentProcessor.ts +23 -0
  50. package/dist/services/prompt-processors/shellProcessor.test.ts +709 -0
  51. package/dist/services/prompt-processors/shellProcessor.ts +248 -0
  52. package/dist/services/prompt-processors/types.ts +44 -0
  53. package/dist/services/types.ts +24 -0
  54. package/dist/src/config/apiValidation.test.d.ts +6 -0
  55. package/dist/src/config/apiValidation.test.js +99 -0
  56. package/dist/src/config/apiValidation.test.js.map +1 -0
  57. package/dist/src/config/database.d.ts +32 -0
  58. package/dist/src/config/database.js +281 -2
  59. package/dist/src/config/database.js.map +1 -1
  60. package/dist/src/config/database.test.d.ts +6 -0
  61. package/dist/src/config/database.test.js +80 -0
  62. package/dist/src/config/database.test.js.map +1 -0
  63. package/dist/src/config/providerManager.d.ts +74 -0
  64. package/dist/src/config/providerManager.js +203 -0
  65. package/dist/src/config/providerManager.js.map +1 -0
  66. package/dist/src/config/providerPersistence.d.ts +75 -0
  67. package/dist/src/config/providerPersistence.js +55 -0
  68. package/dist/src/config/providerPersistence.js.map +1 -0
  69. package/dist/src/config/providerPersistence.test.d.ts +6 -0
  70. package/dist/src/config/providerPersistence.test.js +283 -0
  71. package/dist/src/config/providerPersistence.test.js.map +1 -0
  72. package/dist/src/config/settingsSchema.d.ts +9 -0
  73. package/dist/src/config/settingsSchema.js +9 -0
  74. package/dist/src/config/settingsSchema.js.map +1 -1
  75. package/dist/src/generated/git-commit.d.ts +1 -1
  76. package/dist/src/generated/git-commit.js +1 -1
  77. package/dist/src/services/BuiltinCommandLoader.js +2 -0
  78. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  79. package/dist/src/ui/App.js +14 -2
  80. package/dist/src/ui/App.js.map +1 -1
  81. package/dist/src/ui/commands/contextCommand.d.ts +7 -0
  82. package/dist/src/ui/commands/contextCommand.js +115 -0
  83. package/dist/src/ui/commands/contextCommand.js.map +1 -0
  84. package/dist/src/ui/components/ContextUsageDisplay.d.ts +3 -1
  85. package/dist/src/ui/components/ContextUsageDisplay.js +43 -3
  86. package/dist/src/ui/components/ContextUsageDisplay.js.map +1 -1
  87. package/dist/src/ui/components/Footer.d.ts +1 -0
  88. package/dist/src/ui/components/Footer.js +2 -2
  89. package/dist/src/ui/components/Footer.js.map +1 -1
  90. package/dist/src/ui/components/GeminiKeyDialog.d.ts +11 -0
  91. package/dist/src/ui/components/GeminiKeyDialog.js +156 -0
  92. package/dist/src/ui/components/GeminiKeyDialog.js.map +1 -0
  93. package/dist/src/ui/components/OpenAIEndpointDialog.d.ts +19 -0
  94. package/dist/src/ui/components/OpenAIEndpointDialog.js +163 -0
  95. package/dist/src/ui/components/OpenAIEndpointDialog.js.map +1 -0
  96. package/dist/src/ui/components/WelcomeBackDialog.d.ts +36 -0
  97. package/dist/src/ui/components/WelcomeBackDialog.js +109 -0
  98. package/dist/src/ui/components/WelcomeBackDialog.js.map +1 -0
  99. package/dist/src/ui/hooks/useWelcomeBack.d.ts +52 -0
  100. package/dist/src/ui/hooks/useWelcomeBack.js +214 -0
  101. package/dist/src/ui/hooks/useWelcomeBack.js.map +1 -0
  102. package/dist/src/zed-integration/schema.d.ts +1516 -1516
  103. package/dist/test-setup.ts +12 -0
  104. package/dist/test-utils/customMatchers.ts +65 -0
  105. package/dist/test-utils/mockCommandContext.test.ts +62 -0
  106. package/dist/test-utils/mockCommandContext.ts +105 -0
  107. package/dist/test-utils/render.tsx +18 -0
  108. package/dist/tsconfig.tsbuildinfo +1 -1
  109. package/dist/ui/App.test.tsx +2181 -0
  110. package/dist/ui/App.tsx +1344 -0
  111. package/dist/ui/IdeIntegrationNudge.tsx +98 -0
  112. package/dist/ui/__snapshots__/App.test.tsx.snap +124 -0
  113. package/dist/ui/colors.ts +56 -0
  114. package/dist/ui/commands/aboutCommand.test.ts +153 -0
  115. package/dist/ui/commands/aboutCommand.ts +49 -0
  116. package/dist/ui/commands/authCommand.test.ts +36 -0
  117. package/dist/ui/commands/authCommand.ts +17 -0
  118. package/dist/ui/commands/bugCommand.test.ts +114 -0
  119. package/dist/ui/commands/bugCommand.ts +92 -0
  120. package/dist/ui/commands/chatCommand.test.ts +414 -0
  121. package/dist/ui/commands/chatCommand.ts +280 -0
  122. package/dist/ui/commands/clearCommand.test.ts +100 -0
  123. package/dist/ui/commands/clearCommand.ts +29 -0
  124. package/dist/ui/commands/compressCommand.test.ts +129 -0
  125. package/dist/ui/commands/compressCommand.ts +78 -0
  126. package/dist/ui/commands/contextCommand.ts +132 -0
  127. package/dist/ui/commands/copyCommand.test.ts +296 -0
  128. package/dist/ui/commands/copyCommand.ts +67 -0
  129. package/dist/ui/commands/corgiCommand.test.ts +34 -0
  130. package/dist/ui/commands/corgiCommand.ts +16 -0
  131. package/dist/ui/commands/directoryCommand.test.tsx +185 -0
  132. package/dist/ui/commands/directoryCommand.tsx +179 -0
  133. package/dist/ui/commands/docsCommand.test.ts +99 -0
  134. package/dist/ui/commands/docsCommand.ts +42 -0
  135. package/dist/ui/commands/editorCommand.test.ts +30 -0
  136. package/dist/ui/commands/editorCommand.ts +21 -0
  137. package/dist/ui/commands/extensionsCommand.test.ts +67 -0
  138. package/dist/ui/commands/extensionsCommand.ts +46 -0
  139. package/dist/ui/commands/helpCommand.test.ts +52 -0
  140. package/dist/ui/commands/helpCommand.ts +23 -0
  141. package/dist/ui/commands/ideCommand.test.ts +255 -0
  142. package/dist/ui/commands/ideCommand.ts +283 -0
  143. package/dist/ui/commands/initCommand.test.ts +127 -0
  144. package/dist/ui/commands/initCommand.ts +117 -0
  145. package/dist/ui/commands/mcpCommand.test.ts +1057 -0
  146. package/dist/ui/commands/mcpCommand.ts +531 -0
  147. package/dist/ui/commands/memoryCommand.test.ts +344 -0
  148. package/dist/ui/commands/memoryCommand.ts +305 -0
  149. package/dist/ui/commands/privacyCommand.test.ts +38 -0
  150. package/dist/ui/commands/privacyCommand.ts +17 -0
  151. package/dist/ui/commands/quitCommand.test.ts +55 -0
  152. package/dist/ui/commands/quitCommand.ts +36 -0
  153. package/dist/ui/commands/restoreCommand.test.ts +250 -0
  154. package/dist/ui/commands/restoreCommand.ts +157 -0
  155. package/dist/ui/commands/searchEngineSetupCommand.ts +18 -0
  156. package/dist/ui/commands/settingsCommand.test.ts +36 -0
  157. package/dist/ui/commands/settingsCommand.ts +17 -0
  158. package/dist/ui/commands/setupGithubCommand.test.ts +238 -0
  159. package/dist/ui/commands/setupGithubCommand.ts +212 -0
  160. package/dist/ui/commands/speakCommand.ts +175 -0
  161. package/dist/ui/commands/statsCommand.test.ts +78 -0
  162. package/dist/ui/commands/statsCommand.ts +70 -0
  163. package/dist/ui/commands/terminalSetupCommand.test.ts +85 -0
  164. package/dist/ui/commands/terminalSetupCommand.ts +45 -0
  165. package/dist/ui/commands/themeCommand.test.ts +38 -0
  166. package/dist/ui/commands/themeCommand.ts +17 -0
  167. package/dist/ui/commands/toolsCommand.test.ts +105 -0
  168. package/dist/ui/commands/toolsCommand.ts +71 -0
  169. package/dist/ui/commands/ttsCommand.ts +143 -0
  170. package/dist/ui/commands/types.ts +204 -0
  171. package/dist/ui/commands/vimCommand.ts +25 -0
  172. package/dist/ui/commands/voiceCommand.ts +125 -0
  173. package/dist/ui/components/AboutBox.tsx +133 -0
  174. package/dist/ui/components/AsciiArt.ts +54 -0
  175. package/dist/ui/components/AuthDialog.test.tsx +334 -0
  176. package/dist/ui/components/AuthDialog.tsx +289 -0
  177. package/dist/ui/components/AuthInProgress.tsx +62 -0
  178. package/dist/ui/components/AutoAcceptIndicator.tsx +47 -0
  179. package/dist/ui/components/ConsoleSummaryDisplay.tsx +35 -0
  180. package/dist/ui/components/ContextSummaryDisplay.test.tsx +85 -0
  181. package/dist/ui/components/ContextSummaryDisplay.tsx +120 -0
  182. package/dist/ui/components/ContextUsageDisplay.tsx +77 -0
  183. package/dist/ui/components/DebugProfiler.tsx +36 -0
  184. package/dist/ui/components/DetailedMessagesDisplay.tsx +82 -0
  185. package/dist/ui/components/EditorSettingsDialog.tsx +172 -0
  186. package/dist/ui/components/FolderTrustDialog.test.tsx +36 -0
  187. package/dist/ui/components/FolderTrustDialog.tsx +74 -0
  188. package/dist/ui/components/Footer.test.tsx +159 -0
  189. package/dist/ui/components/Footer.tsx +158 -0
  190. package/dist/ui/components/GeminiKeyDialog.tsx +252 -0
  191. package/dist/ui/components/GeminiRespondingSpinner.tsx +34 -0
  192. package/dist/ui/components/Header.test.tsx +44 -0
  193. package/dist/ui/components/Header.tsx +70 -0
  194. package/dist/ui/components/Help.tsx +174 -0
  195. package/dist/ui/components/HistoryItemDisplay.test.tsx +125 -0
  196. package/dist/ui/components/HistoryItemDisplay.tsx +98 -0
  197. package/dist/ui/components/InputPrompt.test.tsx +1467 -0
  198. package/dist/ui/components/InputPrompt.tsx +641 -0
  199. package/dist/ui/components/LMStudioModelPrompt.tsx +215 -0
  200. package/dist/ui/components/LoadingIndicator.test.tsx +296 -0
  201. package/dist/ui/components/LoadingIndicator.tsx +82 -0
  202. package/dist/ui/components/MemoryUsageDisplay.tsx +36 -0
  203. package/dist/ui/components/ModelStatsDisplay.test.tsx +252 -0
  204. package/dist/ui/components/ModelStatsDisplay.tsx +197 -0
  205. package/dist/ui/components/OllamaModelPrompt.tsx +206 -0
  206. package/dist/ui/components/OpenAIEndpointDialog.tsx +261 -0
  207. package/dist/ui/components/OpenAIKeyPrompt.test.tsx +64 -0
  208. package/dist/ui/components/OpenAIKeyPrompt.tsx +197 -0
  209. package/dist/ui/components/PrepareLabel.tsx +48 -0
  210. package/dist/ui/components/SearchEngineConfigDialog.tsx +280 -0
  211. package/dist/ui/components/SessionSummaryDisplay.test.tsx +75 -0
  212. package/dist/ui/components/SessionSummaryDisplay.tsx +18 -0
  213. package/dist/ui/components/SettingsDialog.test.tsx +865 -0
  214. package/dist/ui/components/SettingsDialog.tsx +753 -0
  215. package/dist/ui/components/ShellConfirmationDialog.test.tsx +53 -0
  216. package/dist/ui/components/ShellConfirmationDialog.tsx +103 -0
  217. package/dist/ui/components/ShellModeIndicator.tsx +18 -0
  218. package/dist/ui/components/ShowMoreLines.tsx +40 -0
  219. package/dist/ui/components/StatsDisplay.test.tsx +401 -0
  220. package/dist/ui/components/StatsDisplay.tsx +273 -0
  221. package/dist/ui/components/SuggestionsDisplay.tsx +102 -0
  222. package/dist/ui/components/ThemeDialog.tsx +310 -0
  223. package/dist/ui/components/Tips.tsx +45 -0
  224. package/dist/ui/components/TodoDisplay.test.tsx +97 -0
  225. package/dist/ui/components/TodoDisplay.tsx +72 -0
  226. package/dist/ui/components/ToolStatsDisplay.test.tsx +180 -0
  227. package/dist/ui/components/ToolStatsDisplay.tsx +208 -0
  228. package/dist/ui/components/UpdateNotification.tsx +23 -0
  229. package/dist/ui/components/WelcomeBackDialog.tsx +290 -0
  230. package/dist/ui/components/__snapshots__/IDEContextDetailDisplay.test.tsx.snap +24 -0
  231. package/dist/ui/components/__snapshots__/ModelStatsDisplay.test.tsx.snap +121 -0
  232. package/dist/ui/components/__snapshots__/SessionSummaryDisplay.test.tsx.snap +30 -0
  233. package/dist/ui/components/__snapshots__/ShellConfirmationDialog.test.tsx.snap +21 -0
  234. package/dist/ui/components/__snapshots__/StatsDisplay.test.tsx.snap +264 -0
  235. package/dist/ui/components/__snapshots__/ToolStatsDisplay.test.tsx.snap +91 -0
  236. package/dist/ui/components/messages/CompressionMessage.tsx +49 -0
  237. package/dist/ui/components/messages/DiffRenderer.test.tsx +365 -0
  238. package/dist/ui/components/messages/DiffRenderer.tsx +358 -0
  239. package/dist/ui/components/messages/ErrorMessage.tsx +31 -0
  240. package/dist/ui/components/messages/GeminiMessage.tsx +43 -0
  241. package/dist/ui/components/messages/GeminiMessageContent.tsx +43 -0
  242. package/dist/ui/components/messages/InfoMessage.tsx +32 -0
  243. package/dist/ui/components/messages/ToolConfirmationMessage.test.tsx +58 -0
  244. package/dist/ui/components/messages/ToolConfirmationMessage.tsx +297 -0
  245. package/dist/ui/components/messages/ToolGroupMessage.tsx +126 -0
  246. package/dist/ui/components/messages/ToolMessage.test.tsx +183 -0
  247. package/dist/ui/components/messages/ToolMessage.tsx +296 -0
  248. package/dist/ui/components/messages/UserMessage.tsx +43 -0
  249. package/dist/ui/components/messages/UserShellMessage.tsx +25 -0
  250. package/dist/ui/components/shared/MaxSizedBox.test.tsx +425 -0
  251. package/dist/ui/components/shared/MaxSizedBox.tsx +624 -0
  252. package/dist/ui/components/shared/RadioButtonSelect.test.tsx +181 -0
  253. package/dist/ui/components/shared/RadioButtonSelect.tsx +234 -0
  254. package/dist/ui/components/shared/__snapshots__/RadioButtonSelect.test.tsx.snap +47 -0
  255. package/dist/ui/components/shared/text-buffer.test.ts +1728 -0
  256. package/dist/ui/components/shared/text-buffer.ts +2227 -0
  257. package/dist/ui/components/shared/vim-buffer-actions.test.ts +1119 -0
  258. package/dist/ui/components/shared/vim-buffer-actions.ts +814 -0
  259. package/dist/ui/constants.ts +17 -0
  260. package/dist/ui/contexts/KeypressContext.test.tsx +391 -0
  261. package/dist/ui/contexts/KeypressContext.tsx +440 -0
  262. package/dist/ui/contexts/OverflowContext.tsx +87 -0
  263. package/dist/ui/contexts/SessionContext.test.tsx +132 -0
  264. package/dist/ui/contexts/SessionContext.tsx +143 -0
  265. package/dist/ui/contexts/SettingsContext.tsx +20 -0
  266. package/dist/ui/contexts/StreamingContext.tsx +22 -0
  267. package/dist/ui/contexts/VimModeContext.tsx +79 -0
  268. package/dist/ui/editors/editorSettingsManager.ts +66 -0
  269. package/dist/ui/hooks/atCommandProcessor.test.ts +1102 -0
  270. package/dist/ui/hooks/atCommandProcessor.ts +485 -0
  271. package/dist/ui/hooks/shellCommandProcessor.test.ts +481 -0
  272. package/dist/ui/hooks/shellCommandProcessor.ts +314 -0
  273. package/dist/ui/hooks/slashCommandProcessor.test.ts +1044 -0
  274. package/dist/ui/hooks/slashCommandProcessor.ts +595 -0
  275. package/dist/ui/hooks/useAtCompletion.test.ts +497 -0
  276. package/dist/ui/hooks/useAtCompletion.ts +244 -0
  277. package/dist/ui/hooks/useAuthCommand.ts +129 -0
  278. package/dist/ui/hooks/useAutoAcceptIndicator.test.ts +300 -0
  279. package/dist/ui/hooks/useAutoAcceptIndicator.ts +52 -0
  280. package/dist/ui/hooks/useBracketedPaste.ts +37 -0
  281. package/dist/ui/hooks/useCommandCompletion.test.ts +518 -0
  282. package/dist/ui/hooks/useCommandCompletion.tsx +238 -0
  283. package/dist/ui/hooks/useCompletion.ts +128 -0
  284. package/dist/ui/hooks/useConsoleMessages.test.ts +147 -0
  285. package/dist/ui/hooks/useConsoleMessages.ts +110 -0
  286. package/dist/ui/hooks/useEditorSettings.test.ts +283 -0
  287. package/dist/ui/hooks/useEditorSettings.ts +75 -0
  288. package/dist/ui/hooks/useFocus.test.ts +119 -0
  289. package/dist/ui/hooks/useFocus.ts +48 -0
  290. package/dist/ui/hooks/useFolderTrust.test.ts +159 -0
  291. package/dist/ui/hooks/useFolderTrust.ts +72 -0
  292. package/dist/ui/hooks/useGeminiStream.test.tsx +1998 -0
  293. package/dist/ui/hooks/useGeminiStream.ts +1017 -0
  294. package/dist/ui/hooks/useGitBranchName.test.ts +280 -0
  295. package/dist/ui/hooks/useGitBranchName.ts +79 -0
  296. package/dist/ui/hooks/useHistoryManager.test.ts +202 -0
  297. package/dist/ui/hooks/useHistoryManager.ts +111 -0
  298. package/dist/ui/hooks/useInputHistory.test.ts +261 -0
  299. package/dist/ui/hooks/useInputHistory.ts +111 -0
  300. package/dist/ui/hooks/useKeypress.test.ts +280 -0
  301. package/dist/ui/hooks/useKeypress.ts +39 -0
  302. package/dist/ui/hooks/useKittyKeyboardProtocol.ts +31 -0
  303. package/dist/ui/hooks/useLoadingIndicator.test.ts +139 -0
  304. package/dist/ui/hooks/useLoadingIndicator.ts +57 -0
  305. package/dist/ui/hooks/useLogger.ts +32 -0
  306. package/dist/ui/hooks/useMessageQueue.test.ts +226 -0
  307. package/dist/ui/hooks/useMessageQueue.ts +69 -0
  308. package/dist/ui/hooks/usePhraseCycler.test.ts +145 -0
  309. package/dist/ui/hooks/usePhraseCycler.ts +198 -0
  310. package/dist/ui/hooks/usePrivacySettings.test.ts +242 -0
  311. package/dist/ui/hooks/usePrivacySettings.ts +150 -0
  312. package/dist/ui/hooks/useReactToolScheduler.ts +309 -0
  313. package/dist/ui/hooks/useRefreshMemoryCommand.ts +7 -0
  314. package/dist/ui/hooks/useReverseSearchCompletion.test.tsx +260 -0
  315. package/dist/ui/hooks/useReverseSearchCompletion.tsx +95 -0
  316. package/dist/ui/hooks/useSettingsCommand.ts +25 -0
  317. package/dist/ui/hooks/useShellHistory.test.ts +219 -0
  318. package/dist/ui/hooks/useShellHistory.ts +133 -0
  319. package/dist/ui/hooks/useShowMemoryCommand.ts +75 -0
  320. package/dist/ui/hooks/useSlashCompletion.test.ts +434 -0
  321. package/dist/ui/hooks/useSlashCompletion.ts +187 -0
  322. package/dist/ui/hooks/useStateAndRef.ts +36 -0
  323. package/dist/ui/hooks/useTerminalSize.ts +32 -0
  324. package/dist/ui/hooks/useThemeCommand.ts +110 -0
  325. package/dist/ui/hooks/useTimer.test.ts +120 -0
  326. package/dist/ui/hooks/useTimer.ts +65 -0
  327. package/dist/ui/hooks/useToolScheduler.test.ts +1123 -0
  328. package/dist/ui/hooks/useWelcomeBack.ts +253 -0
  329. package/dist/ui/hooks/vim.test.ts +1691 -0
  330. package/dist/ui/hooks/vim.ts +784 -0
  331. package/dist/ui/keyMatchers.test.ts +337 -0
  332. package/dist/ui/keyMatchers.ts +105 -0
  333. package/dist/ui/privacy/CloudFreePrivacyNotice.tsx +117 -0
  334. package/dist/ui/privacy/CloudPaidPrivacyNotice.tsx +59 -0
  335. package/dist/ui/privacy/GeminiPrivacyNotice.tsx +62 -0
  336. package/dist/ui/privacy/PrivacyNotice.tsx +42 -0
  337. package/dist/ui/semantic-colors.ts +26 -0
  338. package/dist/ui/themes/ansi-light.ts +150 -0
  339. package/dist/ui/themes/ansi.ts +159 -0
  340. package/dist/ui/themes/atom-one-dark.ts +147 -0
  341. package/dist/ui/themes/ayu-light.ts +139 -0
  342. package/dist/ui/themes/ayu.ts +113 -0
  343. package/dist/ui/themes/color-utils.test.ts +221 -0
  344. package/dist/ui/themes/color-utils.ts +231 -0
  345. package/dist/ui/themes/default-light.ts +108 -0
  346. package/dist/ui/themes/default.ts +151 -0
  347. package/dist/ui/themes/dracula.ts +124 -0
  348. package/dist/ui/themes/fss-code-dark.ts +156 -0
  349. package/dist/ui/themes/fss-dark.ts +113 -0
  350. package/dist/ui/themes/fss-light.ts +139 -0
  351. package/dist/ui/themes/github-dark.ts +147 -0
  352. package/dist/ui/themes/github-light.ts +149 -0
  353. package/dist/ui/themes/googlecode.ts +146 -0
  354. package/dist/ui/themes/no-color.ts +125 -0
  355. package/dist/ui/themes/qwen-dark.ts +118 -0
  356. package/dist/ui/themes/qwen-light.ts +144 -0
  357. package/dist/ui/themes/semantic-tokens.ts +127 -0
  358. package/dist/ui/themes/shades-of-purple.ts +352 -0
  359. package/dist/ui/themes/theme-manager.test.ts +99 -0
  360. package/dist/ui/themes/theme-manager.ts +257 -0
  361. package/dist/ui/themes/theme.test.ts +97 -0
  362. package/dist/ui/themes/theme.ts +451 -0
  363. package/dist/ui/themes/xcode.ts +154 -0
  364. package/dist/ui/types.ts +255 -0
  365. package/dist/ui/utils/CodeColorizer.tsx +217 -0
  366. package/dist/ui/utils/ConsolePatcher.ts +71 -0
  367. package/dist/ui/utils/InlineMarkdownRenderer.tsx +173 -0
  368. package/dist/ui/utils/MarkdownDisplay.test.tsx +244 -0
  369. package/dist/ui/utils/MarkdownDisplay.tsx +415 -0
  370. package/dist/ui/utils/TableRenderer.tsx +159 -0
  371. package/dist/ui/utils/__snapshots__/MarkdownDisplay.test.tsx.snap +93 -0
  372. package/dist/ui/utils/clipboardUtils.test.ts +76 -0
  373. package/dist/ui/utils/clipboardUtils.ts +149 -0
  374. package/dist/ui/utils/commandUtils.test.ts +384 -0
  375. package/dist/ui/utils/commandUtils.ts +106 -0
  376. package/dist/ui/utils/computeStats.test.ts +292 -0
  377. package/dist/ui/utils/computeStats.ts +86 -0
  378. package/dist/ui/utils/displayUtils.test.ts +58 -0
  379. package/dist/ui/utils/displayUtils.ts +32 -0
  380. package/dist/ui/utils/formatters.test.ts +72 -0
  381. package/dist/ui/utils/formatters.ts +63 -0
  382. package/dist/ui/utils/isNarrowWidth.ts +9 -0
  383. package/dist/ui/utils/kittyProtocolDetector.ts +105 -0
  384. package/dist/ui/utils/markdownUtilities.test.ts +50 -0
  385. package/dist/ui/utils/markdownUtilities.ts +125 -0
  386. package/dist/ui/utils/platformConstants.ts +52 -0
  387. package/dist/ui/utils/terminalSetup.ts +342 -0
  388. package/dist/ui/utils/textUtils.ts +40 -0
  389. package/dist/ui/utils/updateCheck.test.ts +163 -0
  390. package/dist/ui/utils/updateCheck.ts +100 -0
  391. package/dist/utils/checks.ts +28 -0
  392. package/dist/utils/cleanup.test.ts +68 -0
  393. package/dist/utils/cleanup.ts +36 -0
  394. package/dist/utils/dialogScopeUtils.ts +64 -0
  395. package/dist/utils/events.ts +14 -0
  396. package/dist/utils/gitUtils.test.ts +149 -0
  397. package/dist/utils/gitUtils.ts +116 -0
  398. package/dist/utils/handleAutoUpdate.test.ts +272 -0
  399. package/dist/utils/handleAutoUpdate.ts +145 -0
  400. package/dist/utils/installationInfo.test.ts +315 -0
  401. package/dist/utils/installationInfo.ts +176 -0
  402. package/dist/utils/package.ts +38 -0
  403. package/dist/utils/readStdin.ts +51 -0
  404. package/dist/utils/resolvePath.ts +21 -0
  405. package/dist/utils/sandbox-macos-permissive-closed.sb +32 -0
  406. package/dist/utils/sandbox-macos-permissive-open.sb +25 -0
  407. package/dist/utils/sandbox-macos-permissive-proxied.sb +37 -0
  408. package/dist/utils/sandbox-macos-restrictive-closed.sb +93 -0
  409. package/dist/utils/sandbox-macos-restrictive-open.sb +96 -0
  410. package/dist/utils/sandbox-macos-restrictive-proxied.sb +98 -0
  411. package/dist/utils/sandbox.ts +962 -0
  412. package/dist/utils/settingsUtils.test.ts +797 -0
  413. package/dist/utils/settingsUtils.ts +489 -0
  414. package/dist/utils/spawnWrapper.ts +9 -0
  415. package/dist/utils/startupWarnings.test.ts +83 -0
  416. package/dist/utils/startupWarnings.ts +40 -0
  417. package/dist/utils/updateEventEmitter.ts +13 -0
  418. package/dist/utils/userStartupWarnings.test.ts +87 -0
  419. package/dist/utils/userStartupWarnings.ts +69 -0
  420. package/dist/utils/version.ts +12 -0
  421. package/dist/validateNonInterActiveAuth.test.ts +260 -0
  422. package/dist/validateNonInterActiveAuth.ts +51 -0
  423. package/dist/vitest.config.ts +37 -0
  424. package/dist/zed-integration/acp.ts +366 -0
  425. package/dist/zed-integration/fileSystemService.ts +47 -0
  426. package/dist/zed-integration/schema.ts +466 -0
  427. package/dist/zed-integration/zedIntegration.ts +944 -0
  428. package/package.json +2 -2
@@ -0,0 +1,489 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Settings, SettingScope, LoadedSettings } from '../config/settings.js';
8
+ import {
9
+ SETTINGS_SCHEMA,
10
+ SettingDefinition,
11
+ SettingsSchema,
12
+ } from '../config/settingsSchema.js';
13
+
14
+ // The schema is now nested, but many parts of the UI and logic work better
15
+ // with a flattened structure and dot-notation keys. This section flattens the
16
+ // schema into a map for easier lookups.
17
+
18
+ function flattenSchema(
19
+ schema: SettingsSchema,
20
+ prefix = '',
21
+ ): Record<string, SettingDefinition & { key: string }> {
22
+ let result: Record<string, SettingDefinition & { key: string }> = {};
23
+ for (const key in schema) {
24
+ const newKey = prefix ? `${prefix}.${key}` : key;
25
+ const definition = schema[key];
26
+ result[newKey] = { ...definition, key: newKey };
27
+ if (definition.properties) {
28
+ result = { ...result, ...flattenSchema(definition.properties, newKey) };
29
+ }
30
+ }
31
+ return result;
32
+ }
33
+
34
+ const FLATTENED_SCHEMA = flattenSchema(SETTINGS_SCHEMA);
35
+
36
+ /**
37
+ * Get all settings grouped by category
38
+ */
39
+ export function getSettingsByCategory(): Record<
40
+ string,
41
+ Array<SettingDefinition & { key: string }>
42
+ > {
43
+ const categories: Record<
44
+ string,
45
+ Array<SettingDefinition & { key: string }>
46
+ > = {};
47
+
48
+ Object.values(FLATTENED_SCHEMA).forEach((definition) => {
49
+ const category = definition.category;
50
+ if (!categories[category]) {
51
+ categories[category] = [];
52
+ }
53
+ categories[category].push(definition);
54
+ });
55
+
56
+ return categories;
57
+ }
58
+
59
+ /**
60
+ * Get a setting definition by key
61
+ */
62
+ export function getSettingDefinition(
63
+ key: string,
64
+ ): (SettingDefinition & { key: string }) | undefined {
65
+ return FLATTENED_SCHEMA[key];
66
+ }
67
+
68
+ /**
69
+ * Check if a setting requires restart
70
+ */
71
+ export function requiresRestart(key: string): boolean {
72
+ return FLATTENED_SCHEMA[key]?.requiresRestart ?? false;
73
+ }
74
+
75
+ /**
76
+ * Get the default value for a setting
77
+ */
78
+ export function getDefaultValue(key: string): SettingDefinition['default'] {
79
+ return FLATTENED_SCHEMA[key]?.default;
80
+ }
81
+
82
+ /**
83
+ * Get all setting keys that require restart
84
+ */
85
+ export function getRestartRequiredSettings(): string[] {
86
+ return Object.values(FLATTENED_SCHEMA)
87
+ .filter((definition) => definition.requiresRestart)
88
+ .map((definition) => definition.key);
89
+ }
90
+
91
+ /**
92
+ * Recursively gets a value from a nested object using a key path array.
93
+ */
94
+ export function getNestedValue(
95
+ obj: Record<string, unknown>,
96
+ path: string[],
97
+ ): unknown {
98
+ const [first, ...rest] = path;
99
+ if (!first || !(first in obj)) {
100
+ return undefined;
101
+ }
102
+ const value = obj[first];
103
+ if (rest.length === 0) {
104
+ return value;
105
+ }
106
+ if (value && typeof value === 'object' && value !== null) {
107
+ return getNestedValue(value as Record<string, unknown>, rest);
108
+ }
109
+ return undefined;
110
+ }
111
+
112
+ /**
113
+ * Get the effective value for a setting, considering inheritance from higher scopes
114
+ * Always returns a value (never undefined) - falls back to default if not set anywhere
115
+ */
116
+ export function getEffectiveValue(
117
+ key: string,
118
+ settings: Settings,
119
+ mergedSettings: Settings,
120
+ ): SettingDefinition['default'] {
121
+ const definition = getSettingDefinition(key);
122
+ if (!definition) {
123
+ return undefined;
124
+ }
125
+
126
+ const path = key.split('.');
127
+
128
+ // Check the current scope's settings first
129
+ let value = getNestedValue(settings as Record<string, unknown>, path);
130
+ if (value !== undefined) {
131
+ return value as SettingDefinition['default'];
132
+ }
133
+
134
+ // Check the merged settings for an inherited value
135
+ value = getNestedValue(mergedSettings as Record<string, unknown>, path);
136
+ if (value !== undefined) {
137
+ return value as SettingDefinition['default'];
138
+ }
139
+
140
+ // Return default value if no value is set anywhere
141
+ return definition.default;
142
+ }
143
+
144
+ /**
145
+ * Get all setting keys from the schema
146
+ */
147
+ export function getAllSettingKeys(): string[] {
148
+ return Object.keys(FLATTENED_SCHEMA);
149
+ }
150
+
151
+ /**
152
+ * Get settings by type
153
+ */
154
+ export function getSettingsByType(
155
+ type: SettingDefinition['type'],
156
+ ): Array<SettingDefinition & { key: string }> {
157
+ return Object.values(FLATTENED_SCHEMA).filter(
158
+ (definition) => definition.type === type,
159
+ );
160
+ }
161
+
162
+ /**
163
+ * Get settings that require restart
164
+ */
165
+ export function getSettingsRequiringRestart(): Array<
166
+ SettingDefinition & {
167
+ key: string;
168
+ }
169
+ > {
170
+ return Object.values(FLATTENED_SCHEMA).filter(
171
+ (definition) => definition.requiresRestart,
172
+ );
173
+ }
174
+
175
+ /**
176
+ * Validate if a setting key exists in the schema
177
+ */
178
+ export function isValidSettingKey(key: string): boolean {
179
+ return key in FLATTENED_SCHEMA;
180
+ }
181
+
182
+ /**
183
+ * Get the category for a setting
184
+ */
185
+ export function getSettingCategory(key: string): string | undefined {
186
+ return FLATTENED_SCHEMA[key]?.category;
187
+ }
188
+
189
+ /**
190
+ * Check if a setting should be shown in the settings dialog
191
+ */
192
+ export function shouldShowInDialog(key: string): boolean {
193
+ return FLATTENED_SCHEMA[key]?.showInDialog ?? true; // Default to true for backward compatibility
194
+ }
195
+
196
+ /**
197
+ * Get all settings that should be shown in the dialog, grouped by category
198
+ */
199
+ export function getDialogSettingsByCategory(): Record<
200
+ string,
201
+ Array<SettingDefinition & { key: string }>
202
+ > {
203
+ const categories: Record<
204
+ string,
205
+ Array<SettingDefinition & { key: string }>
206
+ > = {};
207
+
208
+ Object.values(FLATTENED_SCHEMA)
209
+ .filter((definition) => definition.showInDialog !== false)
210
+ .forEach((definition) => {
211
+ const category = definition.category;
212
+ if (!categories[category]) {
213
+ categories[category] = [];
214
+ }
215
+ categories[category].push(definition);
216
+ });
217
+
218
+ return categories;
219
+ }
220
+
221
+ /**
222
+ * Get settings by type that should be shown in the dialog
223
+ */
224
+ export function getDialogSettingsByType(
225
+ type: SettingDefinition['type'],
226
+ ): Array<SettingDefinition & { key: string }> {
227
+ return Object.values(FLATTENED_SCHEMA).filter(
228
+ (definition) =>
229
+ definition.type === type && definition.showInDialog !== false,
230
+ );
231
+ }
232
+
233
+ /**
234
+ * Get all setting keys that should be shown in the dialog
235
+ */
236
+ export function getDialogSettingKeys(): string[] {
237
+ return Object.values(FLATTENED_SCHEMA)
238
+ .filter((definition) => definition.showInDialog !== false)
239
+ .map((definition) => definition.key);
240
+ }
241
+
242
+ // ============================================================================
243
+ // BUSINESS LOGIC UTILITIES (Higher-level utilities for setting operations)
244
+ // ============================================================================
245
+
246
+ /**
247
+ * Get the current value for a setting in a specific scope
248
+ * Always returns a value (never undefined) - falls back to default if not set anywhere
249
+ */
250
+ export function getSettingValue(
251
+ key: string,
252
+ settings: Settings,
253
+ mergedSettings: Settings,
254
+ ): boolean {
255
+ const definition = getSettingDefinition(key);
256
+ if (!definition) {
257
+ return false; // Default fallback for invalid settings
258
+ }
259
+
260
+ const value = getEffectiveValue(key, settings, mergedSettings);
261
+ // Ensure we return a boolean value, converting from the more general type
262
+ if (typeof value === 'boolean') {
263
+ return value;
264
+ }
265
+ // Fall back to default value, ensuring it's a boolean
266
+ const defaultValue = definition.default;
267
+ if (typeof defaultValue === 'boolean') {
268
+ return defaultValue;
269
+ }
270
+ return false; // Final fallback
271
+ }
272
+
273
+ /**
274
+ * Check if a setting value is modified from its default
275
+ */
276
+ export function isSettingModified(key: string, value: boolean): boolean {
277
+ const defaultValue = getDefaultValue(key);
278
+ // Handle type comparison properly
279
+ if (typeof defaultValue === 'boolean') {
280
+ return value !== defaultValue;
281
+ }
282
+ // If default is not a boolean, consider it modified if value is true
283
+ return value === true;
284
+ }
285
+
286
+ /**
287
+ * Check if a setting exists in the original settings file for a scope
288
+ */
289
+ export function settingExistsInScope(
290
+ key: string,
291
+ scopeSettings: Settings,
292
+ ): boolean {
293
+ const path = key.split('.');
294
+ const value = getNestedValue(scopeSettings as Record<string, unknown>, path);
295
+ return value !== undefined;
296
+ }
297
+
298
+ /**
299
+ * Recursively sets a value in a nested object using a key path array.
300
+ */
301
+ function setNestedValue(
302
+ obj: Record<string, unknown>,
303
+ path: string[],
304
+ value: unknown,
305
+ ): Record<string, unknown> {
306
+ const [first, ...rest] = path;
307
+ if (!first) {
308
+ return obj;
309
+ }
310
+
311
+ if (rest.length === 0) {
312
+ obj[first] = value;
313
+ return obj;
314
+ }
315
+
316
+ if (!obj[first] || typeof obj[first] !== 'object') {
317
+ obj[first] = {};
318
+ }
319
+
320
+ setNestedValue(obj[first] as Record<string, unknown>, rest, value);
321
+ return obj;
322
+ }
323
+
324
+ /**
325
+ * Set a setting value in the pending settings
326
+ */
327
+ export function setPendingSettingValue(
328
+ key: string,
329
+ value: boolean,
330
+ pendingSettings: Settings,
331
+ ): Settings {
332
+ const path = key.split('.');
333
+ const newSettings = JSON.parse(JSON.stringify(pendingSettings));
334
+ setNestedValue(newSettings, path, value);
335
+ return newSettings;
336
+ }
337
+
338
+ /**
339
+ * Generic setter: Set a setting value (boolean, number, string, etc.) in the pending settings
340
+ */
341
+ export function setPendingSettingValueAny(
342
+ key: string,
343
+ value: unknown,
344
+ pendingSettings: Settings,
345
+ ): Settings {
346
+ const path = key.split('.');
347
+ const newSettings = structuredClone(pendingSettings);
348
+ setNestedValue(newSettings, path, value);
349
+ return newSettings;
350
+ }
351
+
352
+ /**
353
+ * Check if any modified settings require a restart
354
+ */
355
+ export function hasRestartRequiredSettings(
356
+ modifiedSettings: Set<string>,
357
+ ): boolean {
358
+ return Array.from(modifiedSettings).some((key) => requiresRestart(key));
359
+ }
360
+
361
+ /**
362
+ * Get the restart required settings from a set of modified settings
363
+ */
364
+ export function getRestartRequiredFromModified(
365
+ modifiedSettings: Set<string>,
366
+ ): string[] {
367
+ return Array.from(modifiedSettings).filter((key) => requiresRestart(key));
368
+ }
369
+
370
+ /**
371
+ * Save modified settings to the appropriate scope
372
+ */
373
+ export function saveModifiedSettings(
374
+ modifiedSettings: Set<string>,
375
+ pendingSettings: Settings,
376
+ loadedSettings: LoadedSettings,
377
+ scope: SettingScope,
378
+ ): void {
379
+ modifiedSettings.forEach((settingKey) => {
380
+ const path = settingKey.split('.');
381
+ const value = getNestedValue(
382
+ pendingSettings as Record<string, unknown>,
383
+ path,
384
+ );
385
+
386
+ if (value === undefined) {
387
+ return;
388
+ }
389
+
390
+ const existsInOriginalFile = settingExistsInScope(
391
+ settingKey,
392
+ loadedSettings.forScope(scope).settings,
393
+ );
394
+
395
+ const isDefaultValue = value === getDefaultValue(settingKey);
396
+
397
+ if (existsInOriginalFile || !isDefaultValue) {
398
+ // This is tricky because setValue only works on top-level keys.
399
+ // We need to set the whole parent object.
400
+ const [parentKey] = path;
401
+ if (parentKey) {
402
+ const newParentValue = setPendingSettingValueAny(
403
+ settingKey,
404
+ value,
405
+ loadedSettings.forScope(scope).settings,
406
+ )[parentKey as keyof Settings];
407
+
408
+ loadedSettings.setValue(
409
+ scope,
410
+ parentKey as keyof Settings,
411
+ newParentValue,
412
+ );
413
+ }
414
+ }
415
+ });
416
+ }
417
+
418
+ /**
419
+ * Get the display value for a setting, showing current scope value with default change indicator
420
+ */
421
+ export function getDisplayValue(
422
+ key: string,
423
+ settings: Settings,
424
+ _mergedSettings: Settings,
425
+ modifiedSettings: Set<string>,
426
+ pendingSettings?: Settings,
427
+ ): string {
428
+ // Prioritize pending changes if user has modified this setting
429
+ let value: boolean;
430
+ if (pendingSettings && settingExistsInScope(key, pendingSettings)) {
431
+ // Show the value from the pending (unsaved) edits when it exists
432
+ value = getSettingValue(key, pendingSettings, {});
433
+ } else if (settingExistsInScope(key, settings)) {
434
+ // Show the value defined at the current scope if present
435
+ value = getSettingValue(key, settings, {});
436
+ } else {
437
+ // Fall back to the schema default when the key is unset in this scope
438
+ const defaultValue = getDefaultValue(key);
439
+ value = typeof defaultValue === 'boolean' ? defaultValue : false;
440
+ }
441
+
442
+ const valueString = String(value);
443
+
444
+ // Check if value is different from default OR if it's in modified settings OR if there are pending changes
445
+ const defaultValue = getDefaultValue(key);
446
+ const isChangedFromDefault =
447
+ typeof defaultValue === 'boolean' ? value !== defaultValue : value === true;
448
+ const isInModifiedSettings = modifiedSettings.has(key);
449
+
450
+ // Mark as modified if setting exists in current scope OR is in modified settings
451
+ if (settingExistsInScope(key, settings) || isInModifiedSettings) {
452
+ return `${valueString}*`; // * indicates setting is set in current scope
453
+ }
454
+ if (isChangedFromDefault || isInModifiedSettings) {
455
+ return `${valueString}*`; // * indicates changed from default value
456
+ }
457
+
458
+ return valueString;
459
+ }
460
+
461
+ /**
462
+ * Check if a setting doesn't exist in current scope (should be greyed out)
463
+ */
464
+ export function isDefaultValue(key: string, settings: Settings): boolean {
465
+ return !settingExistsInScope(key, settings);
466
+ }
467
+
468
+ /**
469
+ * Check if a setting value is inherited (not set at current scope)
470
+ */
471
+ export function isValueInherited(
472
+ key: string,
473
+ settings: Settings,
474
+ _mergedSettings: Settings,
475
+ ): boolean {
476
+ return !settingExistsInScope(key, settings);
477
+ }
478
+
479
+ /**
480
+ * Get the effective value for display, considering inheritance
481
+ * Always returns a boolean value (never undefined)
482
+ */
483
+ export function getEffectiveDisplayValue(
484
+ key: string,
485
+ settings: Settings,
486
+ mergedSettings: Settings,
487
+ ): boolean {
488
+ return getSettingValue(key, settings, mergedSettings);
489
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { spawn } from 'child_process';
8
+
9
+ export const spawnWrapper = spawn;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
8
+ import { getStartupWarnings } from './startupWarnings.js';
9
+ import { getErrorMessage } from 'fss-link-core';
10
+ import fs from 'fs/promises';
11
+
12
+ // Mock fs/promises with proper factory
13
+ vi.mock('fs/promises', () => ({
14
+ default: {
15
+ access: vi.fn(),
16
+ readFile: vi.fn(),
17
+ unlink: vi.fn(),
18
+ },
19
+ }));
20
+
21
+ vi.mock('fss-link-core', async (importOriginal) => {
22
+ const actual = await importOriginal();
23
+ return {
24
+ ...actual,
25
+ getErrorMessage: vi.fn(),
26
+ };
27
+ });
28
+
29
+ describe('startupWarnings', () => {
30
+ beforeEach(() => {
31
+ vi.resetAllMocks();
32
+ });
33
+
34
+ it('should return warnings from the file and delete it', async () => {
35
+ const mockWarnings = 'Warning 1\nWarning 2';
36
+ vi.mocked(fs.access).mockResolvedValue(undefined);
37
+ vi.mocked(fs.readFile).mockResolvedValue(mockWarnings);
38
+ vi.mocked(fs.unlink).mockResolvedValue(undefined);
39
+
40
+ const warnings = await getStartupWarnings();
41
+
42
+ expect(fs.access).toHaveBeenCalled();
43
+ expect(fs.readFile).toHaveBeenCalled();
44
+ expect(fs.unlink).toHaveBeenCalled();
45
+ expect(warnings).toEqual(['Warning 1', 'Warning 2']);
46
+ });
47
+
48
+ it('should return an empty array if the file does not exist', async () => {
49
+ const error = new Error('File not found');
50
+ (error as Error & { code: string }).code = 'ENOENT';
51
+ vi.mocked(fs.access).mockRejectedValue(error);
52
+
53
+ const warnings = await getStartupWarnings();
54
+
55
+ expect(warnings).toEqual([]);
56
+ });
57
+
58
+ it('should return an error message if reading the file fails', async () => {
59
+ const error = new Error('Permission denied');
60
+ vi.mocked(fs.access).mockRejectedValue(error);
61
+ vi.mocked(getErrorMessage).mockReturnValue('Permission denied');
62
+
63
+ const warnings = await getStartupWarnings();
64
+
65
+ expect(warnings).toEqual([
66
+ 'Error checking/reading warnings file: Permission denied',
67
+ ]);
68
+ });
69
+
70
+ it('should return a warning if deleting the file fails', async () => {
71
+ const mockWarnings = 'Warning 1';
72
+ vi.mocked(fs.access).mockResolvedValue(undefined);
73
+ vi.mocked(fs.readFile).mockResolvedValue(mockWarnings);
74
+ vi.mocked(fs.unlink).mockRejectedValue(new Error('Permission denied'));
75
+
76
+ const warnings = await getStartupWarnings();
77
+
78
+ expect(warnings).toEqual([
79
+ 'Warning 1',
80
+ 'Warning: Could not delete temporary warnings file.',
81
+ ]);
82
+ });
83
+ });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import fs from 'fs/promises';
8
+ import os from 'os';
9
+ import { join as pathJoin } from 'node:path';
10
+ import { getErrorMessage } from 'fss-link-core';
11
+
12
+ const warningsFilePath = pathJoin(os.tmpdir(), 'fss-link-warnings.txt');
13
+
14
+ export async function getStartupWarnings(): Promise<string[]> {
15
+ try {
16
+ await fs.access(warningsFilePath); // Check if file exists
17
+ const warningsContent = await fs.readFile(warningsFilePath, 'utf-8');
18
+ const warnings = warningsContent
19
+ .split('\n')
20
+ .filter((line) => line.trim() !== '');
21
+ try {
22
+ await fs.unlink(warningsFilePath);
23
+ } catch {
24
+ warnings.push('Warning: Could not delete temporary warnings file.');
25
+ }
26
+ return warnings;
27
+ } catch (err: unknown) {
28
+ // If fs.access throws, it means the file doesn't exist or is not accessible.
29
+ // This is not an error in the context of fetching warnings, so return empty.
30
+ // Only return an error message if it's not a "file not found" type error.
31
+ // However, the original logic returned an error message for any fs.existsSync failure.
32
+ // To maintain closer parity while making it async, we'll check the error code.
33
+ // ENOENT is "Error NO ENTry" (file not found).
34
+ if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
35
+ return []; // File not found, no warnings to return.
36
+ }
37
+ // For other errors (permissions, etc.), return the error message.
38
+ return [`Error checking/reading warnings file: ${getErrorMessage(err)}`];
39
+ }
40
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { EventEmitter } from 'events';
8
+
9
+ /**
10
+ * A shared event emitter for application-wide communication
11
+ * between decoupled parts of the CLI.
12
+ */
13
+ export const updateEventEmitter = new EventEmitter();