windows-development-sandbox 1.20.0 → 1.23.0

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 (331) hide show
  1. package/config.example.json +8 -1
  2. package/dist/app/features/access/access.def.js +5 -2
  3. package/dist/app/features/access/access.run.js +7 -3
  4. package/dist/app/features/accounts/accounts.def.d.ts +29 -1
  5. package/dist/app/features/accounts/accounts.def.js +10 -4
  6. package/dist/app/features/accounts/accounts.run.d.ts +1 -0
  7. package/dist/app/features/accounts/accounts.run.js +7 -2
  8. package/dist/app/features/accounts/services.def.d.ts +4 -1
  9. package/dist/app/features/accounts/services.def.js +15 -14
  10. package/dist/app/features/accounts/services.run.d.ts +1 -0
  11. package/dist/app/features/accounts/services.run.js +41 -15
  12. package/dist/app/features/defs.d.ts +39 -2
  13. package/dist/app/features/engine/engine.run.js +1 -1
  14. package/dist/app/features/github/github.def.d.ts +44 -3
  15. package/dist/app/features/github/github.def.js +145 -63
  16. package/dist/app/features/github/github.run.d.ts +9 -2
  17. package/dist/app/features/github/github.run.js +43 -5
  18. package/dist/app/features/health/health.def.js +8 -2
  19. package/dist/app/features/logs/logs.def.js +0 -8
  20. package/dist/app/features/projects/projects.def.js +2 -2
  21. package/dist/app/features/projects/projects.run.js +5 -5
  22. package/dist/app/features/sandbox/sandbox.def.js +7 -3
  23. package/dist/app/features/sessions/sessions.def.d.ts +4 -4
  24. package/dist/app/features/sessions/sessions.def.js +0 -5
  25. package/dist/app/features/sessions/sessions.run.d.ts +1 -1
  26. package/dist/app/features/sessions/sessions.run.js +2 -2
  27. package/dist/app/features/settings/categories.def.js +2 -0
  28. package/dist/app/features/settings/settings-rows.js +1 -1
  29. package/dist/app/features/settings/settings.def.js +1 -1
  30. package/dist/app/features/settings/settings.run.js +3 -3
  31. package/dist/app/features/storage/storage.run.js +18 -9
  32. package/dist/app/features/updates/update-lines.js +2 -2
  33. package/dist/app/features/updates/updates.def.js +1 -1
  34. package/dist/app/features/updates/updates.run.js +1 -1
  35. package/dist/app/features/web/web.def.js +2 -2
  36. package/dist/app/features/web/web.run.js +12 -4
  37. package/dist/app/kernel/errors.d.ts +7 -0
  38. package/dist/app/kernel/errors.js +18 -3
  39. package/dist/app/kernel/rpc.d.ts +5 -0
  40. package/dist/app/kernel/view.d.ts +11 -1
  41. package/dist/bin/agent-sandbox.js +2 -2
  42. package/dist/bin/{chunk-DK4NTBUF.js → chunk-JETGP2C3.js} +172 -48
  43. package/dist/bin/{chunk-2IMT7ZCT.js → chunk-XZKOPGNH.js} +1 -1
  44. package/dist/bin/{chunk-Z2P5OGKL.js → chunk-ZTTOF5V3.js} +2430 -1688
  45. package/dist/bin/claude-host.js +2 -2
  46. package/dist/bin/claude-sbx.js +2 -2
  47. package/dist/bin/codex-sbx.js +2 -2
  48. package/dist/bin/gemini-sbx.js +2 -2
  49. package/dist/bin/sandbox.js +2 -2
  50. package/dist/bin/sbx-hide.js +2 -2
  51. package/dist/bin/sbx-show.js +2 -2
  52. package/dist/cli/manifest.d.ts +2 -2
  53. package/dist/cli/manifest.js +2 -1
  54. package/dist/cli/menu/coverage-exempt.d.ts +17 -0
  55. package/dist/cli/menu/coverage-exempt.js +61 -0
  56. package/dist/cli/menu/io-menu.d.ts +2 -0
  57. package/dist/cli/menu/io-menu.js +50 -5
  58. package/dist/cli/menu/pages/accounts-page.js +1 -1
  59. package/dist/cli/menu/pages/agents-page.js +8 -2
  60. package/dist/cli/menu/pages/maintenance-page.d.ts +6 -0
  61. package/dist/cli/menu/pages/maintenance-page.js +9 -2
  62. package/dist/cli/menu/pages/sessions-page.d.ts +2 -0
  63. package/dist/cli/menu/pages/sessions-page.js +12 -4
  64. package/dist/cli/menu/pages/settings-page.js +12 -1
  65. package/dist/cli/menu/remote-menu.js +1 -1
  66. package/dist/cli/menu/shell.d.ts +5 -0
  67. package/dist/cli/menu/shell.js +13 -0
  68. package/dist/cli/report.js +2 -0
  69. package/dist/cli/tui/app.d.ts +5 -0
  70. package/dist/cli/tui/app.js +2 -0
  71. package/dist/core/config/defaults.js +3 -1
  72. package/dist/core/schema/accounts.d.ts +6 -0
  73. package/dist/core/schema/accounts.js +13 -11
  74. package/dist/core/schema/config.d.ts +20 -8
  75. package/dist/core/schema/config.js +4 -4
  76. package/dist/core/schema/github.js +1 -1
  77. package/dist/core/schema/profile.js +8 -4
  78. package/dist/core/schema/resume.js +1 -1
  79. package/dist/core/schema/sandbox.js +3 -3
  80. package/dist/core/schema/vpn.js +1 -1
  81. package/dist/core/schema/web.js +3 -3
  82. package/dist/core/types.d.ts +6 -0
  83. package/dist/core/util/errors.d.ts +11 -1
  84. package/dist/core/util/errors.js +7 -1
  85. package/dist/core/util/index.d.ts +1 -1
  86. package/dist/core/util/index.js +1 -1
  87. package/dist/guest/accounts/adapter.d.ts +2 -0
  88. package/dist/guest/accounts/adapters.js +4 -0
  89. package/dist/guest/accounts/apply.d.ts +6 -2
  90. package/dist/guest/accounts/kinds/cfx.d.ts +7 -0
  91. package/dist/guest/accounts/kinds/cfx.js +33 -3
  92. package/dist/guest/accounts/kinds/comet.d.ts +7 -0
  93. package/dist/guest/accounts/kinds/comet.js +50 -0
  94. package/dist/guest/accounts/kinds/perplexity.d.ts +2 -0
  95. package/dist/guest/accounts/kinds/perplexity.js +12 -0
  96. package/dist/guest/accounts/watch.d.ts +2 -0
  97. package/dist/guest/accounts/watch.js +12 -2
  98. package/dist/guest/agents/agent-installer.js +1 -1
  99. package/dist/guest/agents/profile-links.d.ts +2 -0
  100. package/dist/guest/agents/profile-links.js +18 -4
  101. package/dist/guest/apps/after-install.js +19 -0
  102. package/dist/guest/apps/app-installer.d.ts +11 -0
  103. package/dist/guest/apps/app-installer.js +69 -6
  104. package/dist/guest/apps/comet-unpack.d.ts +20 -0
  105. package/dist/guest/apps/comet-unpack.js +127 -0
  106. package/dist/guest/apps/index.d.ts +1 -1
  107. package/dist/guest/apps/index.js +1 -1
  108. package/dist/guest/apps/resolvers-agents-web.d.ts +2 -0
  109. package/dist/guest/apps/resolvers-agents-web.js +7 -0
  110. package/dist/guest/apps/resolvers.js +2 -0
  111. package/dist/guest/apps/shortcuts.d.ts +2 -0
  112. package/dist/guest/apps/shortcuts.js +1 -0
  113. package/dist/guest/apps/unpack.js +14 -4
  114. package/dist/guest/boot/boot.d.ts +45 -16
  115. package/dist/guest/boot/boot.js +316 -145
  116. package/dist/guest/boot/graph.d.ts +252 -0
  117. package/dist/guest/boot/graph.js +257 -0
  118. package/dist/guest/boot/guide-lines.d.ts +25 -0
  119. package/dist/guest/boot/guide-lines.js +41 -0
  120. package/dist/guest/boot/guide.d.ts +1 -4
  121. package/dist/guest/boot/guide.js +6 -8
  122. package/dist/guest/boot/runner.d.ts +77 -0
  123. package/dist/guest/boot/runner.js +215 -0
  124. package/dist/guest/browser/cdp.d.ts +71 -0
  125. package/dist/guest/browser/cdp.js +306 -0
  126. package/dist/guest/browser/comet.d.ts +28 -0
  127. package/dist/guest/browser/comet.js +155 -0
  128. package/dist/guest/daemon/project-provisioner.js +6 -1
  129. package/dist/guest/devtools/fivem.d.ts +1 -1
  130. package/dist/guest/devtools/fivem.js +243 -3
  131. package/dist/guest/entries/daemon.js +2 -0
  132. package/dist/guest/runtime/log.d.ts +12 -0
  133. package/dist/guest/runtime/log.js +29 -5
  134. package/dist/guest/runtime/pools.d.ts +24 -0
  135. package/dist/guest/runtime/pools.js +60 -0
  136. package/dist/guest/runtime/process.d.ts +4 -0
  137. package/dist/guest/runtime/process.js +38 -1
  138. package/dist/guest/runtime/status.d.ts +5 -1
  139. package/dist/guest/runtime/status.js +19 -0
  140. package/dist/guest/setup/addon-mcp.d.ts +1 -1
  141. package/dist/guest/setup/addon-mcp.js +52 -12
  142. package/dist/guest/setup/agent-addons.d.ts +3 -2
  143. package/dist/guest/setup/agent-addons.js +12 -3
  144. package/dist/guest/setup/environment.d.ts +1 -1
  145. package/dist/guest/setup/environment.js +25 -12
  146. package/dist/guest/setup/headroom.d.ts +8 -3
  147. package/dist/guest/setup/headroom.js +44 -15
  148. package/dist/guest/setup/path-plan.d.ts +28 -0
  149. package/dist/guest/setup/path-plan.js +69 -2
  150. package/dist/guest/setup/shell-path.d.ts +1 -1
  151. package/dist/guest/setup/shell-path.js +3 -3
  152. package/dist/guest/toolchains/fivem/artifacts.d.ts +48 -1
  153. package/dist/guest/toolchains/fivem/artifacts.js +97 -4
  154. package/dist/guest/toolchains/fivem/fivem.d.ts +32 -1
  155. package/dist/guest/toolchains/fivem/fivem.js +168 -3
  156. package/dist/guest/toolchains/fivem/scaffold.d.ts +35 -0
  157. package/dist/guest/toolchains/fivem/scaffold.js +105 -0
  158. package/dist/guest/toolchains/fivem/serve-plan.d.ts +34 -0
  159. package/dist/guest/toolchains/fivem/serve-plan.js +61 -0
  160. package/dist/guest/toolchains/languages/go.js +1 -1
  161. package/dist/guest/toolchains/languages/node.js +2 -1
  162. package/dist/guest/toolchains/languages/php.d.ts +2 -0
  163. package/dist/guest/toolchains/languages/php.js +13 -1
  164. package/dist/guest/toolchains/languages/python-tools.js +1 -1
  165. package/dist/guest/toolchains/languages/ruby.js +1 -1
  166. package/dist/guest/toolchains/native/binstall.js +1 -1
  167. package/dist/guest/toolchains/native/msvc/packages.js +10 -4
  168. package/dist/guest/toolchains/native/rust.d.ts +2 -0
  169. package/dist/guest/toolchains/native/rust.js +11 -4
  170. package/dist/guest/toolchains/native/unreal.d.ts +2 -0
  171. package/dist/guest/toolchains/native/unreal.js +16 -6
  172. package/dist/guest/toolchains/native/vcruntime.d.ts +2 -0
  173. package/dist/guest/toolchains/native/vcruntime.js +10 -2
  174. package/dist/guest/toolchains/native/visual-studio.d.ts +3 -0
  175. package/dist/guest/toolchains/native/visual-studio.js +44 -26
  176. package/dist/guest/toolchains/native/vs-request.d.ts +2 -0
  177. package/dist/guest/toolchains/native/vs-request.js +2 -0
  178. package/dist/guest/toolchains/plan.js +19 -8
  179. package/dist/guest/toolchains/scheduler.d.ts +2 -21
  180. package/dist/guest/toolchains/scheduler.js +7 -121
  181. package/dist/guest/toolchains/setup.d.ts +15 -2
  182. package/dist/guest/toolchains/setup.js +39 -20
  183. package/dist/guest/toolchains/split.d.ts +28 -0
  184. package/dist/guest/toolchains/split.js +60 -0
  185. package/dist/guest/toolchains/steam/client.js +2 -1
  186. package/dist/guest/toolchains/step-scheduler.d.ts +51 -0
  187. package/dist/guest/toolchains/step-scheduler.js +147 -0
  188. package/dist/guest/toolchains/step.d.ts +13 -0
  189. package/dist/guest/toolchains/tools/git.js +18 -0
  190. package/dist/guest/vpn/openvpn.js +3 -4
  191. package/dist/guest/vpn/wireguard.js +11 -4
  192. package/dist/host/accounts/deliver.js +7 -0
  193. package/dist/host/accounts/handoff.js +2 -6
  194. package/dist/host/accounts/index.d.ts +1 -1
  195. package/dist/host/accounts/index.js +1 -1
  196. package/dist/host/accounts/kinds/cfx.d.ts +1 -1
  197. package/dist/host/accounts/kinds/cfx.js +47 -4
  198. package/dist/host/accounts/kinds/comet.d.ts +2 -0
  199. package/dist/host/accounts/kinds/comet.js +6 -0
  200. package/dist/host/accounts/kinds/perplexity.d.ts +2 -0
  201. package/dist/host/accounts/kinds/perplexity.js +30 -0
  202. package/dist/host/accounts/leases.d.ts +7 -1
  203. package/dist/host/accounts/leases.js +13 -2
  204. package/dist/host/accounts/source.d.ts +7 -1
  205. package/dist/host/accounts/source.js +27 -0
  206. package/dist/host/accounts/sources.js +4 -0
  207. package/dist/host/github/deliver.d.ts +1 -1
  208. package/dist/host/github/deliver.js +20 -1
  209. package/dist/host/github/index.d.ts +2 -1
  210. package/dist/host/github/index.js +1 -0
  211. package/dist/host/github/token.d.ts +13 -1
  212. package/dist/host/github/token.js +19 -2
  213. package/dist/host/github/verify.d.ts +27 -0
  214. package/dist/host/github/verify.js +61 -0
  215. package/dist/host/launch/hints/fivem.d.ts +3 -1
  216. package/dist/host/launch/hints/fivem.js +68 -2
  217. package/dist/host/launch/session-file.js +4 -0
  218. package/dist/host/launch/toolchains.d.ts +2 -1
  219. package/dist/host/launch/toolchains.js +5 -3
  220. package/dist/host/vpn/nordvpn.d.ts +2 -4
  221. package/dist/host/vpn/nordvpn.js +2 -24
  222. package/dist/runtime/agent.js +405 -90
  223. package/dist/runtime/daemon.js +3010 -1383
  224. package/dist/runtime/db.js +333 -90
  225. package/dist/runtime/main.js +4720 -1954
  226. package/dist/runtime/netproxy.js +286 -69
  227. package/dist/runtime/tool.js +2852 -1780
  228. package/dist/runtime/view.js +286 -69
  229. package/dist/selftest/agent-paths.test.js +39 -1
  230. package/dist/selftest/agents-web.test.d.ts +2 -0
  231. package/dist/selftest/agents-web.test.js +647 -0
  232. package/dist/selftest/app.test.js +5 -2
  233. package/dist/selftest/boot-runner.test.d.ts +2 -0
  234. package/dist/selftest/boot-runner.test.js +456 -0
  235. package/dist/selftest/channel.test.js +15 -0
  236. package/dist/selftest/fivem.test.js +249 -3
  237. package/dist/selftest/github.test.js +94 -9
  238. package/dist/selftest/guest-boot.test.js +3 -0
  239. package/dist/selftest/health.test.js +1 -1
  240. package/dist/selftest/index.js +8 -0
  241. package/dist/selftest/layout.test.js +5 -0
  242. package/dist/selftest/menu-coverage.test.d.ts +2 -0
  243. package/dist/selftest/menu-coverage.test.js +46 -0
  244. package/dist/selftest/menu-screens.js +37 -1
  245. package/dist/selftest/persisted-installs.test.js +6 -3
  246. package/dist/selftest/service-accounts.test.js +3 -2
  247. package/dist/selftest/support/engine-golden.js +27 -0
  248. package/dist/selftest/support/fixtures.d.ts +6 -0
  249. package/dist/selftest/support/web-preview.d.ts +34 -1
  250. package/dist/selftest/support/web-preview.js +21 -18
  251. package/dist/selftest/toolchains.test.js +23 -1
  252. package/dist/selftest/ui-text.test.d.ts +7 -0
  253. package/dist/selftest/ui-text.test.js +300 -0
  254. package/dist/shared/accounts/index.d.ts +2 -0
  255. package/dist/shared/accounts/index.js +2 -0
  256. package/dist/shared/accounts/kinds/comet.d.ts +12 -0
  257. package/dist/shared/accounts/kinds/comet.js +64 -0
  258. package/dist/shared/accounts/kinds/perplexity.d.ts +8 -0
  259. package/dist/shared/accounts/kinds/perplexity.js +37 -0
  260. package/dist/shared/accounts/kinds.d.ts +24 -5
  261. package/dist/shared/accounts/kinds.js +86 -4
  262. package/dist/shared/accounts/payloads.d.ts +81 -0
  263. package/dist/shared/accounts/payloads.js +39 -1
  264. package/dist/shared/apps/agent-addons.d.ts +33 -5
  265. package/dist/shared/apps/agent-addons.js +95 -3
  266. package/dist/shared/apps/catalogue-agents-web.d.ts +14 -0
  267. package/dist/shared/apps/catalogue-agents-web.js +112 -0
  268. package/dist/shared/apps/catalogue-modding.js +32 -1
  269. package/dist/shared/apps/catalogue.d.ts +27 -3
  270. package/dist/shared/apps/catalogue.js +2 -1
  271. package/dist/shared/apps/index.d.ts +3 -1
  272. package/dist/shared/apps/index.js +3 -1
  273. package/dist/shared/channel/schema.d.ts +21 -3
  274. package/dist/shared/channel/schema.js +7 -0
  275. package/dist/shared/channel/secrets.d.ts +19 -0
  276. package/dist/shared/concurrency/index.d.ts +3 -0
  277. package/dist/shared/concurrency/index.js +3 -0
  278. package/dist/shared/concurrency/installer-retry.d.ts +6 -0
  279. package/dist/shared/concurrency/installer-retry.js +10 -0
  280. package/dist/shared/concurrency/limiter.d.ts +19 -0
  281. package/dist/shared/concurrency/limiter.js +67 -0
  282. package/dist/shared/concurrency/pools.d.ts +7 -0
  283. package/dist/shared/concurrency/pools.js +9 -0
  284. package/dist/shared/download/archive.d.ts +2 -0
  285. package/dist/shared/download/archive.js +11 -1
  286. package/dist/shared/download/bounded-fetch.d.ts +13 -0
  287. package/dist/shared/download/bounded-fetch.js +40 -0
  288. package/dist/shared/download/downloader.d.ts +5 -0
  289. package/dist/shared/download/downloader.js +50 -38
  290. package/dist/shared/download/index.d.ts +2 -1
  291. package/dist/shared/download/index.js +2 -1
  292. package/dist/shared/readiness.d.ts +2 -2
  293. package/dist/shared/readiness.js +2 -2
  294. package/dist/web/index.html +1 -1
  295. package/dist/web/{main-OUUQYNK4.js → main-CCILLX2M.js} +67 -67
  296. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  297. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  298. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  299. package/package.json +1 -1
  300. package/sandbox/addons/browser-use-mcp/requirements.in +1 -0
  301. package/sandbox/addons/browser-use-mcp/requirements.txt +2350 -0
  302. package/sandbox/addons/chrome-devtools-mcp/package-lock.json +38 -0
  303. package/sandbox/addons/chrome-devtools-mcp/package.json +7 -0
  304. package/sandbox/addons/comet-mcp/package-lock.json +1277 -0
  305. package/sandbox/addons/comet-mcp/package.json +7 -0
  306. package/sandbox/addons/perplexity-mcp/package-lock.json +1655 -0
  307. package/sandbox/addons/perplexity-mcp/package.json +7 -0
  308. package/sandbox/scripts/add-firewall-port.ps1 +18 -4
  309. package/sandbox/scripts/set-machine-environment.ps1 +30 -15
  310. package/sandbox/templates/fivem/README.txt +6 -2
  311. package/sandbox/templates/fivem/cs/Client/Client.csproj +15 -0
  312. package/sandbox/templates/fivem/cs/Client/ClientMain.cs +20 -0
  313. package/sandbox/templates/fivem/cs/Server/Server.csproj +14 -0
  314. package/sandbox/templates/fivem/cs/Server/ServerMain.cs +20 -0
  315. package/sandbox/templates/fivem/cs/fxmanifest.lua +11 -0
  316. package/sandbox/templates/fivem/js/client.js +12 -0
  317. package/sandbox/templates/fivem/js/fxmanifest.lua +9 -0
  318. package/sandbox/templates/fivem/js/server.js +6 -0
  319. package/sandbox/templates/fivem/lua/client.lua +8 -0
  320. package/sandbox/templates/fivem/lua/fxmanifest.lua +10 -0
  321. package/sandbox/templates/fivem/lua/server.lua +6 -0
  322. package/sandbox/templates/fivem/server.cfg +37 -0
  323. package/sandbox/templates/fivem/ts/build.js +17 -0
  324. package/sandbox/templates/fivem/ts/fxmanifest.lua +9 -0
  325. package/sandbox/templates/fivem/ts/package.json +16 -0
  326. package/sandbox/templates/fivem/ts/src/client/client.ts +12 -0
  327. package/sandbox/templates/fivem/ts/src/client/tsconfig.json +13 -0
  328. package/sandbox/templates/fivem/ts/src/server/server.ts +6 -0
  329. package/sandbox/templates/fivem/ts/src/server/tsconfig.json +13 -0
  330. package/schemas/config.schema.json +56 -27
  331. package/schemas/sandbox-config.schema.json +37 -14
@@ -0,0 +1,300 @@
1
+ // What the menu and the web say never tells the user to type a command ("we don't develop a menu to tell people to run
2
+ // commands"): every page of every view, built from sample data, is read for command lines, banned words and command
3
+ // links, and so is every message an operation can fail with (the *.run.ts sources). Text for the command line only
4
+ // is marked (`only: 'cli'` notes, `Empty.cliText`, `next`) and is not read. An exemption names the text and the reason.
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { sandboxPage } from '../app/features/sandbox/index.js';
8
+ import { sessionsPage } from '../app/features/sessions/index.js';
9
+ import { agentsPage } from '../app/features/agents/index.js';
10
+ import { accountsPage, servicePage } from '../app/features/accounts/index.js';
11
+ import { projectsPage } from '../app/features/projects/index.js';
12
+ import { healthPage } from '../app/features/health/index.js';
13
+ import { dataPage } from '../app/features/data/index.js';
14
+ import { storagePage } from '../app/features/storage/index.js';
15
+ import { logsPage } from '../app/features/logs/index.js';
16
+ import { auditPage, auditListing, listAudit } from '../app/features/audit/index.js';
17
+ import { updatesPage } from '../app/features/updates/index.js';
18
+ import { enginePage } from '../app/features/engine/index.js';
19
+ import { webPage } from '../app/features/web/index.js';
20
+ import { vpnPage } from '../app/features/vpn/index.js';
21
+ import { githubPage } from '../app/features/github/index.js';
22
+ import { accessPage } from '../app/features/access/index.js';
23
+ import { appsPage, listApps } from '../app/features/apps/index.js';
24
+ import { settingsPage } from '../app/features/settings/index.js';
25
+ import { QUERIES } from '../app/features/defs.js';
26
+ import { pageActions } from '../app/kernel/index.js';
27
+ import { readQuery } from '../app/registry.js';
28
+ import { COMMANDS } from '../cli/manifest.js';
29
+ import { renderScreens } from './menu-screens.js';
30
+ import { sampleAccess, sampleAccounts, sampleAgents, sampleAuditLog, sampleContained, sampleData, sampleEngines, sampleHealth, sampleLogs, sampleLogTail, sampleProjects, sampleSessions, sampleStatus, sampleStorage, sampleUpdates, } from './support/web-preview.js';
31
+ /**
32
+ * Words the menu and the web never show (the glossary fills this in during the wording pass). "(Beta)" is on the
33
+ * list from the plan but waits for round 3: it is in about twenty setting titles that wording pass renames at once.
34
+ */
35
+ const BANNED = [/\bnot available yet\b/i];
36
+ /** Text that is allowed to name a command: where, which text, and why. Keep this list short. */
37
+ const EXEMPT = [];
38
+ /** Every visible string of a page: what a person reads in the menu or the web (CLI-only text is left out). */
39
+ export function visibleText(page) {
40
+ const out = [page.title];
41
+ const add = (...texts) => {
42
+ for (const t of texts)
43
+ if (t)
44
+ out.push(t);
45
+ };
46
+ const act = (a) => add(a.label, a.verb, a.description);
47
+ if (page.notice && page.notice.only !== 'cli')
48
+ add(page.notice.text);
49
+ if (page.empty) {
50
+ add(page.empty.title, page.empty.text);
51
+ if (page.empty.action)
52
+ act(page.empty.action);
53
+ }
54
+ for (const a of page.actions ?? [])
55
+ act(a);
56
+ for (const s of page.sections) {
57
+ add(s.title, s.summary, s.description, ...(s.text ?? []));
58
+ for (const f of s.facts ?? [])
59
+ add(f.label, f.value, f.description, ...(f.details ?? []));
60
+ for (const n of s.notes ?? [])
61
+ if (n.only !== 'cli')
62
+ add(n.text);
63
+ for (const a of s.actions ?? [])
64
+ act(a);
65
+ for (const i of s.items ?? []) {
66
+ add(i.label, i.hint, i.description, i.keys, i.disabled, i.group, ...(i.details ?? []), ...(i.badges ?? []).map((b) => b.text));
67
+ for (const a of [...(i.primary ? [i.primary] : []), ...(i.actions ?? [])])
68
+ act(a);
69
+ }
70
+ }
71
+ return out;
72
+ }
73
+ /** The commands a person could be told to type: `sandbox <name>` for every command, and the internal `_web`. */
74
+ function commandPattern() {
75
+ const names = new Set(COMMANDS.map((c) => c.name));
76
+ const words = [...names].filter((n) => /^[a-z_][a-z0-9-]*$/.test(n)).map((n) => n.replace(/-/g, '\\-'));
77
+ return new RegExp('\\bsandbox (?:' + words.join('|') + ')\\b');
78
+ }
79
+ /** What is wrong with a text, or undefined. */
80
+ export function textProblem(text, where, command = commandPattern()) {
81
+ if (EXEMPT.some((e) => e.where === where && text.includes(e.text)))
82
+ return undefined;
83
+ const hit = command.exec(text);
84
+ if (hit)
85
+ return 'names a command (' + hit[0] + ')';
86
+ const banned = BANNED.find((b) => b.test(text));
87
+ if (banned)
88
+ return 'uses ' + banned;
89
+ return undefined;
90
+ }
91
+ function serviceRow(over) {
92
+ return {
93
+ kind: 'steam',
94
+ label: 'Steam',
95
+ share: false,
96
+ state: 'none',
97
+ source: null,
98
+ savedAt: null,
99
+ summary: null,
100
+ heldBy: null,
101
+ usedBy: ['fivem'],
102
+ usedFor: 'fivem',
103
+ warnings: ['Steam allows one sign-in at a time.'],
104
+ canImport: true,
105
+ canLogin: true,
106
+ needsFile: false,
107
+ ready: true,
108
+ ...over,
109
+ };
110
+ }
111
+ /** Every page the shared core builds, in the states that print the most text. */
112
+ async function samplePages(dir) {
113
+ const configPath = path.join(dir, 'config.json');
114
+ fs.writeFileSync(configPath, '{}\n');
115
+ fs.mkdirSync(path.join(dir, 'project'), { recursive: true });
116
+ const ctx = {
117
+ loaded: { configPath },
118
+ options: { workspace: path.join(dir, 'project') },
119
+ storageRoot: path.join(dir, 'storage'),
120
+ dataPath: path.join(dir, 'data'),
121
+ };
122
+ const query = (id) => QUERIES.find((q) => q.id === id);
123
+ const settings = await readQuery(query('settings.list'), ctx, { scope: 'user' });
124
+ const apps = (await readQuery(listApps, ctx, { scope: 'user' }));
125
+ const audit = sampleAuditLog(path.join(dir, 'audit'));
126
+ const listing = (enabled) => auditListing(audit, { enabled, days: 7, maxMb: 100 }, listAudit.input.parse({}), path.join(dir, 'audit'));
127
+ const services = (state, share) => ({
128
+ services: [
129
+ serviceRow({ state, share, source: state === 'saved' ? 'pc' : null, summary: state === 'saved' ? 'josh' : null }),
130
+ ],
131
+ });
132
+ const health = sampleHealth();
133
+ const accounts = sampleAccounts();
134
+ const vpn = (login, enabled) => ({
135
+ enabled,
136
+ provider: 'nordvpn',
137
+ protocol: 'udp',
138
+ country: 'Netherlands',
139
+ killSwitch: true,
140
+ login,
141
+ loginSavedAt: login === 'saved' ? '2026-10-14T09:30:00.000Z' : null,
142
+ running: null,
143
+ lastFailure: null,
144
+ });
145
+ const github = (token, shareToken) => ({
146
+ shareToken,
147
+ gitAccess: 'full',
148
+ token,
149
+ kind: token === 'saved' ? 'fine-grained' : null,
150
+ savedAt: token === 'saved' ? '2026-10-14T09:30:00.000Z' : null,
151
+ });
152
+ const pages = [];
153
+ const add = (name, page) => pages.push({ name, page });
154
+ for (const running of [true, false]) {
155
+ add('sandbox ' + running, sandboxPage(sampleStatus(running)));
156
+ add('sessions ' + running, sessionsPage(sampleSessions(running)));
157
+ add('projects ' + running, projectsPage(sampleProjects(running)));
158
+ }
159
+ add('projects empty', projectsPage({ all: false, roots: [], exclude: [], projects: [] }));
160
+ const status = sampleStatus(false);
161
+ add('sandbox leftover', sandboxPage({ ...status, leftover: { text: 'Windows Sandbox programs are left over.' } }));
162
+ add('sandbox ended', sandboxPage({ ...status, lastEnd: { text: 'It ended without being closed.', at: '2026-11-01T10:00:00.000Z' } }));
163
+ add('sandbox no account', sandboxPage({
164
+ ...sampleStatus(true),
165
+ sandbox: { ...status.sandbox, running: true, account: null, headless: true },
166
+ }));
167
+ add('agents', agentsPage(sampleAgents(), { enabled: false, dir: 'C:/x', found: [] }));
168
+ add('accounts', accountsPage(accounts, services('saved', true), github('saved', true)));
169
+ add('accounts none', accountsPage({ ...accounts, agents: accounts.agents.map((a) => ({ ...a, accounts: [] })) }, services('none', false)));
170
+ for (const state of ['none', 'saved', 'unreadable'])
171
+ for (const share of [false, true])
172
+ add('service ' + state + share, servicePage(services(state, share).services[0]));
173
+ add('health', healthPage(health));
174
+ add('health none', healthPage({ last: null }));
175
+ add('data', dataPage(sampleData()));
176
+ add('storage', storagePage(sampleStorage(), sampleContained()));
177
+ add('storage unavailable', storagePage(sampleStorage(), {
178
+ ...sampleContained(),
179
+ available: false,
180
+ reasons: ['No.'],
181
+ fixes: ['Do this.'],
182
+ }));
183
+ const logs = sampleLogs();
184
+ add('logs', logsPage(logs, sampleLogTail({})));
185
+ add('logs none', logsPage({ ...logs, files: [], crash: false }));
186
+ add('audit', auditPage(listing(true)));
187
+ add('audit off', auditPage(listing(false)));
188
+ const updates = sampleUpdates();
189
+ add('updates', updatesPage(updates, '1.5.0'));
190
+ add('updates current', updatesPage({ ...updates, failed: null, previous: null, autoUpdate: false }, null));
191
+ add('updates unmanaged', updatesPage({ ...updates, managed: false }, null));
192
+ add('engine', enginePage(sampleEngines()));
193
+ add('web off', webPage({ running: false, port: null, address: null }));
194
+ add('web on', webPage({ running: true, port: 47800, address: 'http://127.0.0.1:47800/' }));
195
+ add('web lan problem', webPage({ running: true, port: 47800, address: 'http://127.0.0.1:47800/', lanProblem: 'No network.' }));
196
+ add('access', accessPage(sampleAccess()));
197
+ add('access off', accessPage({
198
+ ...sampleAccess(),
199
+ lan: { enabled: false, running: false, address: null, fingerprint: null, problem: null, network: null },
200
+ devices: [],
201
+ windows: [],
202
+ users: [],
203
+ pending: null,
204
+ }));
205
+ for (const login of ['none', 'saved', 'unreadable'])
206
+ add('vpn ' + login, vpnPage(vpn(login, login === 'saved')));
207
+ for (const token of ['none', 'saved', 'unreadable'])
208
+ for (const share of [false, true])
209
+ add('github ' + token + share, githubPage(github(token, share)));
210
+ add('settings', settingsPage(settings));
211
+ add('apps', appsPage(apps));
212
+ return pages;
213
+ }
214
+ /** Message and fix strings of the operations: lines of *.run.ts files that quote a command. */
215
+ function runSourceLines(root) {
216
+ const found = [];
217
+ const walk = (dir) => {
218
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
219
+ const full = path.join(dir, entry.name);
220
+ if (entry.isDirectory())
221
+ walk(full);
222
+ else if (entry.name.endsWith('.run.ts')) {
223
+ fs.readFileSync(full, 'utf8')
224
+ .split(/\r?\n/)
225
+ .forEach((text, i) => {
226
+ if (!/^\s*(\/\/|\*|\/\*)/.test(text))
227
+ found.push({ where: path.relative(root, full).replace(/\\/g, '/'), line: i + 1, text });
228
+ });
229
+ }
230
+ }
231
+ };
232
+ walk(root);
233
+ return found;
234
+ }
235
+ /** Lines of the run files that may name a command, because only the command line shows them. */
236
+ const CLI_ONLY_LINES = [
237
+ {
238
+ where: '*',
239
+ text: 'cli:',
240
+ why: 'the command line of an ErrorAction: only the CLI prints it (never sent to the menu or the web)',
241
+ },
242
+ {
243
+ where: 'data/data.run.ts',
244
+ text: 'storageShare',
245
+ why: '"sandbox storage" is the name of the storage drive note, not a command',
246
+ },
247
+ {
248
+ where: 'data/data.run.ts',
249
+ text: 'Name them:',
250
+ why: 'noTerminal fix: shown only when there is no terminal (the command line)',
251
+ },
252
+ {
253
+ where: 'health/health.run.ts',
254
+ text: "command: 'sandbox repair'",
255
+ why: 'the repair log records the command that ran',
256
+ },
257
+ { where: 'projects/projects.run.ts', text: 'next:', why: '`next` is printed by the command line only' },
258
+ { where: 'sandbox/sandbox.run.ts', text: 'Stop it anyway with:', why: 'noTerminal fix: command line only' },
259
+ {
260
+ where: 'storage/storage.run.ts',
261
+ text: "const fix = 'Use: sandbox storage",
262
+ why: 'noTerminal fix: command line only',
263
+ },
264
+ ];
265
+ export const uiTextSuite = {
266
+ name: 'ui-text',
267
+ title: 'menu and web text: no page, message or fix tells the user to run a command',
268
+ run: async (t) => {
269
+ const command = commandPattern();
270
+ t.section('pages');
271
+ const dir = t.dir('ui-text', 'pages');
272
+ const pages = await samplePages(dir);
273
+ const problems = pages.flatMap(({ name, page }) => visibleText(page).flatMap((text) => {
274
+ const why = textProblem(text, name, command);
275
+ return why ? [name + ': "' + text.slice(0, 140) + '" ' + why] : [];
276
+ }));
277
+ if (process.env.UI_TEXT_DUMP)
278
+ fs.writeFileSync(process.env.UI_TEXT_DUMP, problems.join('\n'));
279
+ t.equal(problems, [], 'no page of any view names a command or uses a banned word');
280
+ const commandLinks = pages.flatMap(({ name, page }) => pageActions(page).flatMap((a) => (a.kind === 'link' && a.target.kind === 'command' ? [name] : [])));
281
+ t.equal(commandLinks, [], 'no page links to a command line');
282
+ t.section('messages');
283
+ const lines = runSourceLines(path.join(process.cwd(), 'src', 'app', 'features'));
284
+ const loose = lines
285
+ .filter((l) => command.test(l.text) &&
286
+ !CLI_ONLY_LINES.some((e) => (e.where === '*' || e.where === l.where) && l.text.includes(e.text)))
287
+ .map((l) => l.where + ':' + l.line + ' ' + l.text.trim().slice(0, 140));
288
+ t.equal(loose, [], 'no operation message or fix names a command (use failWith, an action or a CLI-only field)');
289
+ t.section('menu screens');
290
+ const screens = renderScreens(t.dir('ui-text', 'screens'));
291
+ const screenProblems = screens
292
+ .split('\n')
293
+ .filter((l) => !l.startsWith('==='))
294
+ .flatMap((l) => {
295
+ const why = textProblem(l, 'menu', command);
296
+ return why ? [l.trim().slice(0, 140) + ' ' + why] : [];
297
+ });
298
+ t.equal(screenProblems, [], 'no menu screen names a command');
299
+ },
300
+ };
@@ -3,4 +3,6 @@ export * from './payloads.js';
3
3
  export { fingerprint } from './fingerprint.js';
4
4
  export { epicSettingsFile, MAX_EPIC_INI_BYTES, readEpicLogin, withEpicLogin, type EpicPayload } from './kinds/epic.js';
5
5
  export * from './kinds/steam.js';
6
+ export { PERPLEXITY_ENV, perplexityKeyFromText, perplexitySummary, type PerplexityPayload, } from './kinds/perplexity.js';
7
+ export { cometPayloadFromCookies, cometSummary, cookiesToRestore, type CometPayload } from './kinds/comet.js';
6
8
  export * from './anthropic.js';
@@ -5,4 +5,6 @@ export * from './payloads.js';
5
5
  export { fingerprint } from './fingerprint.js';
6
6
  export { epicSettingsFile, MAX_EPIC_INI_BYTES, readEpicLogin, withEpicLogin } from './kinds/epic.js';
7
7
  export * from './kinds/steam.js';
8
+ export { PERPLEXITY_ENV, perplexityKeyFromText, perplexitySummary, } from './kinds/perplexity.js';
9
+ export { cometPayloadFromCookies, cometSummary, cookiesToRestore } from './kinds/comet.js';
8
10
  export * from './anthropic.js';
@@ -0,0 +1,12 @@
1
+ import type { CometCookie, Payload } from '../payloads.js';
2
+ export type CometPayload = Payload<'comet'>;
3
+ /**
4
+ * The sign-in among the cookies Comet reports (every site's, as Storage.getCookies returns them): the allow-listed,
5
+ * persistent HttpOnly ones, in a fixed order so the same sign-in always looks the same. Null when there is none (signed
6
+ * out), or when there are more than a sign-in could need.
7
+ */
8
+ export declare function cometPayloadFromCookies(cookies: readonly unknown[], nowSeconds: number): CometPayload | null;
9
+ /** The cookies still valid now, for Storage.setCookies (an expired one would only be thrown away by Comet). */
10
+ export declare function cookiesToRestore(p: CometPayload, nowSeconds: number): CometCookie[];
11
+ /** One line for the status: how many cookies, never their values. */
12
+ export declare function cometSummary(p: CometPayload): string;
@@ -0,0 +1,64 @@
1
+ // Comet's sign-in, as the sandbox keeps it: only the perplexity.ai cookies that hold a sign-in (persistent, HttpOnly),
2
+ // read from Comet over the DevTools protocol (Storage.getCookies) and written back the same way. Pure: the sandbox
3
+ // side (guest/accounts/kinds/comet.ts) talks to Comet, the self-test checks the filter. Never passwords, other sites,
4
+ // localStorage or history: those are not cookies of an allowed domain, and nothing else is asked for.
5
+ import { cometCookieDomainAllowed, CometPayloadSchema, MAX_COMET_COOKIES } from '../payloads.js';
6
+ /** Cloudflare's bot and clearance cookies: tied to one machine and address, and renewed every few minutes. */
7
+ const MACHINE_BOUND = /^(__cf_bm|cf_clearance|_cfuvid|__cflb)$/i;
8
+ /** Expiry is kept to the hour, so a cookie re-sent with a later expiry on every page is not a new sign-in. */
9
+ const EXPIRY_STEP = 3600;
10
+ const SAME_SITE = ['Strict', 'Lax', 'None'];
11
+ /** One CDP cookie as a kept cookie, or null when it is not one to keep. */
12
+ function keptCookie(raw, nowSeconds) {
13
+ if (typeof raw !== 'object' || raw === null)
14
+ return null;
15
+ const c = raw;
16
+ if (typeof c.name !== 'string' || typeof c.value !== 'string' || typeof c.domain !== 'string')
17
+ return null;
18
+ if (!cometCookieDomainAllowed(c.domain) || MACHINE_BOUND.test(c.name))
19
+ return null;
20
+ // A sign-in is a persistent HttpOnly cookie of the site itself: no session cookies, script cookies or partitions.
21
+ if (c.httpOnly !== true || c.session === true || c.partitionKey !== undefined)
22
+ return null;
23
+ if (typeof c.expires !== 'number' || !Number.isFinite(c.expires) || c.expires <= nowSeconds)
24
+ return null;
25
+ const cookie = {
26
+ name: c.name,
27
+ value: c.value,
28
+ domain: c.domain,
29
+ path: typeof c.path === 'string' && c.path ? c.path : '/',
30
+ expires: Math.floor(c.expires / EXPIRY_STEP) * EXPIRY_STEP,
31
+ secure: c.secure === true,
32
+ httpOnly: true,
33
+ ...(typeof c.sameSite === 'string' && SAME_SITE.includes(c.sameSite)
34
+ ? { sameSite: c.sameSite }
35
+ : {}),
36
+ };
37
+ const parsed = CometPayloadSchema.shape.cookies.element.safeParse(cookie);
38
+ return parsed.success ? parsed.data : null;
39
+ }
40
+ /**
41
+ * The sign-in among the cookies Comet reports (every site's, as Storage.getCookies returns them): the allow-listed,
42
+ * persistent HttpOnly ones, in a fixed order so the same sign-in always looks the same. Null when there is none (signed
43
+ * out), or when there are more than a sign-in could need.
44
+ */
45
+ export function cometPayloadFromCookies(cookies, nowSeconds) {
46
+ const kept = cookies.flatMap((c) => {
47
+ const k = keptCookie(c, nowSeconds);
48
+ return k ? [k] : [];
49
+ });
50
+ if (!kept.length || kept.length > MAX_COMET_COOKIES)
51
+ return null;
52
+ kept.sort((a, b) => (a.domain + ' ' + a.path + ' ' + a.name < b.domain + ' ' + b.path + ' ' + b.name ? -1 : 1));
53
+ const parsed = CometPayloadSchema.safeParse({ cookies: kept });
54
+ return parsed.success ? parsed.data : null;
55
+ }
56
+ /** The cookies still valid now, for Storage.setCookies (an expired one would only be thrown away by Comet). */
57
+ export function cookiesToRestore(p, nowSeconds) {
58
+ return CometPayloadSchema.parse(p).cookies.filter((c) => c.expires === undefined || c.expires > nowSeconds);
59
+ }
60
+ /** One line for the status: how many cookies, never their values. */
61
+ export function cometSummary(p) {
62
+ const n = p.cookies.length;
63
+ return n + ' perplexity.ai sign-in cookie' + (n === 1 ? '' : 's');
64
+ }
@@ -0,0 +1,8 @@
1
+ import type { Payload } from '../payloads.js';
2
+ export type PerplexityPayload = Payload<'perplexity'>;
3
+ /** The variable the agent's sessions get, and the one the MCP servers read. */
4
+ export declare const PERPLEXITY_ENV = "PERPLEXITY_API_KEY";
5
+ /** The key in a file's text: its PERPLEXITY_API_KEY line (the last one wins, as in .env files), or the key alone. */
6
+ export declare function perplexityKeyFromText(text: string): PerplexityPayload | null;
7
+ /** pplx-... and the key's last 4 characters, for the status; never the key. */
8
+ export declare function perplexitySummary(p: PerplexityPayload): string;
@@ -0,0 +1,37 @@
1
+ // A Perplexity API key: typed, or taken from a file that holds it (a .env line PERPLEXITY_API_KEY=..., or the key
2
+ // alone). Pure text handling: only that one line is read, every other line of a .env file is ignored and never kept.
3
+ import { PerplexityPayloadSchema } from '../payloads.js';
4
+ /** The variable the agent's sessions get, and the one the MCP servers read. */
5
+ export const PERPLEXITY_ENV = 'PERPLEXITY_API_KEY';
6
+ const ENV_LINE = /^\s*(?:export\s+)?PERPLEXITY_API_KEY\s*=\s*(.*)$/;
7
+ /** More lines than a file within the import bound (64 KB) can have. */
8
+ const MAX_LINES = 100_000;
9
+ /** A value as written in a .env line: quotes taken off, a trailing comment dropped. */
10
+ function unquote(raw) {
11
+ const v = raw.trim();
12
+ const quoted = /^(["'])(.*)\1$/.exec(v);
13
+ if (quoted)
14
+ return quoted[2];
15
+ return v.replace(/\s+#.*$/, '').trim();
16
+ }
17
+ /** The key in a file's text: its PERPLEXITY_API_KEY line (the last one wins, as in .env files), or the key alone. */
18
+ export function perplexityKeyFromText(text) {
19
+ const lines = text.replace(/^\uFEFF/, '').split(/\r?\n/, MAX_LINES);
20
+ let value = null;
21
+ for (const line of lines) {
22
+ const m = ENV_LINE.exec(line);
23
+ if (m)
24
+ value = unquote(m[1]);
25
+ }
26
+ if (value === null) {
27
+ const words = lines.map((l) => l.trim()).filter((l) => l && !l.startsWith('#'));
28
+ if (words.length === 1)
29
+ value = words[0];
30
+ }
31
+ const parsed = value === null ? null : PerplexityPayloadSchema.safeParse({ apiKey: value });
32
+ return parsed?.success ? parsed.data : null;
33
+ }
34
+ /** pplx-... and the key's last 4 characters, for the status; never the key. */
35
+ export function perplexitySummary(p) {
36
+ return 'pplx-...' + p.apiKey.slice(-4);
37
+ }
@@ -1,8 +1,17 @@
1
- export declare const SERVICE_KINDS: readonly ["epic", "steam", "unity", "cfx", "keystore", "play", "expo", "appstore"];
1
+ export declare const SERVICE_KINDS: readonly ["epic", "steam", "unity", "cfx", "keystore", "play", "expo", "appstore", "perplexity", "comet"];
2
2
  export type ServiceKind = (typeof SERVICE_KINDS)[number];
3
- /** The toolchains a sign-in serves (it is handed over only when one of them is on for the boot). */
4
- export type UsedBy = 'unreal' | 'steam' | 'unity' | 'fivem' | 'android' | 'flutter' | 'reactNative' | 'godot' | 'dotnet';
5
- export declare const USED_BY: readonly UsedBy[];
3
+ /** The toolchains a sign-in can serve (each is a key of `toolchains`). */
4
+ export type ToolchainUser = 'unreal' | 'steam' | 'unity' | 'fivem' | 'android' | 'flutter' | 'reactNative' | 'godot' | 'dotnet';
5
+ /** The apps a sign-in can serve: on when one of APP_USERS' apps is in apps.install. */
6
+ export type AppUser = 'perplexity' | 'comet';
7
+ /** What a sign-in serves (it is handed over only when one of them is on for the boot). */
8
+ export type UsedBy = ToolchainUser | AppUser;
9
+ /** The toolchains a sign-in can serve. */
10
+ export declare const USED_BY: readonly ToolchainUser[];
11
+ /** The apps (apps.install ids) that turn each app user on. */
12
+ export declare const APP_USERS: {
13
+ readonly [U in AppUser]: readonly string[];
14
+ };
6
15
  /** Where a saved sign-in came from: this PC's program, a file you gave, values you typed, or a sandbox. */
7
16
  export type AccountSource = 'pc' | 'file' | 'typed' | 'sandbox';
8
17
  export declare const ACCOUNT_SOURCES: readonly AccountSource[];
@@ -13,6 +22,8 @@ export interface ServiceField {
13
22
  readonly secret: boolean;
14
23
  readonly optional?: boolean;
15
24
  readonly pattern?: RegExp;
25
+ /** Asked only by `login` (typed) or only by `import --from` (file); both when not set. */
26
+ readonly only?: 'typed' | 'file';
16
27
  }
17
28
  export interface ServiceSpec<K extends ServiceKind = ServiceKind> {
18
29
  readonly kind: K;
@@ -26,16 +37,24 @@ export interface ServiceSpec<K extends ServiceKind = ServiceKind> {
26
37
  readonly returns: boolean;
27
38
  /** The largest payload (its JSON) accepted from a file or a sandbox. */
28
39
  readonly maxBytes: number;
40
+ /** The largest file `import --from` reads, when it holds more than the payload (a .env file); default maxBytes. */
41
+ readonly maxFileBytes?: number;
29
42
  /** Shown on every surface. */
30
43
  readonly warnings: readonly string[];
31
44
  /** What `login` asks for, or what `import --from` needs besides the file. */
32
45
  readonly fields: readonly ServiceField[];
33
46
  }
47
+ /** A Perplexity API key: pplx- and its body. */
48
+ export declare const PERPLEXITY_KEY_PATTERN: RegExp;
34
49
  export declare const SERVICE_SPECS: {
35
50
  readonly [K in ServiceKind]: ServiceSpec<K>;
36
51
  };
37
52
  /** Where a sandbox leaves a renewed sign-in, under the data folder: state\accounts\<kind>.json. */
38
53
  export declare const HANDOFF_PARTS: readonly ["state", "accounts"];
39
54
  export declare function isServiceKind(value: string): value is ServiceKind;
40
- /** Whether a sign-in is wanted when these toolchains are on. */
55
+ /** What a sign-in serves, for a sentence: "unreal", "reactNative or flutter", "the Comet app". */
56
+ export declare function usedByText(usedBy: readonly string[]): string;
57
+ /** The app users that the chosen apps (apps.install) turn on. */
58
+ export declare function appUsersOn(install: readonly string[]): AppUser[];
59
+ /** Whether a sign-in is wanted when these toolchains (and apps) are on. */
41
60
  export declare function serviceWanted(kind: ServiceKind, on: ReadonlySet<UsedBy>): boolean;
@@ -2,7 +2,19 @@
2
2
  // come from, and the rules for handing it over (one sandbox at a time, handed back when renewed). Pure, web-safe: no
3
3
  // Node built-ins. The payload shapes are in payloads.ts, the host side in host/accounts, the sandbox side in
4
4
  // guest/accounts.
5
- export const SERVICE_KINDS = ['epic', 'steam', 'unity', 'cfx', 'keystore', 'play', 'expo', 'appstore'];
5
+ export const SERVICE_KINDS = [
6
+ 'epic',
7
+ 'steam',
8
+ 'unity',
9
+ 'cfx',
10
+ 'keystore',
11
+ 'play',
12
+ 'expo',
13
+ 'appstore',
14
+ 'perplexity',
15
+ 'comet',
16
+ ];
17
+ /** The toolchains a sign-in can serve. */
6
18
  export const USED_BY = [
7
19
  'unreal',
8
20
  'steam',
@@ -14,9 +26,16 @@ export const USED_BY = [
14
26
  'godot',
15
27
  'dotnet',
16
28
  ];
29
+ /** The apps (apps.install ids) that turn each app user on. */
30
+ export const APP_USERS = {
31
+ perplexity: ['perplexity-mcp'],
32
+ comet: ['comet'],
33
+ };
17
34
  export const ACCOUNT_SOURCES = ['pc', 'file', 'typed', 'sandbox'];
18
35
  const AGENT_CAN_USE = 'The agent in the sandbox can use this sign-in while it is there.';
19
36
  const ONE_PLACE = 'It works in one sandbox at a time: a second sandbox starts without it while the first one runs.';
37
+ /** A Perplexity API key: pplx- and its body. */
38
+ export const PERPLEXITY_KEY_PATTERN = /^pplx-[A-Za-z0-9_-]{8,200}$/;
20
39
  export const SERVICE_SPECS = {
21
40
  epic: {
22
41
  kind: 'epic',
@@ -70,19 +89,28 @@ export const SERVICE_SPECS = {
70
89
  kind: 'cfx',
71
90
  label: 'Cfx.re (FiveM / RedM server key)',
72
91
  usedBy: ['fivem'],
73
- sources: ['typed'],
92
+ sources: ['typed', 'file'],
74
93
  singleHolder: false,
75
94
  returns: false,
95
+ // `import cfx --from` reads a whole server.cfg (they often run to tens of KB); the payload itself is tiny.
76
96
  maxBytes: 4 * 1024,
97
+ maxFileBytes: 256 * 1024,
77
98
  warnings: [AGENT_CAN_USE + ' Anyone with the key can run a server under your Cfx.re account.'],
78
99
  fields: [
79
- { name: 'licenseKey', label: 'Server licence key (cfxk_...)', secret: true, pattern: /^cfxk_[A-Za-z0-9_]+$/ },
100
+ {
101
+ name: 'licenseKey',
102
+ label: 'Server licence key (cfxk_...)',
103
+ secret: true,
104
+ pattern: /^cfxk_[A-Za-z0-9_]+$/,
105
+ only: 'typed',
106
+ },
80
107
  {
81
108
  name: 'steamWebApiKey',
82
109
  label: 'Steam Web API key (optional)',
83
110
  secret: true,
84
111
  optional: true,
85
112
  pattern: /^[A-F0-9]{32}$/,
113
+ only: 'typed',
86
114
  },
87
115
  ],
88
116
  },
@@ -137,6 +165,47 @@ export const SERVICE_SPECS = {
137
165
  { name: 'keyId', label: 'Key ID', secret: false, pattern: /^[A-Z0-9]{10}$/ },
138
166
  ],
139
167
  },
168
+ perplexity: {
169
+ kind: 'perplexity',
170
+ label: 'Perplexity API key',
171
+ usedBy: ['perplexity'],
172
+ sources: ['pc', 'file', 'typed'],
173
+ singleHolder: false,
174
+ returns: false,
175
+ maxBytes: 1024,
176
+ maxFileBytes: 64 * 1024,
177
+ warnings: [
178
+ AGENT_CAN_USE + ' It can read the key and spend your API credit: use a separate API key with a spending limit.',
179
+ 'Perplexity Pro does not include API use; the API key is billed separately.',
180
+ 'Only the variable PERPLEXITY_API_KEY of the agent gets it: nothing in the sandbox writes it to a file.',
181
+ ],
182
+ fields: [
183
+ {
184
+ name: 'apiKey',
185
+ label: 'Perplexity API key (pplx-...)',
186
+ secret: true,
187
+ pattern: PERPLEXITY_KEY_PATTERN,
188
+ only: 'typed',
189
+ },
190
+ ],
191
+ },
192
+ comet: {
193
+ kind: 'comet',
194
+ label: 'Comet (Perplexity browser)',
195
+ usedBy: ['comet'],
196
+ sources: ['sandbox'],
197
+ singleHolder: false,
198
+ returns: true,
199
+ maxBytes: 256 * 1024,
200
+ warnings: [
201
+ AGENT_CAN_USE +
202
+ ' It can act as you in Comet and spend your Perplexity Computer credits; agentic browsing is open to prompt injection from the pages it reads.',
203
+ "Your Comet sign-in can't be copied from this PC: Windows locks it to your PC account. Sign in once inside a sandbox and it is kept for the next ones.",
204
+ 'Only the perplexity.ai sign-in cookies are kept: never saved passwords, other sites, localStorage or history. Never import your main browser profile.',
205
+ 'Several sandboxes can use it at once: the sign-in the last one hands back is kept.',
206
+ ],
207
+ fields: [],
208
+ },
140
209
  };
141
210
  /** Where a sandbox leaves a renewed sign-in, under the data folder: state\accounts\<kind>.json. */
142
211
  export const HANDOFF_PARTS = ['state', 'accounts'];
@@ -145,7 +214,20 @@ const ANDROID_USERS = ['flutter', 'reactNative', 'godot', 'unity'];
145
214
  export function isServiceKind(value) {
146
215
  return SERVICE_KINDS.includes(value);
147
216
  }
148
- /** Whether a sign-in is wanted when these toolchains are on. */
217
+ /** How the apps that use a sign-in are named in sentences ("for sandboxes that use ..."). */
218
+ const APP_USER_TEXT = {
219
+ perplexity: 'the Perplexity MCP app',
220
+ comet: 'the Comet app',
221
+ };
222
+ /** What a sign-in serves, for a sentence: "unreal", "reactNative or flutter", "the Comet app". */
223
+ export function usedByText(usedBy) {
224
+ return usedBy.map((u) => (u in APP_USER_TEXT ? APP_USER_TEXT[u] : u)).join(' or ');
225
+ }
226
+ /** The app users that the chosen apps (apps.install) turn on. */
227
+ export function appUsersOn(install) {
228
+ return Object.keys(APP_USERS).filter((u) => APP_USERS[u].some((id) => install.includes(id)));
229
+ }
230
+ /** Whether a sign-in is wanted when these toolchains (and apps) are on. */
149
231
  export function serviceWanted(kind, on) {
150
232
  const spec = SERVICE_SPECS[kind];
151
233
  if (spec.usedBy.some((u) => on.has(u)))