@astrale-os/cli 1.0.0-beta.32 → 1.0.0-beta.43

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 (441) hide show
  1. package/CHANGELOG.md +928 -0
  2. package/README.md +53 -43
  3. package/THIRD-PARTY-NOTICES.md +37 -1
  4. package/dist/astrale.js +349631 -83669
  5. package/dist/public/connect-core.js +16 -47
  6. package/dist/public/keys/index.js +1 -1
  7. package/dist/types/admin/contract.d.ts +11 -0
  8. package/dist/types/admin/graph/index.d.ts +1 -0
  9. package/dist/types/admin/graph/nodes.d.ts +12 -0
  10. package/dist/types/admin/instance/client.d.ts +2 -0
  11. package/dist/types/connection/lifetime.d.ts +1 -1
  12. package/dist/types/errors.d.ts +3 -0
  13. package/dist/types/lib/proc.d.ts +1 -0
  14. package/dist/types/lib/update.d.ts +15 -7
  15. package/package.json +8 -6
  16. package/studio/client/dist/assets/index-5C1uT6ML.css +2 -0
  17. package/studio/client/dist/assets/index-C4kNlvwF.js +80 -0
  18. package/studio/client/dist/assets/schema-studio-BR9d0faB.js +8 -0
  19. package/studio/client/dist/index.html +13 -2
  20. package/studio/package.json +2 -1
  21. package/studio/server/agent/ask.test.ts +7 -1
  22. package/studio/server/agent/bridge/grant.ts +15 -3
  23. package/studio/server/agent/prompts/system.test.ts +5 -0
  24. package/studio/server/agent/prompts/system.ts +19 -4
  25. package/studio/server/agent/routes.test.ts +39 -1
  26. package/studio/server/agent/routes.ts +3 -0
  27. package/studio/server/agent/run/coordinator.test.ts +7 -1
  28. package/studio/server/agent/run/preparation.ts +1 -0
  29. package/studio/server/agent/run/transcript.test.ts +47 -2
  30. package/studio/server/agent/run/transcript.ts +32 -3
  31. package/studio/server/api/documents.test.ts +31 -0
  32. package/studio/server/api/documents.ts +35 -1
  33. package/studio/server/api-agent-loadout.test.ts +7 -1
  34. package/studio/server/api.test.ts +7 -1
  35. package/studio/server/cache.ts +4 -4
  36. package/studio/server/detect.ts +9 -12
  37. package/studio/server/domain.test.ts +83 -3
  38. package/studio/server/domain.ts +205 -27
  39. package/studio/server/handoff/service.ts +1 -1
  40. package/studio/server/index.ts +5 -8
  41. package/studio/server/introspect/extractor.ts +111 -17
  42. package/studio/server/introspect/overlay-tsmorph.test.ts +9 -9
  43. package/studio/server/introspect/overlay.ts +1 -1
  44. package/studio/server/introspect/runtime.ts +9 -1
  45. package/studio/server/introspect/source-overlay/spans.ts +2 -5
  46. package/studio/server/lifecycle.ts +1 -1
  47. package/studio/server/state/baseline.test.ts +14 -0
  48. package/studio/server/state/baseline.ts +16 -2
  49. package/studio/server/watch.test.ts +2 -1
  50. package/studio/server/watch.ts +5 -2
  51. package/studio/server/workspace/create.ts +9 -8
  52. package/studio/server/workspace-state.ts +4 -6
  53. package/studio/server/workspace-watch.ts +33 -22
  54. package/studio/shared/contracts/agent.ts +3 -0
  55. package/viewer/dist/main.js +12 -12
  56. package/src/__tests__/fixtures/publication.ts +0 -20
  57. package/src/admin/.spec/api.d.ts +0 -3
  58. package/src/admin/.spec/architecture.md +0 -22
  59. package/src/admin/.spec/icon.svg +0 -3
  60. package/src/admin/.spec/layout.ts +0 -6
  61. package/src/admin/__tests__/fixture.ts +0 -25
  62. package/src/admin/catalog/.spec/api.d.ts +0 -63
  63. package/src/admin/catalog/.spec/architecture.md +0 -7
  64. package/src/admin/catalog/.spec/icon.svg +0 -3
  65. package/src/admin/catalog/.spec/layout.ts +0 -6
  66. package/src/admin/catalog/__tests__/client.test.ts +0 -245
  67. package/src/admin/catalog/client.ts +0 -195
  68. package/src/admin/catalog/index.ts +0 -13
  69. package/src/admin/catalog/model.ts +0 -41
  70. package/src/admin/catalog/tsconfig.json +0 -8
  71. package/src/admin/contract.ts +0 -47
  72. package/src/admin/graph/.spec/api.d.ts +0 -22
  73. package/src/admin/graph/.spec/architecture.md +0 -5
  74. package/src/admin/graph/.spec/icon.svg +0 -3
  75. package/src/admin/graph/.spec/layout.ts +0 -3
  76. package/src/admin/graph/index.ts +0 -6
  77. package/src/admin/graph/nodes.ts +0 -56
  78. package/src/admin/index.ts +0 -2
  79. package/src/admin/instance/.spec/api.d.ts +0 -62
  80. package/src/admin/instance/.spec/architecture.md +0 -10
  81. package/src/admin/instance/.spec/icon.svg +0 -3
  82. package/src/admin/instance/.spec/layout.ts +0 -6
  83. package/src/admin/instance/__tests__/client.test.ts +0 -273
  84. package/src/admin/instance/client.ts +0 -191
  85. package/src/admin/instance/index.ts +0 -15
  86. package/src/admin/instance/model.ts +0 -59
  87. package/src/admin/instance/tsconfig.json +0 -8
  88. package/src/commands/__tests__/admin-instance.test.ts +0 -91
  89. package/src/commands/__tests__/auth-login.test.ts +0 -231
  90. package/src/commands/__tests__/auth-token.test.ts +0 -223
  91. package/src/commands/__tests__/call-describe.test.ts +0 -80
  92. package/src/commands/__tests__/call.test.ts +0 -63
  93. package/src/commands/__tests__/domain-install-operation.test.ts +0 -145
  94. package/src/commands/__tests__/domain-install-owned.test.ts +0 -191
  95. package/src/commands/__tests__/domain-list.test.ts +0 -133
  96. package/src/commands/__tests__/domain-uninstall.test.ts +0 -53
  97. package/src/commands/__tests__/install-direct.test.ts +0 -128
  98. package/src/commands/__tests__/install-identity-override.test.ts +0 -76
  99. package/src/commands/__tests__/instance-bookmark.test.ts +0 -166
  100. package/src/commands/__tests__/instance-list-admin.test.ts +0 -26
  101. package/src/commands/__tests__/instance-list-rows.test.ts +0 -64
  102. package/src/commands/__tests__/instance-status.test.ts +0 -139
  103. package/src/commands/__tests__/instance-use.test.ts +0 -67
  104. package/src/commands/__tests__/introspect-parse.test.ts +0 -100
  105. package/src/commands/__tests__/logs.test.ts +0 -345
  106. package/src/commands/__tests__/read-commands.test.ts +0 -377
  107. package/src/commands/__tests__/setup-plan.test.ts +0 -61
  108. package/src/commands/__tests__/studio-descriptor.test.ts +0 -22
  109. package/src/commands/__tests__/token-ttl.test.ts +0 -66
  110. package/src/commands/__tests__/update.test.ts +0 -95
  111. package/src/commands/__tests__/view-build.test.ts +0 -58
  112. package/src/commands/__tests__/view.test.ts +0 -197
  113. package/src/commands/admin/status.ts +0 -61
  114. package/src/commands/admin/use.ts +0 -100
  115. package/src/commands/auth/login.ts +0 -82
  116. package/src/commands/auth/logout.ts +0 -53
  117. package/src/commands/auth/status.ts +0 -86
  118. package/src/commands/auth/token.ts +0 -174
  119. package/src/commands/browser.ts +0 -236
  120. package/src/commands/call-describe.ts +0 -123
  121. package/src/commands/call.ts +0 -230
  122. package/src/commands/domain/install.ts +0 -575
  123. package/src/commands/domain/list.ts +0 -155
  124. package/src/commands/domain/publish.ts +0 -136
  125. package/src/commands/domain/uninstall.ts +0 -128
  126. package/src/commands/get.ts +0 -69
  127. package/src/commands/identity/__tests__/register.test.ts +0 -93
  128. package/src/commands/identity/create.ts +0 -35
  129. package/src/commands/identity/delete.ts +0 -24
  130. package/src/commands/identity/export.ts +0 -46
  131. package/src/commands/identity/import.ts +0 -61
  132. package/src/commands/identity/list.ts +0 -56
  133. package/src/commands/identity/register.ts +0 -195
  134. package/src/commands/identity/sync.ts +0 -38
  135. package/src/commands/identity/unsync.ts +0 -34
  136. package/src/commands/identity/use.ts +0 -24
  137. package/src/commands/identity/whoami.ts +0 -34
  138. package/src/commands/idp/add.ts +0 -150
  139. package/src/commands/idp/list.ts +0 -57
  140. package/src/commands/idp/refresh.ts +0 -38
  141. package/src/commands/idp/remove.ts +0 -36
  142. package/src/commands/idp/show.ts +0 -29
  143. package/src/commands/instance/active.ts +0 -73
  144. package/src/commands/instance/bookmark.ts +0 -101
  145. package/src/commands/instance/create.ts +0 -61
  146. package/src/commands/instance/delete.ts +0 -69
  147. package/src/commands/instance/forget.ts +0 -26
  148. package/src/commands/instance/list.ts +0 -197
  149. package/src/commands/instance/status.ts +0 -92
  150. package/src/commands/instance/use.ts +0 -263
  151. package/src/commands/introspect.ts +0 -129
  152. package/src/commands/logs.ts +0 -371
  153. package/src/commands/mutate.ts +0 -122
  154. package/src/commands/query.ts +0 -142
  155. package/src/commands/session/analyze.ts +0 -72
  156. package/src/commands/session/list.ts +0 -36
  157. package/src/commands/setup.ts +0 -54
  158. package/src/commands/status.ts +0 -60
  159. package/src/commands/studio.ts +0 -434
  160. package/src/commands/token.ts +0 -121
  161. package/src/commands/ui/__tests__/commands.test.ts +0 -152
  162. package/src/commands/ui/add.ts +0 -51
  163. package/src/commands/ui/doctor.ts +0 -21
  164. package/src/commands/ui/init.ts +0 -38
  165. package/src/commands/ui/list.ts +0 -26
  166. package/src/commands/ui/preset-apply.ts +0 -19
  167. package/src/commands/ui/preset-list.ts +0 -12
  168. package/src/commands/ui/shared.ts +0 -25
  169. package/src/commands/update.ts +0 -356
  170. package/src/commands/use.ts +0 -86
  171. package/src/commands/view-serve.ts +0 -26
  172. package/src/commands/view.ts +0 -658
  173. package/src/connect-core.test.ts +0 -38
  174. package/src/connect-core.ts +0 -42
  175. package/src/connection/.spec/api.d.ts +0 -110
  176. package/src/connection/.spec/architecture.md +0 -58
  177. package/src/connection/.spec/capabilities/connection.ts +0 -13
  178. package/src/connection/.spec/examples/call.ts +0 -7
  179. package/src/connection/.spec/flows/command.ts +0 -20
  180. package/src/connection/.spec/flows/session.ts +0 -62
  181. package/src/connection/.spec/laws/connection.ts +0 -213
  182. package/src/connection/.spec/layout.ts +0 -22
  183. package/src/connection/__tests__/auth.test.ts +0 -129
  184. package/src/connection/__tests__/ca-fetch.test.ts +0 -60
  185. package/src/connection/__tests__/call.test.ts +0 -17
  186. package/src/connection/__tests__/credential.test.ts +0 -254
  187. package/src/connection/__tests__/errors.test.ts +0 -577
  188. package/src/connection/__tests__/exchange.test.ts +0 -404
  189. package/src/connection/__tests__/failure-fixtures.ts +0 -39
  190. package/src/connection/__tests__/failure-safety.test.ts +0 -75
  191. package/src/connection/__tests__/fixtures/localhost-cert.pem +0 -19
  192. package/src/connection/__tests__/fixtures/localhost-key.base64 +0 -1
  193. package/src/connection/__tests__/managed.test.ts +0 -39
  194. package/src/connection/__tests__/reasons.test.ts +0 -78
  195. package/src/connection/__tests__/self.test.ts +0 -71
  196. package/src/connection/__tests__/session.test.ts +0 -144
  197. package/src/connection/__tests__/target.test.ts +0 -95
  198. package/src/connection/auth.ts +0 -265
  199. package/src/connection/call.ts +0 -7
  200. package/src/connection/command.ts +0 -67
  201. package/src/connection/credential.ts +0 -138
  202. package/src/connection/errors.ts +0 -1
  203. package/src/connection/exchange.ts +0 -394
  204. package/src/connection/failure/classify.ts +0 -110
  205. package/src/connection/failure/index.ts +0 -18
  206. package/src/connection/failure/model.ts +0 -28
  207. package/src/connection/failure/render.ts +0 -116
  208. package/src/connection/index.ts +0 -8
  209. package/src/connection/lifetime.ts +0 -25
  210. package/src/connection/reasons.ts +0 -209
  211. package/src/connection/self.ts +0 -138
  212. package/src/connection/session.ts +0 -203
  213. package/src/connection/target.ts +0 -112
  214. package/src/connection/tsconfig.json +0 -6
  215. package/src/errors.ts +0 -66
  216. package/src/graph/.spec/api.d.ts +0 -41
  217. package/src/graph/.spec/laws/documents.ts +0 -24
  218. package/src/graph/.spec/laws/projection.ts +0 -13
  219. package/src/graph/.spec/layout.ts +0 -14
  220. package/src/graph/__tests__/mutation.test.ts +0 -51
  221. package/src/graph/__tests__/projection.test.ts +0 -22
  222. package/src/graph/__tests__/query.test.ts +0 -180
  223. package/src/graph/class.ts +0 -30
  224. package/src/graph/index.ts +0 -4
  225. package/src/graph/mutation.ts +0 -46
  226. package/src/graph/projection.ts +0 -21
  227. package/src/graph/query.ts +0 -95
  228. package/src/graph/tsconfig.json +0 -6
  229. package/src/identity/.history/ard/bootstrap-root-transfer.md +0 -57
  230. package/src/identity/.history/ard/domain-mediated-registration.md +0 -36
  231. package/src/identity/.spec/api.d.ts +0 -124
  232. package/src/identity/.spec/architecture.md +0 -33
  233. package/src/identity/.spec/capabilities/identity.ts +0 -7
  234. package/src/identity/.spec/flows/bootstrap-root.ts +0 -79
  235. package/src/identity/.spec/flows/domain-mediated-registration.ts +0 -51
  236. package/src/identity/.spec/flows/import.ts +0 -29
  237. package/src/identity/.spec/laws/identity.ts +0 -56
  238. package/src/identity/.spec/layout.ts +0 -13
  239. package/src/identity/__tests__/fixtures/registry-journey.ts +0 -65
  240. package/src/identity/__tests__/registration.test.ts +0 -64
  241. package/src/identity/__tests__/registry.test.ts +0 -74
  242. package/src/identity/__tests__/transfer.test.ts +0 -198
  243. package/src/identity/index.ts +0 -23
  244. package/src/identity/registration.ts +0 -72
  245. package/src/identity/registry.ts +0 -181
  246. package/src/identity/transfer.ts +0 -187
  247. package/src/identity/tsconfig.json +0 -6
  248. package/src/index.ts +0 -1
  249. package/src/keys/.spec/api.d.ts +0 -74
  250. package/src/keys/.spec/architecture.md +0 -13
  251. package/src/keys/.spec/laws/keys.ts +0 -43
  252. package/src/keys/.spec/layout.ts +0 -6
  253. package/src/keys/__tests__/algorithm.test.ts +0 -39
  254. package/src/keys/__tests__/keys.test.ts +0 -172
  255. package/src/keys/algorithm.ts +0 -33
  256. package/src/keys/credential.ts +0 -113
  257. package/src/keys/index.ts +0 -14
  258. package/src/keys/pair.ts +0 -219
  259. package/src/keys/tsconfig.json +0 -6
  260. package/src/lib/__tests__/admin-target.test.ts +0 -184
  261. package/src/lib/__tests__/binary.test.ts +0 -70
  262. package/src/lib/__tests__/browser-retention.test.ts +0 -212
  263. package/src/lib/__tests__/command-dx.test.ts +0 -102
  264. package/src/lib/__tests__/concurrency.test.ts +0 -62
  265. package/src/lib/__tests__/config.test.ts +0 -81
  266. package/src/lib/__tests__/design.test.ts +0 -99
  267. package/src/lib/__tests__/domain-publication.test.ts +0 -138
  268. package/src/lib/__tests__/format.test.ts +0 -22
  269. package/src/lib/__tests__/idp-session.driver.ts +0 -60
  270. package/src/lib/__tests__/idp-session.test.ts +0 -404
  271. package/src/lib/__tests__/idp.test.ts +0 -415
  272. package/src/lib/__tests__/instance-candidates.test.ts +0 -146
  273. package/src/lib/__tests__/instance-target.test.ts +0 -213
  274. package/src/lib/__tests__/instance.test.ts +0 -192
  275. package/src/lib/__tests__/local-status.test.ts +0 -202
  276. package/src/lib/__tests__/log-errors.test.ts +0 -64
  277. package/src/lib/__tests__/log.test.ts +0 -31
  278. package/src/lib/__tests__/meta.test.ts +0 -55
  279. package/src/lib/__tests__/output.test.ts +0 -162
  280. package/src/lib/__tests__/panel.test.ts +0 -40
  281. package/src/lib/__tests__/port.test.ts +0 -44
  282. package/src/lib/__tests__/prompt.test.ts +0 -25
  283. package/src/lib/__tests__/sdk-deps.test.ts +0 -68
  284. package/src/lib/__tests__/self.test.ts +0 -50
  285. package/src/lib/__tests__/skills.test.ts +0 -834
  286. package/src/lib/__tests__/studio-server-deps.test.ts +0 -111
  287. package/src/lib/__tests__/table.test.ts +0 -53
  288. package/src/lib/__tests__/update.test.ts +0 -511
  289. package/src/lib/__tests__/use-target.test.ts +0 -56
  290. package/src/lib/__tests__/validation.test.ts +0 -34
  291. package/src/lib/__tests__/view-assets.test.ts +0 -171
  292. package/src/lib/__tests__/view-external-open-intent.test.ts +0 -150
  293. package/src/lib/__tests__/view-external-open-origins.test.ts +0 -27
  294. package/src/lib/__tests__/view-open-intent.test.ts +0 -280
  295. package/src/lib/__tests__/view-port-allocation.test.ts +0 -82
  296. package/src/lib/__tests__/view-server.test.ts +0 -91
  297. package/src/lib/__tests__/view-session.test.ts +0 -100
  298. package/src/lib/__tests__/view-snapshot.test.ts +0 -77
  299. package/src/lib/admin-domain.ts +0 -43
  300. package/src/lib/admin-instance.ts +0 -56
  301. package/src/lib/admin-target.ts +0 -290
  302. package/src/lib/binary.ts +0 -127
  303. package/src/lib/browser-retention.ts +0 -210
  304. package/src/lib/browser.ts +0 -150
  305. package/src/lib/ca-fetch.ts +0 -133
  306. package/src/lib/command-dx.ts +0 -191
  307. package/src/lib/concurrency.ts +0 -31
  308. package/src/lib/config.ts +0 -57
  309. package/src/lib/credential-lifetime.ts +0 -24
  310. package/src/lib/domain-publication.ts +0 -104
  311. package/src/lib/failure-debug.ts +0 -26
  312. package/src/lib/format.ts +0 -4
  313. package/src/lib/idp-session.ts +0 -156
  314. package/src/lib/idp.ts +0 -926
  315. package/src/lib/instance-candidates.ts +0 -49
  316. package/src/lib/instance-target.ts +0 -197
  317. package/src/lib/instance.ts +0 -468
  318. package/src/lib/invocation.ts +0 -11
  319. package/src/lib/local-status.ts +0 -152
  320. package/src/lib/log.ts +0 -143
  321. package/src/lib/login-flow.ts +0 -201
  322. package/src/lib/meta.ts +0 -90
  323. package/src/lib/output.ts +0 -238
  324. package/src/lib/panel.ts +0 -61
  325. package/src/lib/port.ts +0 -41
  326. package/src/lib/proc.ts +0 -87
  327. package/src/lib/prompt.ts +0 -136
  328. package/src/lib/provision-instance.ts +0 -139
  329. package/src/lib/sdk-deps.ts +0 -104
  330. package/src/lib/self.ts +0 -22
  331. package/src/lib/skills/lock.ts +0 -117
  332. package/src/lib/skills/sync.ts +0 -878
  333. package/src/lib/skills.ts +0 -120
  334. package/src/lib/table.ts +0 -62
  335. package/src/lib/update.ts +0 -568
  336. package/src/lib/use-target.ts +0 -24
  337. package/src/lib/validation.ts +0 -59
  338. package/src/lib/view/assets.ts +0 -87
  339. package/src/lib/view/external-open-intent.ts +0 -41
  340. package/src/lib/view/external-open-origins.ts +0 -37
  341. package/src/lib/view/host-capabilities.ts +0 -17
  342. package/src/lib/view/open-intent.ts +0 -67
  343. package/src/lib/view/port-allocation.ts +0 -19
  344. package/src/lib/view/resolve.ts +0 -111
  345. package/src/lib/view/server.ts +0 -259
  346. package/src/lib/view/session.ts +0 -154
  347. package/src/lib/view/snapshot.ts +0 -101
  348. package/src/paths/.spec/api.d.ts +0 -13
  349. package/src/paths/.spec/architecture.md +0 -5
  350. package/src/paths/.spec/layout.ts +0 -3
  351. package/src/paths/index.ts +0 -13
  352. package/src/program/.spec/api.d.ts +0 -43
  353. package/src/program/.spec/architecture.md +0 -18
  354. package/src/program/.spec/laws/program.ts +0 -29
  355. package/src/program/.spec/layout.ts +0 -14
  356. package/src/program/__tests__/program.test.ts +0 -556
  357. package/src/program/argv.ts +0 -14
  358. package/src/program/build.ts +0 -205
  359. package/src/program/command.ts +0 -43
  360. package/src/program/index.ts +0 -3
  361. package/src/program/options.ts +0 -41
  362. package/src/program/registry.ts +0 -64
  363. package/src/program/tsconfig.json +0 -6
  364. package/src/setup/__tests__/instance-step.test.ts +0 -256
  365. package/src/setup/__tests__/util.test.ts +0 -29
  366. package/src/setup/engine.ts +0 -83
  367. package/src/setup/render.ts +0 -107
  368. package/src/setup/steps/admin.ts +0 -93
  369. package/src/setup/steps/agent-browser.ts +0 -81
  370. package/src/setup/steps/auth.ts +0 -54
  371. package/src/setup/steps/domain.ts +0 -68
  372. package/src/setup/steps/index.ts +0 -18
  373. package/src/setup/steps/instance.ts +0 -206
  374. package/src/setup/steps/skills-bridge.ts +0 -70
  375. package/src/setup/steps/skills.ts +0 -61
  376. package/src/setup/types.ts +0 -61
  377. package/src/setup/util.ts +0 -34
  378. package/src/state/.spec/api.d.ts +0 -152
  379. package/src/state/.spec/architecture.md +0 -45
  380. package/src/state/.spec/capabilities/state.ts +0 -7
  381. package/src/state/.spec/flows/identity-update.ts +0 -22
  382. package/src/state/.spec/laws/state.ts +0 -73
  383. package/src/state/.spec/layout.ts +0 -15
  384. package/src/state/__tests__/exchange-credentials.test.ts +0 -341
  385. package/src/state/__tests__/files.test.ts +0 -136
  386. package/src/state/__tests__/fixtures/identity-transition.ts +0 -37
  387. package/src/state/__tests__/fixtures/session-route-process.ts +0 -93
  388. package/src/state/__tests__/identities.test.ts +0 -142
  389. package/src/state/__tests__/paths.test.ts +0 -28
  390. package/src/state/__tests__/session-routes.test.ts +0 -138
  391. package/src/state/exchange-credentials.ts +0 -250
  392. package/src/state/files.ts +0 -170
  393. package/src/state/identities.ts +0 -229
  394. package/src/state/index.ts +0 -31
  395. package/src/state/paths.ts +0 -64
  396. package/src/state/session-routes.ts +0 -34
  397. package/src/state/tsconfig.json +0 -6
  398. package/src/telemetry/__tests__/analyze-log.test.ts +0 -38
  399. package/src/telemetry/__tests__/gate.test.ts +0 -63
  400. package/src/telemetry/__tests__/recorder.test.ts +0 -111
  401. package/src/telemetry/__tests__/redact.test.ts +0 -79
  402. package/src/telemetry/__tests__/retention.test.ts +0 -267
  403. package/src/telemetry/__tests__/session.test.ts +0 -166
  404. package/src/telemetry/__tests__/settings.test.ts +0 -102
  405. package/src/telemetry/__tests__/store-scan.test.ts +0 -128
  406. package/src/telemetry/__tests__/trigger.test.ts +0 -78
  407. package/src/telemetry/adapters/__tests__/claude-code.test.ts +0 -89
  408. package/src/telemetry/adapters/__tests__/codex.test.ts +0 -138
  409. package/src/telemetry/adapters/__tests__/index.test.ts +0 -88
  410. package/src/telemetry/adapters/claude-code.ts +0 -90
  411. package/src/telemetry/adapters/codex.ts +0 -160
  412. package/src/telemetry/adapters/index.ts +0 -45
  413. package/src/telemetry/adapters/types.ts +0 -21
  414. package/src/telemetry/analyze.ts +0 -249
  415. package/src/telemetry/gate.ts +0 -79
  416. package/src/telemetry/recorder.ts +0 -68
  417. package/src/telemetry/redact.ts +0 -53
  418. package/src/telemetry/retention.ts +0 -176
  419. package/src/telemetry/session.ts +0 -94
  420. package/src/telemetry/settings.ts +0 -79
  421. package/src/telemetry/store.ts +0 -174
  422. package/src/telemetry/trigger.ts +0 -107
  423. package/src/telemetry/types.ts +0 -64
  424. package/src/test-utils.ts +0 -18
  425. package/src/ui/.spec/api.d.ts +0 -14
  426. package/src/ui/.spec/architecture.md +0 -14
  427. package/src/ui/.spec/laws.ts +0 -50
  428. package/src/ui/.spec/layout.ts +0 -16
  429. package/src/ui/__tests__/ui.test.ts +0 -1742
  430. package/src/ui/index.ts +0 -13
  431. package/src/ui/lock.ts +0 -89
  432. package/src/ui/model.ts +0 -83
  433. package/src/ui/operations.ts +0 -1044
  434. package/src/ui/project.ts +0 -333
  435. package/src/ui/release.ts +0 -398
  436. package/src/ui/runner.ts +0 -18
  437. package/studio/client/dist/assets/index-BFVFs_8x.js +0 -81
  438. package/studio/client/dist/assets/index-DuB9iUdu.css +0 -2
  439. package/studio/client/dist/assets/schema-studio-DH6oEWME.js +0 -8
  440. package/studio/tsconfig.json +0 -24
  441. package/viewer/tsconfig.json +0 -13
@@ -1,111 +0,0 @@
1
- import { afterEach, beforeAll, beforeEach, describe, expect, test } from 'bun:test'
2
- import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
3
- import { tmpdir } from 'node:os'
4
- import { join } from 'node:path'
5
-
6
- import type { Finalizer } from '../recorder'
7
- import type { SessionInfo } from '../store'
8
- import type { TelemetryEvent } from '../types'
9
-
10
- // ASTRALE_HOME must be set before the store/paths singleton is first imported.
11
- process.env.ASTRALE_HOME = mkdtempSync(join(tmpdir(), 'astrale-tele-recorder-'))
12
-
13
- let beginInvocation: (argv: string[]) => Finalizer
14
- let eventsPath: (id: string) => string
15
- let listSessions: () => SessionInfo[]
16
- let sessionsRoot: () => string
17
- let configPath: string
18
-
19
- const SESSION = 'rec-test'
20
-
21
- let work: string
22
- let cwd0: string
23
-
24
- beforeAll(async () => {
25
- const recorder = await import('../recorder')
26
- const store = await import('../store')
27
- beginInvocation = recorder.beginInvocation
28
- eventsPath = store.eventsPath
29
- listSessions = store.listSessions
30
- sessionsRoot = store.sessionsRoot
31
- configPath = join(process.env.ASTRALE_HOME!, 'config.json')
32
- })
33
-
34
- beforeEach(() => {
35
- // Destructive cleanup must be provably confined to this file's mkdtemp home.
36
- if (!sessionsRoot().startsWith(tmpdir())) throw new Error('refusing to clean a non-tmp home')
37
- rmSync(sessionsRoot(), { recursive: true, force: true })
38
- rmSync(configPath, { force: true })
39
- delete process.env.ASTRALE_TELEMETRY
40
- process.env.ASTRALE_SESSION = SESSION
41
- work = mkdtempSync(join(tmpdir(), 'astrale-tele-recwork-'))
42
- cwd0 = process.cwd()
43
- process.chdir(work)
44
- })
45
-
46
- afterEach(() => {
47
- process.chdir(cwd0)
48
- rmSync(work, { recursive: true, force: true })
49
- })
50
-
51
- function readEvents(id: string): TelemetryEvent[] {
52
- return readFileSync(eventsPath(id), 'utf-8')
53
- .split('\n')
54
- .filter(Boolean)
55
- .map((l) => JSON.parse(l) as TelemetryEvent)
56
- }
57
-
58
- describe('beginInvocation', () => {
59
- test('appends one valid, parseable event with redacted argv', () => {
60
- const cwd = process.cwd()
61
- const finalize = beginInvocation(['node', 'astrale', 'get', '--token', 'abc', 'x'])
62
- finalize(0)
63
-
64
- const events = readEvents(SESSION)
65
- expect(events).toHaveLength(1)
66
- const [e] = events
67
- expect(e.v).toBe(1)
68
- expect(e.surface).toBe('cli')
69
- expect(e.exitCode).toBe(0)
70
- expect(e.argv).toEqual(['get', '--token', '<redacted>', 'x'])
71
- expect(e.cwd).toBe(cwd)
72
- expect(e.root).toBe(cwd)
73
- expect(typeof e.ts).toBe('string')
74
- expect(typeof e.durationMs).toBe('number')
75
- expect('errorName' in e).toBe(false)
76
- })
77
-
78
- test('records exitCode and errorName on failure', () => {
79
- beginInvocation(['node', 'astrale', 'call', 'foo'])(1, 'AuthError')
80
- const [e] = readEvents(SESSION)
81
- expect(e.exitCode).toBe(1)
82
- expect(e.errorName).toBe('AuthError')
83
- })
84
-
85
- test('appends a second line on a second invocation', () => {
86
- beginInvocation(['node', 'astrale', 'query'])(0)
87
- beginInvocation(['node', 'astrale', 'whoami'])(0)
88
- expect(readEvents(SESSION)).toHaveLength(2)
89
- })
90
-
91
- test('writes nothing for help/version-only invocations', () => {
92
- beginInvocation(['node', 'astrale', '--help'])(0)
93
- beginInvocation(['node', 'astrale', '--version'])(0)
94
- beginInvocation(['node', 'astrale', '--cli-version'])(0)
95
- beginInvocation(['node', 'astrale'])(0)
96
- expect(listSessions()).toHaveLength(0)
97
- })
98
-
99
- test('writes nothing when disabled via ASTRALE_TELEMETRY', () => {
100
- process.env.ASTRALE_TELEMETRY = '0'
101
- beginInvocation(['node', 'astrale', 'get', 'x'])(0)
102
- expect(listSessions()).toHaveLength(0)
103
- expect(existsSync(eventsPath(SESSION))).toBe(false)
104
- })
105
-
106
- test('writes nothing when disabled via config telemetry.enabled=false', () => {
107
- writeFileSync(configPath, JSON.stringify({ telemetry: { enabled: false } }))
108
- beginInvocation(['node', 'astrale', 'get', 'x'])(0)
109
- expect(listSessions()).toHaveLength(0)
110
- })
111
- })
@@ -1,79 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
-
3
- import { redactArgv } from '../redact'
4
-
5
- describe('redactArgv', () => {
6
- test('redacts the value following a secret flag', () => {
7
- expect(redactArgv(['--token', 'abc123', '--json'])).toEqual(['--token', '<redacted>', '--json'])
8
- })
9
-
10
- test('matches secret substrings in flag names, case-insensitively', () => {
11
- expect(redactArgv(['--api-Token', 's', '--Bearer', 'b', '--jwk', 'k'])).toEqual([
12
- '--api-Token',
13
- '<redacted>',
14
- '--Bearer',
15
- '<redacted>',
16
- '--jwk',
17
- '<redacted>',
18
- ])
19
- })
20
-
21
- test('redacts the value part of inline k=v secret args', () => {
22
- expect(redactArgv(['--secret=hunter2', 'password=p', '--verbose'])).toEqual([
23
- '--secret=<redacted>',
24
- 'password=<redacted>',
25
- '--verbose',
26
- ])
27
- })
28
-
29
- test('leaves non-secret flags and their values intact', () => {
30
- expect(redactArgv(['query', '--limit', '3', '--format=json'])).toEqual([
31
- 'query',
32
- '--limit',
33
- '3',
34
- '--format=json',
35
- ])
36
- })
37
-
38
- test('truncates any arg longer than 200 chars to 200 + ellipsis', () => {
39
- const long = 'x'.repeat(300)
40
- const [only] = redactArgv([long])
41
- expect(only).toBe('x'.repeat(200) + '…')
42
- expect([...only].length).toBe(201)
43
- })
44
-
45
- test('a secret value is redacted before length matters', () => {
46
- expect(redactArgv(['--password', 'y'.repeat(500)])).toEqual(['--password', '<redacted>'])
47
- })
48
-
49
- test('caps the array at 40 items with a …+N marker', () => {
50
- const args = Array.from({ length: 45 }, (_, i) => `a${i}`)
51
- const out = redactArgv(args)
52
- expect(out.length).toBe(41)
53
- expect(out.slice(0, 40)).toEqual(args.slice(0, 40))
54
- expect(out[40]).toBe('…+5')
55
- })
56
-
57
- test('does not cap at exactly 40 items', () => {
58
- const args = Array.from({ length: 40 }, (_, i) => `a${i}`)
59
- expect(redactArgv(args)).toEqual(args)
60
- })
61
-
62
- test('a trailing secret flag with no value does not throw', () => {
63
- expect(redactArgv(['call', '--token'])).toEqual(['call', '--token'])
64
- })
65
- })
66
-
67
- describe('secret-shaped values', () => {
68
- test('positional JWT is redacted wherever it appears', () => {
69
- const jwt =
70
- 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U'
71
- expect(redactArgv(['call', '/x::y', jwt])).toEqual(['call', '/x::y', '<redacted>'])
72
- expect(redactArgv(['call', `body=Bearer ${jwt}`])[1]).toBe('body=Bearer <redacted>')
73
- })
74
- test('sk- style api key is redacted, plain paths are not', () => {
75
- expect(redactArgv(['x', 'sk-abcdefghijklmnop1234'])).toEqual(['x', '<redacted>'])
76
- const p = '/Users/someone/Documents/astrale/workspace/cli/src/telemetry/adapters/index.ts'
77
- expect(redactArgv([p])).toEqual([p])
78
- })
79
- })
@@ -1,267 +0,0 @@
1
- import { afterEach, beforeAll, beforeEach, describe, expect, test } from 'bun:test'
2
- import { existsSync, mkdirSync, mkdtempSync, rmSync, utimesSync, writeFileSync } from 'node:fs'
3
- import { tmpdir } from 'node:os'
4
- import { join } from 'node:path'
5
-
6
- import type { SweepOptions, SweepResult } from '../retention'
7
- import type { RetentionBudget } from '../settings'
8
- import type { SessionScan } from '../store'
9
-
10
- // Set the home before the paths singleton is captured (dynamic imports below).
11
- process.env.ASTRALE_HOME = mkdtempSync(join(tmpdir(), 'astrale-tele-retention-'))
12
-
13
- let sweepByAge: (sessions: readonly SessionScan[], options?: SweepOptions) => SweepResult
14
- let sweepToBudget: (sessions: readonly SessionScan[], options?: SweepOptions) => SweepResult
15
- let sweepStore: (options?: SweepOptions) => SweepResult
16
- let tidySession: (id: string, options?: { keepPrompt?: boolean }) => string[]
17
- let scanSessions: () => SessionScan[]
18
- let sessionDir: (id: string) => string
19
- let sessionBytes: (id: string) => number
20
- let sessionsRoot: () => string
21
-
22
- const DAY = 24 * 60 * 60 * 1000
23
- const BUDGET: RetentionBudget = { maxAgeMs: 30 * DAY, maxBytes: 10_000 }
24
-
25
- beforeAll(async () => {
26
- ;({ sweepByAge, sweepToBudget, sweepStore, tidySession } = await import('../retention'))
27
- ;({ scanSessions, sessionDir, sessionBytes, sessionsRoot } = await import('../store'))
28
- })
29
-
30
- beforeEach(() => {
31
- // Destructive cleanup must be provably confined to this file's mkdtemp home.
32
- if (!sessionsRoot().startsWith(tmpdir())) throw new Error('refusing to clean a non-tmp home')
33
- rmSync(sessionsRoot(), { recursive: true, force: true })
34
- delete process.env.ASTRALE_TELEMETRY_MAX_AGE_DAYS
35
- delete process.env.ASTRALE_TELEMETRY_MAX_BYTES
36
- })
37
-
38
- afterEach(() => {
39
- rmSync(sessionsRoot(), { recursive: true, force: true })
40
- })
41
-
42
- type Seed = { ageMs?: number; analyzed?: boolean; bytes?: number; events?: boolean }
43
-
44
- function seed(id: string, { ageMs = 0, analyzed = false, bytes = 0, events = true }: Seed): void {
45
- const dir = sessionDir(id)
46
- mkdirSync(dir, { recursive: true })
47
- writeFileSync(join(dir, 'meta.json'), JSON.stringify({ id, root: '/w', explicit: false }))
48
- if (bytes > 0) writeFileSync(join(dir, 'report.md'), 'x'.repeat(bytes))
49
- if (analyzed) {
50
- writeFileSync(
51
- join(dir, '.analyzed'),
52
- JSON.stringify({ analyzedAt: new Date().toISOString(), outcome: 'reported' }),
53
- )
54
- }
55
- if (!events) return
56
- writeFileSync(join(dir, 'events.jsonl'), '{}\n')
57
- const when = new Date(Date.now() - ageMs)
58
- utimesSync(join(dir, 'events.jsonl'), when, when)
59
- }
60
-
61
- const alive = (id: string): boolean => existsSync(sessionDir(id))
62
-
63
- describe('sweepByAge', () => {
64
- test('drops sessions past the age bound whether or not they were analyzed', () => {
65
- seed('old-analyzed', { ageMs: 40 * DAY, analyzed: true })
66
- seed('old-unanalyzed', { ageMs: 40 * DAY })
67
- seed('recent-analyzed', { ageMs: 2 * DAY, analyzed: true })
68
- seed('recent-unanalyzed', { ageMs: 2 * DAY })
69
-
70
- const { removed } = sweepByAge(scanSessions(), { budget: BUDGET })
71
-
72
- expect(removed.sort()).toEqual(['old-analyzed', 'old-unanalyzed'])
73
- expect(alive('recent-analyzed')).toBe(true)
74
- expect(alive('recent-unanalyzed')).toBe(true)
75
- })
76
-
77
- test('never touches a session with no events — that is the invocation running now', () => {
78
- seed('no-events-yet', { events: false })
79
- expect(sweepByAge(scanSessions(), { budget: BUDGET }).removed).toEqual([])
80
- expect(alive('no-events-yet')).toBe(true)
81
- })
82
-
83
- test('honours the removal cap so a backlog drains over several runs', () => {
84
- for (let i = 0; i < 5; i++) seed(`stale-${i}`, { ageMs: 40 * DAY, analyzed: true })
85
- expect(sweepByAge(scanSessions(), { budget: BUDGET, limit: 2 }).removed).toHaveLength(2)
86
- expect(scanSessions()).toHaveLength(3)
87
- })
88
-
89
- test('respects the configured age bound', () => {
90
- seed('week-old', { ageMs: 8 * DAY, analyzed: true })
91
- const strict: RetentionBudget = { ...BUDGET, maxAgeMs: 7 * DAY }
92
- expect(sweepByAge(scanSessions(), { budget: strict }).removed).toEqual(['week-old'])
93
- })
94
-
95
- test('protected ids survive the age bound', () => {
96
- seed('old-but-mine', { ageMs: 40 * DAY, analyzed: true })
97
- const options = { budget: BUDGET, protect: new Set(['old-but-mine']) }
98
- expect(sweepByAge(scanSessions(), options).removed).toEqual([])
99
- expect(alive('old-but-mine')).toBe(true)
100
- })
101
- })
102
-
103
- describe('sweepToBudget', () => {
104
- test('does nothing while the store fits', () => {
105
- seed('small', { ageMs: DAY, analyzed: true, bytes: 100 })
106
- expect(sweepToBudget(scanSessions(), { budget: BUDGET }).removed).toEqual([])
107
- expect(alive('small')).toBe(true)
108
- })
109
-
110
- test('evicts oldest-first until the store is back under budget', () => {
111
- seed('oldest', { ageMs: 5 * DAY, analyzed: true, bytes: 4_000 })
112
- seed('middle', { ageMs: 3 * DAY, analyzed: true, bytes: 4_000 })
113
- seed('newest', { ageMs: DAY, analyzed: true, bytes: 4_000 })
114
-
115
- // 3 × ~4 KB against a 5 KB bound: two must go, and the newest must stay.
116
- const { removed } = sweepToBudget(scanSessions(), { budget: { ...BUDGET, maxBytes: 5_000 } })
117
-
118
- expect(removed).toEqual(['oldest', 'middle'])
119
- expect(alive('newest')).toBe(true)
120
- })
121
-
122
- test('evicts analyzed sessions before unanalyzed ones of any age', () => {
123
- seed('unanalyzed-oldest', { ageMs: 9 * DAY, bytes: 6_000 })
124
- seed('analyzed-newest', { ageMs: DAY, analyzed: true, bytes: 6_000 })
125
-
126
- expect(sweepToBudget(scanSessions(), { budget: BUDGET }).removed).toEqual(['analyzed-newest'])
127
- expect(alive('unanalyzed-oldest')).toBe(true)
128
- })
129
-
130
- test('falls through to unanalyzed sessions when analyzed ones are not enough', () => {
131
- seed('unanalyzed-old', { ageMs: 9 * DAY, bytes: 6_000 })
132
- seed('analyzed-old', { ageMs: 8 * DAY, analyzed: true, bytes: 6_000 })
133
- seed('unanalyzed-new', { ageMs: DAY, bytes: 6_000 })
134
-
135
- const { removed } = sweepToBudget(scanSessions(), { budget: BUDGET })
136
-
137
- expect(removed).toEqual(['analyzed-old', 'unanalyzed-old'])
138
- expect(alive('unanalyzed-new')).toBe(true)
139
- })
140
-
141
- test('never evicts an open session, even when that leaves the store over budget', () => {
142
- // No age offset → inside IDLE_WINDOW_MS → open.
143
- seed('open-and-huge', { analyzed: true, bytes: 20_000 })
144
- expect(sweepToBudget(scanSessions(), { budget: BUDGET }).removed).toEqual([])
145
- expect(alive('open-and-huge')).toBe(true)
146
- })
147
-
148
- test('never evicts a protected session', () => {
149
- seed('just-analyzed', { ageMs: DAY, analyzed: true, bytes: 20_000 })
150
- const options = { budget: BUDGET, protect: new Set(['just-analyzed']) }
151
- expect(sweepToBudget(scanSessions(), options).removed).toEqual([])
152
- })
153
-
154
- test('counts nested analyzer output — a session is bounded by all it holds', () => {
155
- seed('nested', { ageMs: DAY, analyzed: true, bytes: 100 })
156
- mkdirSync(join(sessionDir('nested'), 'scratch'), { recursive: true })
157
- writeFileSync(join(sessionDir('nested'), 'scratch', 'dump.txt'), 'x'.repeat(5_000))
158
-
159
- expect(sessionBytes('nested')).toBeGreaterThan(5_000)
160
- })
161
- })
162
-
163
- describe('sweepStore', () => {
164
- test('applies age first, then trims what survives to the size bound', () => {
165
- seed('expired', { ageMs: 40 * DAY, analyzed: true, bytes: 6_000 })
166
- seed('fresh-big', { ageMs: 2 * DAY, analyzed: true, bytes: 6_000 })
167
- seed('fresh-small', { ageMs: DAY, analyzed: true, bytes: 100 })
168
-
169
- // Age alone frees 6 KB, leaving ~6.1 KB — already under the 10 KB bound,
170
- // so the size pass must find nothing left to do.
171
- expect(sweepStore({ budget: BUDGET }).removed).toEqual(['expired'])
172
- expect(alive('fresh-big')).toBe(true)
173
- expect(alive('fresh-small')).toBe(true)
174
- })
175
-
176
- test('reads its budget from the environment when none is passed', () => {
177
- process.env.ASTRALE_TELEMETRY_MAX_AGE_DAYS = '1'
178
- seed('two-days-old', { ageMs: 2 * DAY, analyzed: true })
179
- expect(sweepStore().removed).toEqual(['two-days-old'])
180
- })
181
- })
182
-
183
- describe('sweepStore tidying', () => {
184
- test('tidies analyzed survivors, so the bound applies to sessions already on disk', () => {
185
- seed('store-tidy', { ageMs: DAY, analyzed: true, bytes: 100 })
186
- writeFileSync(join(sessionDir('store-tidy'), 'calls.txt'), 'x'.repeat(50_000))
187
-
188
- sweepStore({ budget: BUDGET })
189
-
190
- expect(existsSync(join(sessionDir('store-tidy'), 'calls.txt'))).toBe(false)
191
- expect(existsSync(join(sessionDir('store-tidy'), 'report.md'))).toBe(true)
192
- })
193
-
194
- test('an unanalyzed session is left alone — its analyzer may still be running', () => {
195
- seed('store-pending', { ageMs: DAY, bytes: 100 })
196
- writeFileSync(join(sessionDir('store-pending'), 'analyzer-prompt.md'), '# in flight')
197
-
198
- sweepStore({ budget: BUDGET })
199
-
200
- expect(existsSync(join(sessionDir('store-pending'), 'analyzer-prompt.md'))).toBe(true)
201
- })
202
-
203
- test('scratch is tidied before the size bound is measured, not after', () => {
204
- // 60 KB of scratch against a 10 KB budget: tidying first brings the store
205
- // back under on its own, so nothing should be evicted.
206
- seed('store-bloated', { ageMs: DAY, analyzed: true, bytes: 100 })
207
- writeFileSync(join(sessionDir('store-bloated'), 'calls.txt'), 'x'.repeat(60_000))
208
-
209
- expect(sweepStore({ budget: BUDGET }).removed).toEqual([])
210
- expect(existsSync(sessionDir('store-bloated'))).toBe(true)
211
- })
212
-
213
- test('a failed analysis keeps its prompt through the sweep', () => {
214
- const dir = sessionDir('store-failed')
215
- seed('store-failed', { ageMs: DAY, analyzed: true })
216
- writeFileSync(
217
- join(dir, '.analyzed'),
218
- JSON.stringify({ analyzedAt: new Date().toISOString(), outcome: 'error', note: 'boom' }),
219
- )
220
- writeFileSync(join(dir, 'analyzer-prompt.md'), '# what we asked')
221
-
222
- sweepStore({ budget: BUDGET })
223
-
224
- expect(existsSync(join(dir, 'analyzer-prompt.md'))).toBe(true)
225
- })
226
- })
227
-
228
- describe('tidySession', () => {
229
- test('removes what the analyzer left behind, keeps the durable artifacts', () => {
230
- const dir = sessionDir('tidy-scratch')
231
- seed('tidy-scratch', { ageMs: DAY, analyzed: true, bytes: 100 })
232
- writeFileSync(join(dir, 'analyzer.log'), 'exit 0')
233
- writeFileSync(join(dir, 'analyzer-prompt.md'), '# prompt')
234
- // Scratch: the analyzer runs with Write in here and answers to nobody.
235
- writeFileSync(join(dir, 'calls.txt'), 'x'.repeat(50_000))
236
- mkdirSync(join(dir, 'notes'), { recursive: true })
237
- writeFileSync(join(dir, 'notes', 'draft.md'), 'scratch')
238
-
239
- const removed = tidySession('tidy-scratch')
240
-
241
- expect(removed.sort()).toEqual(['analyzer-prompt.md', 'calls.txt', 'notes'])
242
- for (const keep of ['meta.json', 'events.jsonl', 'report.md', '.analyzed', 'analyzer.log']) {
243
- expect(existsSync(join(dir, keep))).toBe(true)
244
- }
245
- expect(existsSync(join(dir, 'notes'))).toBe(false)
246
- })
247
-
248
- test('keepPrompt spares the prompt — the failing case is when it matters', () => {
249
- const dir = sessionDir('tidy-failed')
250
- seed('tidy-failed', { ageMs: DAY, analyzed: true })
251
- writeFileSync(join(dir, 'analyzer-prompt.md'), '# prompt')
252
- writeFileSync(join(dir, 'scratch.json'), '{}')
253
-
254
- expect(tidySession('tidy-failed', { keepPrompt: true })).toEqual(['scratch.json'])
255
- expect(existsSync(join(dir, 'analyzer-prompt.md'))).toBe(true)
256
- })
257
-
258
- test('a session with nothing to tidy is left exactly as it was', () => {
259
- seed('tidy-clean', { ageMs: DAY, analyzed: true, bytes: 10 })
260
- expect(tidySession('tidy-clean')).toEqual([])
261
- expect(existsSync(sessionDir('tidy-clean'))).toBe(true)
262
- })
263
-
264
- test('a missing session directory is a no-op, not an error', () => {
265
- expect(tidySession('tidy-absent')).toEqual([])
266
- })
267
- })
@@ -1,166 +0,0 @@
1
- import { afterEach, beforeAll, beforeEach, describe, expect, test } from 'bun:test'
2
- import {
3
- existsSync,
4
- mkdirSync,
5
- mkdtempSync,
6
- readFileSync,
7
- rmSync,
8
- utimesSync,
9
- writeFileSync,
10
- } from 'node:fs'
11
- import { tmpdir } from 'node:os'
12
- import { join } from 'node:path'
13
-
14
- import type { ResolvedSession } from '../session'
15
- import type { SessionMeta } from '../types'
16
-
17
- /** Seed an ambient session on disk with a chosen id + last-activity age. */
18
- function seedAmbient(id: string, root: string, ageMs: number): void {
19
- mkdirSync(sessionDir(id), { recursive: true })
20
- const meta: SessionMeta = { id, root, explicit: false, createdAt: new Date().toISOString() }
21
- writeFileSync(metaPath(id), JSON.stringify(meta))
22
- writeFileSync(eventsPath(id), '{}\n')
23
- const when = new Date(Date.now() - ageMs)
24
- utimesSync(eventsPath(id), when, when)
25
- }
26
-
27
- // Set ASTRALE_HOME before the store/paths singleton is first evaluated (below,
28
- // via dynamic import). All on-disk access goes through the store helpers so it
29
- // stays consistent whichever home the singleton captured.
30
- process.env.ASTRALE_HOME = mkdtempSync(join(tmpdir(), 'astrale-tele-session-'))
31
-
32
- let resolveSession: (cwd: string) => ResolvedSession
33
- let ensureSession: (cwd: string) => ResolvedSession
34
- let eventsPath: (id: string) => string
35
- let metaPath: (id: string) => string
36
- let sessionDir: (id: string) => string
37
- let sessionsRoot: () => string
38
- let IDLE_WINDOW_MS: number
39
-
40
- let work: string
41
-
42
- beforeAll(async () => {
43
- const session = await import('../session')
44
- const store = await import('../store')
45
- resolveSession = session.resolveSession
46
- ensureSession = session.ensureSession
47
- eventsPath = store.eventsPath
48
- metaPath = store.metaPath
49
- sessionDir = store.sessionDir
50
- sessionsRoot = store.sessionsRoot
51
- IDLE_WINDOW_MS = store.IDLE_WINDOW_MS
52
- })
53
-
54
- beforeEach(() => {
55
- if (!sessionsRoot().startsWith(tmpdir())) throw new Error('refusing to clean a non-tmp home')
56
- rmSync(sessionsRoot(), { recursive: true, force: true })
57
- delete process.env.ASTRALE_SESSION
58
- work = mkdtempSync(join(tmpdir(), 'astrale-tele-work-'))
59
- })
60
-
61
- afterEach(() => {
62
- rmSync(work, { recursive: true, force: true })
63
- })
64
-
65
- describe('resolveSession — ambient', () => {
66
- test('reuses an open ambient session bucketed to the same root', () => {
67
- // An old-stamp id proves reuse: a fresh mint would carry the current minute.
68
- const existing = 'amb-deadbeef-200001010000'
69
- seedAmbient(existing, work, 5 * 60_000)
70
-
71
- const r = resolveSession(work)
72
-
73
- expect(r).toEqual({ id: existing, root: work, explicit: false })
74
- })
75
-
76
- test('mints a fresh session once the previous one is idle', () => {
77
- const existing = 'amb-deadbeef-200001010000'
78
- seedAmbient(existing, work, IDLE_WINDOW_MS + 60_000)
79
-
80
- const r = resolveSession(work)
81
-
82
- expect(r.id).not.toBe(existing)
83
- expect(r.explicit).toBe(false)
84
- expect(r.id).toMatch(/^amb-[0-9a-f]{8}-\d{12}$/)
85
- })
86
-
87
- test('does not reuse a session bucketed to a different root', () => {
88
- const existing = 'amb-deadbeef-200001010000'
89
- seedAmbient(existing, work, 5 * 60_000)
90
-
91
- const other = mkdtempSync(join(tmpdir(), 'astrale-tele-other-'))
92
- try {
93
- expect(resolveSession(other).id).not.toBe(existing)
94
- } finally {
95
- rmSync(other, { recursive: true, force: true })
96
- }
97
- })
98
-
99
- test('ensureSession creates then reuses one session across calls', () => {
100
- const first = ensureSession(work)
101
- const second = ensureSession(work)
102
- expect(second.id).toBe(first.id)
103
- expect(first.id).toMatch(/^amb-[0-9a-f]{8}-\d{12}$/)
104
- })
105
- })
106
-
107
- describe('resolveSession — root bucketing', () => {
108
- test('buckets by the nearest .git ancestor, not cwd', () => {
109
- mkdirSync(join(work, '.git'), { recursive: true })
110
- const deep = join(work, 'sub', 'deep')
111
- mkdirSync(deep, { recursive: true })
112
-
113
- expect(resolveSession(deep).root).toBe(work)
114
- })
115
-
116
- test('falls back to cwd when there is no git root', () => {
117
- expect(resolveSession(work).root).toBe(work)
118
- })
119
- })
120
-
121
- describe('resolveSession — explicit ASTRALE_SESSION', () => {
122
- test('honors an explicit id, sanitizing unsafe chars and capping at 64', () => {
123
- process.env.ASTRALE_SESSION = 'my/weird:sess!!'
124
- const r = resolveSession(work)
125
- expect(r).toEqual({ id: 'myweirdsess', root: work, explicit: true })
126
-
127
- process.env.ASTRALE_SESSION = 'a'.repeat(80)
128
- expect(resolveSession(work).id).toBe('a'.repeat(64))
129
- })
130
-
131
- test('falls through to ambient when the id is empty after sanitizing', () => {
132
- process.env.ASTRALE_SESSION = '///'
133
- const r = resolveSession(work)
134
- expect(r.explicit).toBe(false)
135
- expect(r.id).toMatch(/^amb-/)
136
- })
137
- })
138
-
139
- describe('ensureSession', () => {
140
- test('creates the session dir and a readable meta.json once', () => {
141
- const r = ensureSession(work)
142
-
143
- expect(existsSync(metaPath(r.id))).toBe(true)
144
- const meta = JSON.parse(readFileSync(metaPath(r.id), 'utf-8')) as SessionMeta
145
- expect(meta.id).toBe(r.id)
146
- expect(meta.root).toBe(work)
147
- expect(meta.explicit).toBe(false)
148
- expect(typeof meta.createdAt).toBe('string')
149
- })
150
- })
151
-
152
- describe('analyzed sessions are never reused', () => {
153
- test('an open ambient session with an .analyzed marker gets a fresh mint', async () => {
154
- const { markerPath } = await import('../store')
155
- const root = join(work, 'analyzed-root')
156
- mkdirSync(root, { recursive: true })
157
- seedAmbient('amb-analyzed-open', root, 0) // fresh mtime → open
158
- writeFileSync(
159
- markerPath('amb-analyzed-open'),
160
- JSON.stringify({ analyzedAt: new Date().toISOString(), outcome: 'reported' }),
161
- )
162
- const resolved = resolveSession(root)
163
- expect(resolved.id).not.toBe('amb-analyzed-open')
164
- expect(resolved.id.startsWith('amb-')).toBe(true)
165
- })
166
- })