windows-development-sandbox 1.17.3 → 1.18.2

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 (315) hide show
  1. package/README.md +1 -1
  2. package/config.example.json +97 -4
  3. package/dist/app/features/accounts/accounts.def.d.ts +116 -2
  4. package/dist/app/features/accounts/accounts.def.js +80 -12
  5. package/dist/app/features/accounts/accounts.run.d.ts +29 -0
  6. package/dist/app/features/accounts/accounts.run.js +36 -6
  7. package/dist/app/features/accounts/index.d.ts +2 -0
  8. package/dist/app/features/accounts/index.js +4 -1
  9. package/dist/app/features/accounts/services.def.d.ts +111 -0
  10. package/dist/app/features/accounts/services.def.js +187 -0
  11. package/dist/app/features/accounts/services.run.d.ts +65 -0
  12. package/dist/app/features/accounts/services.run.js +212 -0
  13. package/dist/app/features/defs.d.ts +112 -1
  14. package/dist/app/features/defs.js +5 -0
  15. package/dist/app/features/settings/categories.def.js +21 -4
  16. package/dist/app/registry.js +5 -1
  17. package/dist/bin/agent-sandbox.js +2 -2
  18. package/dist/bin/{chunk-QLGGAG3D.js → chunk-IIHL7W6B.js} +51 -16
  19. package/dist/bin/{chunk-XYUVIVJT.js → chunk-M4OM632I.js} +1 -1
  20. package/dist/bin/{chunk-REE3EMHD.js → chunk-MQIA7GNB.js} +3813 -1702
  21. package/dist/bin/claude-host.js +2 -2
  22. package/dist/bin/claude-sbx.js +2 -2
  23. package/dist/bin/codex-sbx.js +2 -2
  24. package/dist/bin/gemini-sbx.js +2 -2
  25. package/dist/bin/sandbox.js +2 -2
  26. package/dist/bin/sbx-hide.js +2 -2
  27. package/dist/bin/sbx-show.js +2 -2
  28. package/dist/cli/commands/launch/launch.js +30 -1
  29. package/dist/cli/manifest.d.ts +93 -65
  30. package/dist/cli/manifest.js +23 -5
  31. package/dist/cli/menu/pages/accounts-page.js +31 -16
  32. package/dist/cli/services/accounts-service.d.ts +14 -4
  33. package/dist/cli/services/accounts-service.js +19 -6
  34. package/dist/cli/services/data-collect.js +26 -1
  35. package/dist/core/config/defaults.js +47 -4
  36. package/dist/core/detect/toolchains.d.ts +15 -1
  37. package/dist/core/detect/toolchains.js +45 -12
  38. package/dist/core/local-config/overrides.js +6 -0
  39. package/dist/core/schema/accounts.d.ts +27 -0
  40. package/dist/core/schema/accounts.js +32 -0
  41. package/dist/core/schema/config.d.ts +239 -0
  42. package/dist/core/schema/config.js +6 -0
  43. package/dist/core/schema/pc-installs.d.ts +7 -0
  44. package/dist/core/schema/pc-installs.js +17 -0
  45. package/dist/core/schema/toolchains.d.ts +61 -0
  46. package/dist/core/schema/toolchains.js +106 -0
  47. package/dist/core/types.d.ts +18 -0
  48. package/dist/guest/accounts/adapter.d.ts +25 -0
  49. package/dist/guest/accounts/adapter.js +21 -0
  50. package/dist/guest/accounts/adapters.d.ts +5 -0
  51. package/dist/guest/accounts/adapters.js +18 -0
  52. package/dist/guest/accounts/apply.d.ts +41 -0
  53. package/dist/guest/accounts/apply.js +47 -0
  54. package/dist/guest/accounts/kinds/appstore.d.ts +2 -0
  55. package/dist/guest/accounts/kinds/appstore.js +7 -0
  56. package/dist/guest/accounts/kinds/cfx.d.ts +2 -0
  57. package/dist/guest/accounts/kinds/cfx.js +7 -0
  58. package/dist/guest/accounts/kinds/epic.d.ts +2 -0
  59. package/dist/guest/accounts/kinds/epic.js +26 -0
  60. package/dist/guest/accounts/kinds/expo.d.ts +2 -0
  61. package/dist/guest/accounts/kinds/expo.js +7 -0
  62. package/dist/guest/accounts/kinds/keystore.d.ts +2 -0
  63. package/dist/guest/accounts/kinds/keystore.js +7 -0
  64. package/dist/guest/accounts/kinds/play.d.ts +2 -0
  65. package/dist/guest/accounts/kinds/play.js +7 -0
  66. package/dist/guest/accounts/kinds/steam.d.ts +4 -0
  67. package/dist/guest/accounts/kinds/steam.js +17 -0
  68. package/dist/guest/accounts/kinds/unity.d.ts +2 -0
  69. package/dist/guest/accounts/kinds/unity.js +7 -0
  70. package/dist/guest/accounts/watch.d.ts +28 -0
  71. package/dist/guest/accounts/watch.js +78 -0
  72. package/dist/guest/apps/resolvers-gamedev.d.ts +2 -0
  73. package/dist/guest/apps/resolvers-gamedev.js +1 -0
  74. package/dist/guest/apps/resolvers-mobile.d.ts +2 -0
  75. package/dist/guest/apps/resolvers-mobile.js +1 -0
  76. package/dist/guest/apps/resolvers.js +4 -0
  77. package/dist/guest/boot/boot.d.ts +5 -0
  78. package/dist/guest/boot/boot.js +35 -1
  79. package/dist/guest/daemon/accounts.d.ts +3 -0
  80. package/dist/guest/daemon/accounts.js +25 -0
  81. package/dist/guest/daemon/daemon.js +2 -0
  82. package/dist/guest/daemon/idle-shutdown.js +3 -1
  83. package/dist/guest/daemon/sessions.js +2 -0
  84. package/dist/guest/devtools/android.d.ts +2 -0
  85. package/dist/guest/devtools/android.js +7 -0
  86. package/dist/guest/devtools/devtool.d.ts +9 -0
  87. package/dist/guest/devtools/devtool.js +5 -0
  88. package/dist/guest/devtools/fivem.d.ts +2 -0
  89. package/dist/guest/devtools/fivem.js +7 -0
  90. package/dist/guest/devtools/registry.d.ts +6 -0
  91. package/dist/guest/devtools/registry.js +12 -0
  92. package/dist/guest/devtools/steam.d.ts +2 -0
  93. package/dist/guest/devtools/steam.js +228 -0
  94. package/dist/guest/devtools/unity.d.ts +2 -0
  95. package/dist/guest/devtools/unity.js +7 -0
  96. package/dist/guest/entries/daemon.js +2 -0
  97. package/dist/guest/entries/tool.d.ts +1 -0
  98. package/dist/guest/entries/tool.js +17 -0
  99. package/dist/guest/toolchains/engines/godot.d.ts +4 -0
  100. package/dist/guest/toolchains/engines/godot.js +4 -0
  101. package/dist/guest/toolchains/engines/unity-release.d.ts +15 -0
  102. package/dist/guest/toolchains/engines/unity-release.js +6 -0
  103. package/dist/guest/toolchains/engines/unity.d.ts +4 -0
  104. package/dist/guest/toolchains/engines/unity.js +4 -0
  105. package/dist/guest/toolchains/engines/unreal-engines.d.ts +4 -0
  106. package/dist/guest/toolchains/engines/unreal-engines.js +4 -0
  107. package/dist/guest/toolchains/fivem/artifacts.d.ts +6 -0
  108. package/dist/guest/toolchains/fivem/artifacts.js +7 -0
  109. package/dist/guest/toolchains/fivem/fivem.d.ts +4 -0
  110. package/dist/guest/toolchains/fivem/fivem.js +4 -0
  111. package/dist/guest/toolchains/installers.d.ts +12 -0
  112. package/dist/guest/toolchains/installers.js +39 -0
  113. package/dist/guest/toolchains/languages/java.js +1 -1
  114. package/dist/guest/toolchains/languages/kotlin.d.ts +3 -0
  115. package/dist/guest/toolchains/languages/kotlin.js +6 -0
  116. package/dist/guest/toolchains/mobile/android-repo.d.ts +11 -0
  117. package/dist/guest/toolchains/mobile/android-repo.js +10 -0
  118. package/dist/guest/toolchains/mobile/android.d.ts +17 -0
  119. package/dist/guest/toolchains/mobile/android.js +16 -0
  120. package/dist/guest/toolchains/mobile/dart.d.ts +4 -0
  121. package/dist/guest/toolchains/mobile/dart.js +4 -0
  122. package/dist/guest/toolchains/mobile/flutter.d.ts +4 -0
  123. package/dist/guest/toolchains/mobile/flutter.js +4 -0
  124. package/dist/guest/toolchains/mobile/react-native.d.ts +4 -0
  125. package/dist/guest/toolchains/mobile/react-native.js +4 -0
  126. package/dist/guest/toolchains/native/unreal.d.ts +3 -7
  127. package/dist/guest/toolchains/native/unreal.js +16 -167
  128. package/dist/guest/toolchains/native/visual-studio.d.ts +15 -0
  129. package/dist/guest/toolchains/native/visual-studio.js +159 -0
  130. package/dist/guest/toolchains/native/vs-request.d.ts +19 -0
  131. package/dist/guest/toolchains/native/vs-request.js +46 -0
  132. package/dist/guest/toolchains/not-yet.d.ts +2 -0
  133. package/dist/guest/toolchains/not-yet.js +6 -0
  134. package/dist/guest/toolchains/persisted.d.ts +48 -0
  135. package/dist/guest/toolchains/persisted.js +159 -0
  136. package/dist/guest/toolchains/plan.js +80 -4
  137. package/dist/guest/toolchains/steam/apps.d.ts +27 -0
  138. package/dist/guest/toolchains/steam/apps.js +112 -0
  139. package/dist/guest/toolchains/steam/client.d.ts +5 -0
  140. package/dist/guest/toolchains/steam/client.js +67 -0
  141. package/dist/guest/toolchains/steam/commands.d.ts +58 -0
  142. package/dist/guest/toolchains/steam/commands.js +155 -0
  143. package/dist/guest/toolchains/steam/steam.d.ts +4 -0
  144. package/dist/guest/toolchains/steam/steam.js +61 -0
  145. package/dist/guest/toolchains/steam/steamcmd-install.d.ts +29 -0
  146. package/dist/guest/toolchains/steam/steamcmd-install.js +177 -0
  147. package/dist/guest/toolchains/steam/steamcmd.d.ts +6 -0
  148. package/dist/guest/toolchains/steam/steamcmd.js +23 -0
  149. package/dist/guest/toolchains/step.d.ts +10 -0
  150. package/dist/host/accounts/deliver.d.ts +26 -0
  151. package/dist/host/accounts/deliver.js +80 -0
  152. package/dist/host/accounts/handoff.d.ts +17 -0
  153. package/dist/host/accounts/handoff.js +110 -0
  154. package/dist/host/accounts/index.d.ts +6 -0
  155. package/dist/host/accounts/index.js +8 -0
  156. package/dist/host/accounts/kinds/appstore.d.ts +2 -0
  157. package/dist/host/accounts/kinds/appstore.js +10 -0
  158. package/dist/host/accounts/kinds/cfx.d.ts +2 -0
  159. package/dist/host/accounts/kinds/cfx.js +7 -0
  160. package/dist/host/accounts/kinds/epic.d.ts +2 -0
  161. package/dist/host/accounts/kinds/epic.js +15 -0
  162. package/dist/host/accounts/kinds/expo.d.ts +2 -0
  163. package/dist/host/accounts/kinds/expo.js +7 -0
  164. package/dist/host/accounts/kinds/keystore.d.ts +2 -0
  165. package/dist/host/accounts/kinds/keystore.js +7 -0
  166. package/dist/host/accounts/kinds/play.d.ts +2 -0
  167. package/dist/host/accounts/kinds/play.js +7 -0
  168. package/dist/host/accounts/kinds/steam.d.ts +6 -0
  169. package/dist/host/accounts/kinds/steam.js +31 -0
  170. package/dist/host/accounts/kinds/unity.d.ts +2 -0
  171. package/dist/host/accounts/kinds/unity.js +13 -0
  172. package/dist/host/accounts/leases.d.ts +40 -0
  173. package/dist/host/accounts/leases.js +62 -0
  174. package/dist/host/accounts/source.d.ts +21 -0
  175. package/dist/host/accounts/source.js +15 -0
  176. package/dist/host/accounts/sources.d.ts +5 -0
  177. package/dist/host/accounts/sources.js +18 -0
  178. package/dist/host/accounts/store.d.ts +23 -0
  179. package/dist/host/accounts/store.js +74 -0
  180. package/dist/host/audit/log.d.ts +1 -1
  181. package/dist/host/launch/generate.d.ts +2 -0
  182. package/dist/host/launch/generate.js +16 -1
  183. package/dist/host/launch/hints/android.d.ts +3 -0
  184. package/dist/host/launch/hints/android.js +3 -0
  185. package/dist/host/launch/hints/engines.d.ts +3 -0
  186. package/dist/host/launch/hints/engines.js +3 -0
  187. package/dist/host/launch/hints/fivem.d.ts +3 -0
  188. package/dist/host/launch/hints/fivem.js +3 -0
  189. package/dist/host/launch/hints/hint.d.ts +2 -0
  190. package/dist/host/launch/hints/hint.js +1 -0
  191. package/dist/host/launch/hints/index.d.ts +5 -0
  192. package/dist/host/launch/hints/index.js +29 -0
  193. package/dist/host/launch/hints/mobile.d.ts +3 -0
  194. package/dist/host/launch/hints/mobile.js +3 -0
  195. package/dist/host/launch/hints/steam.d.ts +7 -0
  196. package/dist/host/launch/hints/steam.js +59 -0
  197. package/dist/host/launch/index.d.ts +2 -2
  198. package/dist/host/launch/index.js +2 -2
  199. package/dist/host/launch/mappings.js +2 -0
  200. package/dist/host/launch/session-file.js +2 -0
  201. package/dist/host/launch/stage.js +10 -1
  202. package/dist/host/launch/toolchains.d.ts +7 -0
  203. package/dist/host/launch/toolchains.js +11 -0
  204. package/dist/host/pc-installs/discover.d.ts +19 -0
  205. package/dist/host/pc-installs/discover.js +59 -0
  206. package/dist/host/pc-installs/epic.d.ts +2 -0
  207. package/dist/host/pc-installs/epic.js +7 -0
  208. package/dist/host/pc-installs/index.d.ts +3 -0
  209. package/dist/host/pc-installs/index.js +2 -0
  210. package/dist/host/pc-installs/source.d.ts +9 -0
  211. package/dist/host/pc-installs/source.js +1 -0
  212. package/dist/host/pc-installs/steam.d.ts +16 -0
  213. package/dist/host/pc-installs/steam.js +143 -0
  214. package/dist/host/pc-installs/unity.d.ts +2 -0
  215. package/dist/host/pc-installs/unity.js +7 -0
  216. package/dist/host/pc-installs/unreal.d.ts +2 -0
  217. package/dist/host/pc-installs/unreal.js +7 -0
  218. package/dist/host/profile/accounts.d.ts +21 -6
  219. package/dist/host/profile/accounts.js +108 -28
  220. package/dist/host/profile/claude-login.d.ts +32 -2
  221. package/dist/host/profile/claude-login.js +101 -8
  222. package/dist/host/profile/index.d.ts +1 -1
  223. package/dist/host/profile/index.js +1 -1
  224. package/dist/host/secrets/launch-secrets.js +1 -0
  225. package/dist/runtime/agent.js +321 -9
  226. package/dist/runtime/daemon.js +1987 -604
  227. package/dist/runtime/db.js +334 -11
  228. package/dist/runtime/main.js +2519 -723
  229. package/dist/runtime/netproxy.js +321 -9
  230. package/dist/runtime/tool.js +28549 -0
  231. package/dist/runtime/view.js +321 -9
  232. package/dist/selftest/accounts.test.js +172 -1
  233. package/dist/selftest/agent-paths.test.js +1 -0
  234. package/dist/selftest/android.test.d.ts +2 -0
  235. package/dist/selftest/android.test.js +7 -0
  236. package/dist/selftest/app.test.js +1 -1
  237. package/dist/selftest/engines.test.d.ts +2 -0
  238. package/dist/selftest/engines.test.js +7 -0
  239. package/dist/selftest/fivem.test.d.ts +2 -0
  240. package/dist/selftest/fivem.test.js +7 -0
  241. package/dist/selftest/generate.test.js +1 -1
  242. package/dist/selftest/index.js +18 -0
  243. package/dist/selftest/menu-screens.js +1 -0
  244. package/dist/selftest/menu.test.js +2 -1
  245. package/dist/selftest/mobile.test.d.ts +2 -0
  246. package/dist/selftest/mobile.test.js +7 -0
  247. package/dist/selftest/pc-installs.test.d.ts +2 -0
  248. package/dist/selftest/pc-installs.test.js +111 -0
  249. package/dist/selftest/persisted-installs.test.d.ts +2 -0
  250. package/dist/selftest/persisted-installs.test.js +107 -0
  251. package/dist/selftest/projects.test.js +6 -4
  252. package/dist/selftest/service-accounts.test.d.ts +2 -0
  253. package/dist/selftest/service-accounts.test.js +320 -0
  254. package/dist/selftest/steam.test.d.ts +2 -0
  255. package/dist/selftest/steam.test.js +316 -0
  256. package/dist/selftest/storage-helper.test.js +31 -8
  257. package/dist/selftest/support/engine-golden.js +3 -0
  258. package/dist/selftest/support/fixtures.d.ts +87 -0
  259. package/dist/selftest/support/fixtures.js +4 -3
  260. package/dist/selftest/support/pc-memory.d.ts +2 -0
  261. package/dist/selftest/support/pc-memory.js +17 -0
  262. package/dist/selftest/support/web-preview.js +20 -1
  263. package/dist/selftest/unity.test.d.ts +2 -0
  264. package/dist/selftest/unity.test.js +7 -0
  265. package/dist/shared/accounts/fingerprint.d.ts +1 -0
  266. package/dist/shared/accounts/fingerprint.js +20 -0
  267. package/dist/shared/accounts/index.d.ts +5 -0
  268. package/dist/shared/accounts/index.js +7 -0
  269. package/dist/shared/accounts/kinds/epic.d.ts +10 -0
  270. package/dist/shared/accounts/kinds/epic.js +56 -0
  271. package/dist/shared/accounts/kinds/steam.d.ts +28 -0
  272. package/dist/shared/accounts/kinds/steam.js +99 -0
  273. package/dist/shared/accounts/kinds.d.ts +41 -0
  274. package/dist/shared/accounts/kinds.js +154 -0
  275. package/dist/shared/accounts/payloads.d.ts +151 -0
  276. package/dist/shared/accounts/payloads.js +128 -0
  277. package/dist/shared/apps/catalogue-gamedev.d.ts +2 -0
  278. package/dist/shared/apps/catalogue-gamedev.js +1 -0
  279. package/dist/shared/apps/catalogue-mobile.d.ts +2 -0
  280. package/dist/shared/apps/catalogue-mobile.js +1 -0
  281. package/dist/shared/apps/catalogue-modding.d.ts +2 -0
  282. package/dist/shared/apps/catalogue-modding.js +1 -0
  283. package/dist/shared/apps/catalogue.d.ts +1 -0
  284. package/dist/shared/apps/catalogue.js +6 -1
  285. package/dist/shared/channel/schema.d.ts +183 -0
  286. package/dist/shared/channel/secrets.d.ts +42 -0
  287. package/dist/shared/channel/secrets.js +3 -0
  288. package/dist/shared/download/downloader.d.ts +7 -0
  289. package/dist/shared/download/downloader.js +10 -3
  290. package/dist/shared/installs.d.ts +19 -0
  291. package/dist/shared/installs.js +13 -0
  292. package/dist/shared/io/bounded.d.ts +6 -0
  293. package/dist/shared/io/bounded.js +38 -0
  294. package/dist/shared/io/index.d.ts +2 -0
  295. package/dist/shared/io/index.js +2 -0
  296. package/dist/shared/io/text.d.ts +9 -0
  297. package/dist/shared/io/text.js +18 -0
  298. package/dist/shared/paths/guest-paths.js +1 -1
  299. package/dist/shared/paths/index.d.ts +1 -0
  300. package/dist/shared/paths/index.js +1 -0
  301. package/dist/shared/paths/pc-installs.d.ts +20 -0
  302. package/dist/shared/paths/pc-installs.js +19 -0
  303. package/dist/shared/vdf.d.ts +25 -0
  304. package/dist/shared/vdf.js +151 -0
  305. package/dist/sync/sync-cycle.js +9 -0
  306. package/dist/web/index.html +1 -1
  307. package/dist/web/{main-JTH3FT4O.js → main-CWIF4VQI.js} +63 -61
  308. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  309. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  310. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  311. package/package.json +1 -1
  312. package/sandbox/scripts/add-firewall-port.ps1 +13 -0
  313. package/sandbox/templates/fivem/README.txt +2 -0
  314. package/schemas/config.schema.json +485 -0
  315. package/schemas/sandbox-config.schema.json +416 -0
@@ -0,0 +1,111 @@
1
+ import { z } from 'zod';
2
+ import { type Page, type Section } from '../../kernel/index.js';
3
+ /** The kinds, as the help says them (the self-test checks this against shared/accounts SERVICE_KINDS). */
4
+ export declare const SERVICE_NAMES_TEXT = "epic, steam, unity, cfx, keystore, play, expo or appstore";
5
+ export declare const ServiceRowSchema: z.ZodObject<{
6
+ kind: z.ZodString;
7
+ label: z.ZodString;
8
+ share: z.ZodBoolean;
9
+ state: z.ZodEnum<{
10
+ none: "none";
11
+ saved: "saved";
12
+ unreadable: "unreadable";
13
+ }>;
14
+ source: z.ZodNullable<z.ZodString>;
15
+ savedAt: z.ZodNullable<z.ZodString>;
16
+ summary: z.ZodNullable<z.ZodString>;
17
+ heldBy: z.ZodNullable<z.ZodString>;
18
+ usedBy: z.ZodArray<z.ZodString>;
19
+ warnings: z.ZodArray<z.ZodString>;
20
+ canImport: z.ZodBoolean;
21
+ canLogin: z.ZodBoolean;
22
+ needsFile: z.ZodBoolean;
23
+ }, z.core.$strip>;
24
+ export type ServiceRow = z.output<typeof ServiceRowSchema>;
25
+ export declare const ServicesListSchema: z.ZodObject<{
26
+ services: z.ZodArray<z.ZodObject<{
27
+ kind: z.ZodString;
28
+ label: z.ZodString;
29
+ share: z.ZodBoolean;
30
+ state: z.ZodEnum<{
31
+ none: "none";
32
+ saved: "saved";
33
+ unreadable: "unreadable";
34
+ }>;
35
+ source: z.ZodNullable<z.ZodString>;
36
+ savedAt: z.ZodNullable<z.ZodString>;
37
+ summary: z.ZodNullable<z.ZodString>;
38
+ heldBy: z.ZodNullable<z.ZodString>;
39
+ usedBy: z.ZodArray<z.ZodString>;
40
+ warnings: z.ZodArray<z.ZodString>;
41
+ canImport: z.ZodBoolean;
42
+ canLogin: z.ZodBoolean;
43
+ needsFile: z.ZodBoolean;
44
+ }, z.core.$strip>>;
45
+ }, z.core.$strip>;
46
+ export type ServicesList = z.output<typeof ServicesListSchema>;
47
+ /** `sandbox account status`: one line per service. */
48
+ export declare function servicesText(o: ServicesList): string;
49
+ export declare const listServices: import("../../kernel/define.js").QueryDef<z.ZodObject<{
50
+ name: z.ZodOptional<z.ZodString>;
51
+ }, z.core.$strip>, z.ZodObject<{
52
+ services: z.ZodArray<z.ZodObject<{
53
+ kind: z.ZodString;
54
+ label: z.ZodString;
55
+ share: z.ZodBoolean;
56
+ state: z.ZodEnum<{
57
+ none: "none";
58
+ saved: "saved";
59
+ unreadable: "unreadable";
60
+ }>;
61
+ source: z.ZodNullable<z.ZodString>;
62
+ savedAt: z.ZodNullable<z.ZodString>;
63
+ summary: z.ZodNullable<z.ZodString>;
64
+ heldBy: z.ZodNullable<z.ZodString>;
65
+ usedBy: z.ZodArray<z.ZodString>;
66
+ warnings: z.ZodArray<z.ZodString>;
67
+ canImport: z.ZodBoolean;
68
+ canLogin: z.ZodBoolean;
69
+ needsFile: z.ZodBoolean;
70
+ }, z.core.$strip>>;
71
+ }, z.core.$strip>, "accounts.services">;
72
+ export declare const serviceImport: import("../../kernel/define.js").OperationDef<z.ZodObject<{
73
+ name: z.ZodOptional<z.ZodString>;
74
+ from: z.ZodOptional<z.ZodString>;
75
+ }, z.core.$strip>, z.ZodObject<{
76
+ message: z.ZodString;
77
+ }, z.core.$strip>, "accounts.service.import">;
78
+ export declare const serviceLogin: import("../../kernel/define.js").OperationDef<z.ZodObject<{
79
+ name: z.ZodOptional<z.ZodString>;
80
+ }, z.core.$strip>, z.ZodObject<{
81
+ message: z.ZodString;
82
+ }, z.core.$strip>, "accounts.service.login">;
83
+ export declare const serviceLogout: import("../../kernel/define.js").OperationDef<z.ZodObject<{
84
+ name: z.ZodOptional<z.ZodString>;
85
+ }, z.core.$strip>, z.ZodObject<{
86
+ deleted: z.ZodBoolean;
87
+ message: z.ZodString;
88
+ }, z.core.$strip>, "accounts.service.logout">;
89
+ export declare const SERVICE_OPERATIONS: readonly [import("../../kernel/define.js").OperationDef<z.ZodObject<{
90
+ name: z.ZodOptional<z.ZodString>;
91
+ from: z.ZodOptional<z.ZodString>;
92
+ }, z.core.$strip>, z.ZodObject<{
93
+ message: z.ZodString;
94
+ }, z.core.$strip>, "accounts.service.import">, import("../../kernel/define.js").OperationDef<z.ZodObject<{
95
+ name: z.ZodOptional<z.ZodString>;
96
+ }, z.core.$strip>, z.ZodObject<{
97
+ message: z.ZodString;
98
+ }, z.core.$strip>, "accounts.service.login">, import("../../kernel/define.js").OperationDef<z.ZodObject<{
99
+ name: z.ZodOptional<z.ZodString>;
100
+ }, z.core.$strip>, z.ZodObject<{
101
+ deleted: z.ZodBoolean;
102
+ message: z.ZodString;
103
+ }, z.core.$strip>, "accounts.service.logout">];
104
+ /** The category id of a service in the menu and its section id on the web. */
105
+ export declare function serviceCategory(kind: string): string;
106
+ /** One service's section: its facts, its warnings and its actions. */
107
+ export declare function serviceSection(s: ServiceRow): Section;
108
+ /** The sections of every service (appended to the accounts page on the web). */
109
+ export declare function serviceSections(o: ServicesList): Section[];
110
+ /** One service's page for the menu: its section, and its actions as keys. */
111
+ export declare function servicePage(s: ServiceRow): Page;
@@ -0,0 +1,187 @@
1
+ // Service sign-ins for sandboxes (accounts.<kind>.share): Epic, Steam, Unity, Cfx.re, signing keys and app-store
2
+ // keys. Whether each one is shared and saved (where it came from and when, never its value), taking one from this PC
3
+ // or a file, typing one, and forgetting it. Pure: schemas, labels and page builders; services.run.ts does the work.
4
+ // The kinds live in shared/accounts (a def imports no shared code): the query's rows carry everything a page shows.
5
+ import { z } from 'zod';
6
+ import { action } from '../../kernel/index.js';
7
+ import { defineOperation, defineQuery } from '../../kernel/index.js';
8
+ import { setSetting } from '../settings/settings.def.js';
9
+ /** The kinds, as the help says them (the self-test checks this against shared/accounts SERVICE_KINDS). */
10
+ export const SERVICE_NAMES_TEXT = 'epic, steam, unity, cfx, keystore, play, expo or appstore';
11
+ const KindSchema = z
12
+ .string()
13
+ .max(40)
14
+ .optional()
15
+ .describe('the service: ' + SERVICE_NAMES_TEXT);
16
+ export const ServiceRowSchema = z.object({
17
+ kind: z.string().describe('The service (epic, steam ...)'),
18
+ label: z.string().describe('Its name'),
19
+ share: z.boolean().describe('Sandboxes get it (setting accounts.<kind>.share)'),
20
+ state: z.enum(['none', 'saved', 'unreadable']).describe('Whether one is saved and can be opened by you'),
21
+ source: z.string().nullable().describe('Where the saved one came from: pc, file, typed or sandbox'),
22
+ savedAt: z.string().nullable().describe('When it was saved'),
23
+ summary: z.string().nullable().describe('Which account (never the secret)'),
24
+ heldBy: z.string().nullable().describe('The data set whose sandbox holds it now (one-holder services)'),
25
+ usedBy: z.array(z.string()).describe('The toolchains it is handed over for'),
26
+ warnings: z.array(z.string()).describe('What everyone who shares it must know'),
27
+ canImport: z.boolean().describe('It can be taken from this PC or a file (sandbox account import)'),
28
+ canLogin: z.boolean().describe('It can be typed (sandbox account login)'),
29
+ needsFile: z.boolean().describe('Importing it needs --from <file>'),
30
+ });
31
+ export const ServicesListSchema = z.object({ services: z.array(ServiceRowSchema) });
32
+ const STATE_TEXT = {
33
+ none: 'not saved',
34
+ saved: 'saved (encrypted)',
35
+ unreadable: 'cannot be read',
36
+ };
37
+ const STATE_TONE = {
38
+ none: 'muted',
39
+ saved: 'ok',
40
+ unreadable: 'danger',
41
+ };
42
+ const SOURCE_TEXT = {
43
+ pc: 'taken from this PC',
44
+ file: 'from a file',
45
+ typed: 'typed in',
46
+ sandbox: 'signed in (or renewed) in a sandbox',
47
+ };
48
+ const day = (iso) => (iso ? iso.slice(0, 10) : '');
49
+ /** `sandbox account status`: one line per service. */
50
+ export function servicesText(o) {
51
+ const table = [
52
+ ['SERVICE', 'SHARE', 'SIGN-IN', 'SAVED', 'NOTE'],
53
+ ...o.services.map((s) => [
54
+ s.kind,
55
+ s.share ? 'on' : 'off',
56
+ STATE_TEXT[s.state],
57
+ day(s.savedAt) || '-',
58
+ [s.summary, s.heldBy ? 'in use by sandbox ' + s.heldBy : null].filter(Boolean).join(', ') || '-',
59
+ ]),
60
+ ];
61
+ const widths = table[0].map((_, i) => Math.max(...table.map((r) => r[i].length)));
62
+ return table
63
+ .map((r) => r.map((cell, i) => (i === r.length - 1 ? cell : cell.padEnd(widths[i] + 2))).join(''))
64
+ .join('\n');
65
+ }
66
+ export const listServices = defineQuery({
67
+ id: 'accounts.services',
68
+ title: 'Services',
69
+ summary: 'service sign-ins for sandboxes (Epic, Steam, Unity, signing keys ...): shared or not, saved or not',
70
+ input: z.object({ name: KindSchema }),
71
+ output: ServicesListSchema,
72
+ text: servicesText,
73
+ cli: { name: 'account', action: 'status', positional: ['name'], json: true },
74
+ });
75
+ const MessageSchema = z.object({ message: z.string().describe('What happened') });
76
+ export const serviceImport = defineOperation({
77
+ id: 'accounts.service.import',
78
+ title: 'Import…',
79
+ summary: "save a service sign-in from this PC's program or a file, for sandboxes",
80
+ input: z.object({
81
+ name: KindSchema,
82
+ from: z.string().max(1000).optional().describe('(import) the file or folder to take the sign-in from'),
83
+ }),
84
+ output: MessageSchema,
85
+ text: (o) => o.message,
86
+ cli: { name: 'account', action: 'import', positional: ['name'], flags: { from: { flag: '--from', value: 'path' } } },
87
+ });
88
+ export const serviceLogin = defineOperation({
89
+ id: 'accounts.service.login',
90
+ title: 'Sign in…',
91
+ summary: 'type a service key or token to keep for sandboxes',
92
+ input: z.object({ name: KindSchema }),
93
+ output: MessageSchema,
94
+ text: (o) => o.message,
95
+ cli: { name: 'account', action: 'login', positional: ['name'] },
96
+ });
97
+ export const serviceLogout = defineOperation({
98
+ id: 'accounts.service.logout',
99
+ title: 'Forget…',
100
+ summary: 'delete a saved service sign-in, and every copy a sandbox left that this PC has not taken yet',
101
+ danger: true,
102
+ input: z.object({ name: KindSchema }),
103
+ output: z.object({ deleted: z.boolean().describe('One was saved and is gone'), message: z.string() }),
104
+ text: (o) => o.message,
105
+ cli: { name: 'account', action: 'logout', positional: ['name'] },
106
+ });
107
+ export const SERVICE_OPERATIONS = [serviceImport, serviceLogin, serviceLogout];
108
+ /** The category id of a service in the menu and its section id on the web. */
109
+ export function serviceCategory(kind) {
110
+ return 'service:' + kind;
111
+ }
112
+ /** What can be done with one service: share or stop, import, sign in, forget. */
113
+ function serviceActions(s) {
114
+ return [
115
+ action(setSetting, { scope: 'user', key: 'accounts.' + s.kind + '.share', value: !s.share }, {
116
+ key: 't',
117
+ verb: s.share ? 'stop sharing' : 'share',
118
+ label: s.share ? 'Stop sharing' : 'Share with sandboxes',
119
+ importance: 1,
120
+ }),
121
+ ...(s.canImport ? [action(serviceImport, { name: s.kind }, { key: 'i', verb: 'import', importance: 1 })] : []),
122
+ ...(s.canLogin ? [action(serviceLogin, { name: s.kind }, { key: 'n', verb: 'sign in', importance: 1 })] : []),
123
+ ...(s.state !== 'none'
124
+ ? [action(serviceLogout, { name: s.kind }, { key: 'x', verb: 'forget', importance: 2 })]
125
+ : []),
126
+ ];
127
+ }
128
+ /** How to get a first sign-in, in words. */
129
+ function howTo(s) {
130
+ const ways = [
131
+ ...(s.canImport ? ['sandbox account import ' + s.kind + (s.needsFile ? ' --from <file>' : '')] : []),
132
+ ...(s.canLogin ? ['sandbox account login ' + s.kind] : []),
133
+ ];
134
+ return ways.length ? ways.join(', or ') : 'sign in once inside a sandbox';
135
+ }
136
+ /** One service's section: its facts, its warnings and its actions. */
137
+ export function serviceSection(s) {
138
+ const facts = [
139
+ {
140
+ id: s.kind + ':share',
141
+ label: 'Shared with sandboxes',
142
+ value: s.share ? 'on' : 'off (sandbox config set accounts.' + s.kind + '.share true)',
143
+ tone: s.share ? 'ok' : 'muted',
144
+ description: 'For sandboxes that use ' + s.usedBy.join(' or ') + '. ' + s.warnings.join(' '),
145
+ },
146
+ {
147
+ id: s.kind + ':state',
148
+ label: 'Sign-in',
149
+ value: STATE_TEXT[s.state] + (s.savedAt ? ' ' + day(s.savedAt) : ''),
150
+ tone: STATE_TONE[s.state],
151
+ description: s.state === 'none' ? 'Save one: ' + howTo(s) + '.' : undefined,
152
+ },
153
+ ...(s.source ? [{ id: s.kind + ':source', label: 'From', value: SOURCE_TEXT[s.source] ?? s.source }] : []),
154
+ ...(s.summary ? [{ id: s.kind + ':summary', label: 'Account', value: s.summary }] : []),
155
+ ...(s.heldBy
156
+ ? [{ id: s.kind + ':held', label: 'In use by', value: 'sandbox ' + s.heldBy, tone: 'accent' }]
157
+ : []),
158
+ ];
159
+ const warnings = s.warnings.map((w, i) => ({
160
+ id: s.kind + ':warning:' + i,
161
+ label: w,
162
+ tone: 'warn',
163
+ group: 'Before you share it',
164
+ }));
165
+ const notes = [
166
+ ...(s.state === 'unreadable'
167
+ ? [{ text: 'Only the Windows user that saved it can read it: save it again.', tone: 'muted' }]
168
+ : []),
169
+ ];
170
+ return {
171
+ id: serviceCategory(s.kind),
172
+ title: s.label,
173
+ summary: (s.share ? 'shared' : 'not shared') + ' · ' + STATE_TEXT[s.state],
174
+ facts,
175
+ items: warnings,
176
+ notes,
177
+ actions: serviceActions(s),
178
+ };
179
+ }
180
+ /** The sections of every service (appended to the accounts page on the web). */
181
+ export function serviceSections(o) {
182
+ return o.services.map(serviceSection);
183
+ }
184
+ /** One service's page for the menu: its section, and its actions as keys. */
185
+ export function servicePage(s) {
186
+ return { title: 'Accounts', sections: [serviceSection(s)] };
187
+ }
@@ -0,0 +1,65 @@
1
+ import type { Config } from '../../../core/types.js';
2
+ import { AccountLeases, ServiceAccountStore, type PcEnv } from '../../../host/accounts/index.js';
3
+ import { type ServiceKind } from '../../../shared/accounts/index.js';
4
+ import { type OperationIO } from '../../kernel/index.js';
5
+ import { type ServiceRow } from './services.def.js';
6
+ /** One service's row: never the sign-in, and the store's summary through safeText. */
7
+ export declare function serviceRow(kind: ServiceKind, config: Config, store: ServiceAccountStore, leases: AccountLeases): ServiceRow;
8
+ export interface ImportDeps {
9
+ readonly store: ServiceAccountStore;
10
+ readonly pc: PcEnv;
11
+ readonly share: boolean;
12
+ }
13
+ /** Saves a kind's sign-in from this PC's program or from a file, after a confirmation (--yes answers it). */
14
+ export declare function importServiceFlow(io: OperationIO, kind: ServiceKind, from: string | undefined, deps: ImportDeps): Promise<{
15
+ message: string;
16
+ }>;
17
+ /** Saves what you type for a kind (a key, a token). */
18
+ export declare function loginServiceFlow(io: OperationIO, kind: ServiceKind, deps: Omit<ImportDeps, 'pc'>): Promise<{
19
+ message: string;
20
+ }>;
21
+ /** Forgets a kind's sign-in everywhere after asking (--yes answers it). */
22
+ export declare function logoutServiceFlow(io: OperationIO, kind: ServiceKind, storageRoot: string, store: ServiceAccountStore, leases: AccountLeases): Promise<{
23
+ deleted: boolean;
24
+ message: string;
25
+ }>;
26
+ export declare const listServicesRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").QueryDef<import("zod").ZodObject<{
27
+ name: import("zod").ZodOptional<import("zod").ZodString>;
28
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
29
+ services: import("zod").ZodArray<import("zod").ZodObject<{
30
+ kind: import("zod").ZodString;
31
+ label: import("zod").ZodString;
32
+ share: import("zod").ZodBoolean;
33
+ state: import("zod").ZodEnum<{
34
+ none: "none";
35
+ saved: "saved";
36
+ unreadable: "unreadable";
37
+ }>;
38
+ source: import("zod").ZodNullable<import("zod").ZodString>;
39
+ savedAt: import("zod").ZodNullable<import("zod").ZodString>;
40
+ summary: import("zod").ZodNullable<import("zod").ZodString>;
41
+ heldBy: import("zod").ZodNullable<import("zod").ZodString>;
42
+ usedBy: import("zod").ZodArray<import("zod").ZodString>;
43
+ warnings: import("zod").ZodArray<import("zod").ZodString>;
44
+ canImport: import("zod").ZodBoolean;
45
+ canLogin: import("zod").ZodBoolean;
46
+ needsFile: import("zod").ZodBoolean;
47
+ }, import("zod/v4/core").$strip>>;
48
+ }, import("zod/v4/core").$strip>, "accounts.services">, import("../../../cli/commands/context.js").Context>;
49
+ export declare const serviceImportRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{
50
+ name: import("zod").ZodOptional<import("zod").ZodString>;
51
+ from: import("zod").ZodOptional<import("zod").ZodString>;
52
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
53
+ message: import("zod").ZodString;
54
+ }, import("zod/v4/core").$strip>, "accounts.service.import">, import("../../../cli/commands/context.js").Context>;
55
+ export declare const serviceLoginRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{
56
+ name: import("zod").ZodOptional<import("zod").ZodString>;
57
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
58
+ message: import("zod").ZodString;
59
+ }, import("zod/v4/core").$strip>, "accounts.service.login">, import("../../../cli/commands/context.js").Context>;
60
+ export declare const serviceLogoutRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{
61
+ name: import("zod").ZodOptional<import("zod").ZodString>;
62
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
63
+ deleted: import("zod").ZodBoolean;
64
+ message: import("zod").ZodString;
65
+ }, import("zod/v4/core").$strip>, "accounts.service.logout">, import("../../../cli/commands/context.js").Context>;
@@ -0,0 +1,212 @@
1
+ // The service sign-ins' work: their status, taking one from this PC or a file, typing one, and forgetting one
2
+ // everywhere. Talks only through io; the sign-in itself is never an output, a log line or part of a question. The
3
+ // flows take the store, the leases and this PC's folders, so the self-test runs them with its own.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { requireOnPc } from '../../../cli/commands/context.js';
7
+ import { fail, safeText } from '../../../core/util/index.js';
8
+ import { AccountLeases, forgetEverywhere, HOST_SOURCES, pcEnv, ServiceAccountStore, } from '../../../host/accounts/index.js';
9
+ import { isServiceKind, SERVICE_KINDS, SERVICE_SPECS, } from '../../../shared/accounts/index.js';
10
+ import { readBoundedFile } from '../../../shared/io/index.js';
11
+ import { implement } from '../../context.js';
12
+ import { cancelled } from '../../kernel/index.js';
13
+ import { listServices, serviceImport, serviceLogin, serviceLogout } from './services.def.js';
14
+ /** One service's row: never the sign-in, and the store's summary through safeText. */
15
+ export function serviceRow(kind, config, store, leases) {
16
+ const spec = SERVICE_SPECS[kind];
17
+ const source = HOST_SOURCES[kind];
18
+ const state = store.state(kind);
19
+ const info = state === 'none' ? null : store.info(kind);
20
+ const holder = spec.singleHolder ? leases.holder(kind) : null;
21
+ return {
22
+ kind,
23
+ label: spec.label,
24
+ share: config.accounts[kind].share,
25
+ state,
26
+ source: info?.source ?? null,
27
+ savedAt: info?.savedAt ?? null,
28
+ summary: info?.summary ? safeText(info.summary) : null,
29
+ heldBy: holder ? safeText(holder.dataSet) : null,
30
+ usedBy: [...spec.usedBy],
31
+ warnings: [...spec.warnings],
32
+ canImport: Boolean(source.fromPc || source.fromFile),
33
+ canLogin: Boolean(source.fromAnswers),
34
+ needsFile: Boolean(source.fromFile && !source.fromPc),
35
+ };
36
+ }
37
+ /** The kind an operation is about: the name given (checked), else asked. */
38
+ async function kindOf(io, given, verb, offered = () => true) {
39
+ if (given !== undefined) {
40
+ const name = given.trim().toLowerCase();
41
+ if (!isServiceKind(name))
42
+ fail('There is no service called "' + safeText(given) + '".', 'Use one of: ' + SERVICE_KINDS.join(', '));
43
+ return name;
44
+ }
45
+ const choices = SERVICE_KINDS.filter((k) => offered(SERVICE_SPECS[k])).map((k) => ({
46
+ value: k,
47
+ label: SERVICE_SPECS[k].label,
48
+ }));
49
+ if (!choices.length)
50
+ fail('No service can do that.');
51
+ const picked = await io.choose({ title: 'Which service do you want to ' + verb + '?', choices });
52
+ if (picked === undefined || !isServiceKind(picked))
53
+ cancelled();
54
+ return picked;
55
+ }
56
+ /** Asks for the kind's fields; optional ones may stay empty. */
57
+ async function askFields(io, spec) {
58
+ const answers = {};
59
+ for (const field of spec.fields) {
60
+ const validate = (value) => {
61
+ const v = value.trim();
62
+ if (!v)
63
+ return field.optional ? true : 'This one is needed.';
64
+ return !field.pattern || field.pattern.test(v) ? true : 'That does not look right for ' + field.label + '.';
65
+ };
66
+ const title = field.label + (field.optional ? ' (Enter to skip)' : '') + ':';
67
+ const answer = field.secret
68
+ ? await io.secret({ title, validate })
69
+ : await io.text({ title, validate, allowEmpty: Boolean(field.optional) });
70
+ if (answer === undefined) {
71
+ if (field.optional)
72
+ continue;
73
+ cancelled();
74
+ }
75
+ if (answer.trim())
76
+ answers[field.name] = answer.trim();
77
+ }
78
+ return answers;
79
+ }
80
+ function savedMessage(kind, share) {
81
+ return ('Saved your ' +
82
+ SERVICE_SPECS[kind].label +
83
+ ' sign-in (encrypted for your Windows user).' +
84
+ (share ? '' : ' Sandboxes get it once you share it: sandbox config set accounts.' + kind + '.share true'));
85
+ }
86
+ /** Saves a kind's sign-in from this PC's program or from a file, after a confirmation (--yes answers it). */
87
+ export async function importServiceFlow(io, kind, from, deps) {
88
+ const spec = SERVICE_SPECS[kind];
89
+ const source = HOST_SOURCES[kind];
90
+ if (!source.fromPc && !source.fromFile)
91
+ fail(spec.label + ' cannot be imported.', 'Type it instead: sandbox account login ' + kind);
92
+ let file = from === undefined ? undefined : path.resolve(from);
93
+ const isFile = (p) => {
94
+ try {
95
+ return fs.statSync(p).isFile();
96
+ }
97
+ catch {
98
+ return false;
99
+ }
100
+ };
101
+ const useFile = Boolean(source.fromFile && (file ? isFile(file) : !source.fromPc));
102
+ if (useFile && !file) {
103
+ const asked = await io.path({ title: 'The ' + spec.label + ' file:', pathKind: 'file' });
104
+ if (asked === undefined)
105
+ cancelled();
106
+ file = path.resolve(asked);
107
+ }
108
+ const yes = await io.confirm({
109
+ title: useFile
110
+ ? 'Save the ' + spec.label + ' sign-in from this file for sandboxes?'
111
+ : "Take this PC's " + spec.label + ' sign-in for sandboxes?',
112
+ lines: [...spec.warnings],
113
+ yes: 'Save it',
114
+ noTerminal: { message: 'Cannot ask here (no terminal).', fix: 'Add --yes to save it without asking.' },
115
+ });
116
+ if (!yes)
117
+ cancelled('Nothing saved.');
118
+ let payload;
119
+ let saved;
120
+ if (useFile) {
121
+ const bytes = readBoundedFile(file, spec.maxBytes);
122
+ if (!bytes)
123
+ fail('Cannot read ' + file + ' (missing, a link, or bigger than ' + spec.maxBytes / 1024 + ' KB).');
124
+ payload = source.fromFile(bytes, await askFields(io, spec));
125
+ saved = 'file';
126
+ }
127
+ else {
128
+ payload = source.fromPc(deps.pc, file);
129
+ saved = 'pc';
130
+ }
131
+ if (!payload)
132
+ return {
133
+ message: 'No saved ' +
134
+ spec.label +
135
+ ' sign-in found on this PC' +
136
+ (file ? ' in ' + file : '') +
137
+ (spec.returns ? ' (sign in there first, or sign in once inside a sandbox).' : '.'),
138
+ };
139
+ deps.store.save(kind, payload, saved, source.summary(payload));
140
+ for (const w of spec.warnings)
141
+ io.log(w, 'warn');
142
+ return { message: savedMessage(kind, deps.share) };
143
+ }
144
+ /** Saves what you type for a kind (a key, a token). */
145
+ export async function loginServiceFlow(io, kind, deps) {
146
+ const spec = SERVICE_SPECS[kind];
147
+ const source = HOST_SOURCES[kind];
148
+ if (!source.fromAnswers)
149
+ fail(spec.label + ' has nothing to type.', spec.sources.includes('sandbox')
150
+ ? 'Sign in once inside a sandbox (it is kept), or: sandbox account import ' + kind
151
+ : 'Run: sandbox account import ' + kind + ' --from <file>');
152
+ const payload = source.fromAnswers(await askFields(io, spec));
153
+ deps.store.save(kind, payload, 'typed', source.summary(payload));
154
+ for (const w of spec.warnings)
155
+ io.log(w, 'warn');
156
+ return { message: savedMessage(kind, deps.share) };
157
+ }
158
+ /** Forgets a kind's sign-in everywhere after asking (--yes answers it). */
159
+ export async function logoutServiceFlow(io, kind, storageRoot, store, leases) {
160
+ const label = SERVICE_SPECS[kind].label;
161
+ const yes = await io.confirm({
162
+ title: 'Forget the saved ' + label + ' sign-in?',
163
+ lines: ['The next sandboxes start signed out; a copy a sandbox left behind is deleted too.'],
164
+ yes: 'Forget',
165
+ danger: true,
166
+ noTerminal: { message: 'Cannot ask here (no terminal).', fix: 'Add --yes to forget it without asking.' },
167
+ });
168
+ if (!yes)
169
+ cancelled('Nothing changed.');
170
+ const { removed, pendingDeleted } = forgetEverywhere(kind, storageRoot, store, leases);
171
+ if (!removed && !pendingDeleted)
172
+ return { deleted: false, message: 'No ' + label + ' sign-in was saved.' };
173
+ return {
174
+ deleted: true,
175
+ message: 'The saved ' +
176
+ label +
177
+ ' sign-in is deleted' +
178
+ (pendingDeleted ? ' (and ' + pendingDeleted + ' copy a sandbox left)' : '') +
179
+ '. The next sandboxes start signed out.',
180
+ };
181
+ }
182
+ export const listServicesRun = implement(listServices, async (ctx, input) => {
183
+ const store = new ServiceAccountStore(ctx.storageRoot);
184
+ const leases = new AccountLeases(ctx.storageRoot);
185
+ const name = input.name?.trim().toLowerCase();
186
+ if (name !== undefined && name !== '' && !isServiceKind(name))
187
+ fail('There is no service called "' + safeText(input.name ?? '') + '".', 'Use one of: ' + SERVICE_KINDS.join(', '));
188
+ const kinds = name ? [name] : [...SERVICE_KINDS];
189
+ return { services: kinds.map((k) => serviceRow(k, ctx.loaded.config, store, leases)) };
190
+ });
191
+ export const serviceImportRun = implement(serviceImport, async (ctx, input, io) => {
192
+ requireOnPc('account import');
193
+ const kind = await kindOf(io, input.name, 'import', (s) => Boolean(HOST_SOURCES[s.kind].fromPc || HOST_SOURCES[s.kind].fromFile));
194
+ return importServiceFlow(io, kind, input.from, {
195
+ store: new ServiceAccountStore(ctx.storageRoot),
196
+ pc: pcEnv(),
197
+ share: ctx.loaded.config.accounts[kind].share,
198
+ });
199
+ });
200
+ export const serviceLoginRun = implement(serviceLogin, async (ctx, input, io) => {
201
+ requireOnPc('account login');
202
+ const kind = await kindOf(io, input.name, 'sign in to', (s) => Boolean(HOST_SOURCES[s.kind].fromAnswers));
203
+ return loginServiceFlow(io, kind, {
204
+ store: new ServiceAccountStore(ctx.storageRoot),
205
+ share: ctx.loaded.config.accounts[kind].share,
206
+ });
207
+ });
208
+ export const serviceLogoutRun = implement(serviceLogout, async (ctx, input, io) => {
209
+ requireOnPc('account logout');
210
+ const kind = await kindOf(io, input.name, 'forget');
211
+ return logoutServiceFlow(io, kind, ctx.storageRoot, new ServiceAccountStore(ctx.storageRoot), new AccountLeases(ctx.storageRoot));
212
+ });