@build-astron-co/nimbus 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/bin/nimbus +26 -10
  2. package/bin/nimbus.cmd +41 -0
  3. package/bin/nimbus.mjs +70 -0
  4. package/completions/nimbus.bash +38 -0
  5. package/completions/nimbus.fish +48 -0
  6. package/completions/nimbus.zsh +81 -0
  7. package/dist/src/agent/compaction-agent.js +215 -0
  8. package/dist/src/agent/context-manager.js +385 -0
  9. package/dist/src/agent/context.js +322 -0
  10. package/dist/src/agent/deploy-preview.js +395 -0
  11. package/dist/src/agent/expand-files.js +95 -0
  12. package/dist/src/agent/index.js +18 -0
  13. package/dist/src/agent/loop.js +1535 -0
  14. package/dist/src/agent/modes.js +347 -0
  15. package/dist/src/agent/permissions.js +396 -0
  16. package/dist/src/agent/subagents/base.js +67 -0
  17. package/dist/src/agent/subagents/cost.js +45 -0
  18. package/dist/src/agent/subagents/explore.js +36 -0
  19. package/dist/src/agent/subagents/general.js +41 -0
  20. package/dist/src/agent/subagents/index.js +88 -0
  21. package/dist/src/agent/subagents/infra.js +52 -0
  22. package/dist/src/agent/subagents/security.js +60 -0
  23. package/dist/src/agent/system-prompt.js +860 -0
  24. package/dist/src/app.js +152 -0
  25. package/dist/src/audit/activity-log.js +209 -0
  26. package/dist/src/audit/compliance-checker.js +419 -0
  27. package/dist/src/audit/cost-tracker.js +231 -0
  28. package/dist/src/audit/index.js +10 -0
  29. package/dist/src/audit/security-scanner.js +490 -0
  30. package/dist/src/auth/guard.js +64 -0
  31. package/dist/src/auth/index.js +19 -0
  32. package/dist/src/auth/keychain.js +79 -0
  33. package/dist/src/auth/oauth.js +389 -0
  34. package/dist/src/auth/providers.js +415 -0
  35. package/dist/src/auth/sso.js +87 -0
  36. package/dist/src/auth/store.js +424 -0
  37. package/dist/src/auth/types.js +5 -0
  38. package/dist/src/cli/index.js +8 -0
  39. package/dist/src/cli/init.js +1048 -0
  40. package/dist/src/cli/openapi-spec.js +346 -0
  41. package/dist/src/cli/run.js +505 -0
  42. package/dist/src/cli/serve-auth.js +56 -0
  43. package/dist/src/cli/serve.js +432 -0
  44. package/dist/src/cli/web.js +50 -0
  45. package/dist/src/cli.js +1574 -0
  46. package/dist/src/clients/core-engine-client.js +156 -0
  47. package/dist/src/clients/enterprise-client.js +246 -0
  48. package/dist/src/clients/generator-client.js +219 -0
  49. package/dist/src/clients/git-client.js +367 -0
  50. package/dist/src/clients/github-client.js +229 -0
  51. package/dist/src/clients/helm-client.js +299 -0
  52. package/dist/src/clients/index.js +18 -0
  53. package/dist/src/clients/k8s-client.js +270 -0
  54. package/dist/src/clients/llm-client.js +119 -0
  55. package/dist/src/clients/rest-client.js +104 -0
  56. package/dist/src/clients/service-discovery.js +35 -0
  57. package/dist/src/clients/terraform-client.js +302 -0
  58. package/dist/src/clients/tools-client.js +1227 -0
  59. package/dist/src/clients/ws-client.js +93 -0
  60. package/dist/src/commands/alias.js +91 -0
  61. package/dist/src/commands/analyze/index.js +313 -0
  62. package/dist/src/commands/apply/helm.js +375 -0
  63. package/dist/src/commands/apply/index.js +176 -0
  64. package/dist/src/commands/apply/k8s.js +350 -0
  65. package/dist/src/commands/apply/terraform.js +465 -0
  66. package/dist/src/commands/ask.js +137 -0
  67. package/dist/src/commands/audit/index.js +322 -0
  68. package/dist/src/commands/auth-cloud.js +345 -0
  69. package/dist/src/commands/auth-list.js +112 -0
  70. package/dist/src/commands/auth-profile.js +104 -0
  71. package/dist/src/commands/auth-refresh.js +161 -0
  72. package/dist/src/commands/auth-status.js +122 -0
  73. package/dist/src/commands/aws/ec2.js +402 -0
  74. package/dist/src/commands/aws/iam.js +304 -0
  75. package/dist/src/commands/aws/index.js +108 -0
  76. package/dist/src/commands/aws/lambda.js +317 -0
  77. package/dist/src/commands/aws/rds.js +345 -0
  78. package/dist/src/commands/aws/s3.js +346 -0
  79. package/dist/src/commands/aws/vpc.js +302 -0
  80. package/dist/src/commands/aws-discover.js +413 -0
  81. package/dist/src/commands/aws-terraform.js +618 -0
  82. package/dist/src/commands/azure/aks.js +305 -0
  83. package/dist/src/commands/azure/functions.js +200 -0
  84. package/dist/src/commands/azure/index.js +93 -0
  85. package/dist/src/commands/azure/storage.js +378 -0
  86. package/dist/src/commands/azure/vm.js +291 -0
  87. package/dist/src/commands/billing/index.js +224 -0
  88. package/dist/src/commands/chat.js +259 -0
  89. package/dist/src/commands/completions.js +255 -0
  90. package/dist/src/commands/config.js +291 -0
  91. package/dist/src/commands/cost/cloud-cost-estimator.js +211 -0
  92. package/dist/src/commands/cost/estimator.js +73 -0
  93. package/dist/src/commands/cost/index.js +625 -0
  94. package/dist/src/commands/cost/parsers/terraform.js +234 -0
  95. package/dist/src/commands/cost/parsers/types.js +4 -0
  96. package/dist/src/commands/cost/pricing/aws.js +501 -0
  97. package/dist/src/commands/cost/pricing/azure.js +462 -0
  98. package/dist/src/commands/cost/pricing/gcp.js +359 -0
  99. package/dist/src/commands/cost/pricing/index.js +24 -0
  100. package/dist/src/commands/demo.js +196 -0
  101. package/dist/src/commands/deploy.js +215 -0
  102. package/dist/src/commands/doctor.js +1291 -0
  103. package/dist/src/commands/drift/index.js +674 -0
  104. package/dist/src/commands/explain.js +235 -0
  105. package/dist/src/commands/export.js +120 -0
  106. package/dist/src/commands/feedback.js +319 -0
  107. package/dist/src/commands/fix.js +263 -0
  108. package/dist/src/commands/fs/index.js +338 -0
  109. package/dist/src/commands/gcp/compute.js +266 -0
  110. package/dist/src/commands/gcp/functions.js +221 -0
  111. package/dist/src/commands/gcp/gke.js +357 -0
  112. package/dist/src/commands/gcp/iam.js +295 -0
  113. package/dist/src/commands/gcp/index.js +105 -0
  114. package/dist/src/commands/gcp/storage.js +232 -0
  115. package/dist/src/commands/generate-helm.js +1026 -0
  116. package/dist/src/commands/generate-k8s.js +1263 -0
  117. package/dist/src/commands/generate-terraform.js +1058 -0
  118. package/dist/src/commands/gh/index.js +663 -0
  119. package/dist/src/commands/git/index.js +1208 -0
  120. package/dist/src/commands/helm/index.js +985 -0
  121. package/dist/src/commands/help.js +639 -0
  122. package/dist/src/commands/history.js +120 -0
  123. package/dist/src/commands/import.js +782 -0
  124. package/dist/src/commands/incident.js +144 -0
  125. package/dist/src/commands/index.js +109 -0
  126. package/dist/src/commands/init.js +955 -0
  127. package/dist/src/commands/k8s/index.js +979 -0
  128. package/dist/src/commands/login.js +588 -0
  129. package/dist/src/commands/logout.js +61 -0
  130. package/dist/src/commands/logs.js +160 -0
  131. package/dist/src/commands/onboarding.js +382 -0
  132. package/dist/src/commands/pipeline.js +153 -0
  133. package/dist/src/commands/plan/display.js +216 -0
  134. package/dist/src/commands/plan/index.js +525 -0
  135. package/dist/src/commands/plugin.js +325 -0
  136. package/dist/src/commands/preview.js +356 -0
  137. package/dist/src/commands/profile.js +297 -0
  138. package/dist/src/commands/questionnaire.js +1021 -0
  139. package/dist/src/commands/resume.js +35 -0
  140. package/dist/src/commands/rollback.js +259 -0
  141. package/dist/src/commands/rollout.js +74 -0
  142. package/dist/src/commands/runbook.js +307 -0
  143. package/dist/src/commands/schedule.js +202 -0
  144. package/dist/src/commands/status.js +213 -0
  145. package/dist/src/commands/team/index.js +309 -0
  146. package/dist/src/commands/team-context.js +200 -0
  147. package/dist/src/commands/template.js +204 -0
  148. package/dist/src/commands/tf/index.js +989 -0
  149. package/dist/src/commands/upgrade.js +515 -0
  150. package/dist/src/commands/usage/index.js +118 -0
  151. package/dist/src/commands/version.js +145 -0
  152. package/dist/src/commands/watch.js +127 -0
  153. package/dist/src/compat/index.js +2 -0
  154. package/dist/src/compat/runtime.js +10 -0
  155. package/dist/src/compat/sqlite.js +144 -0
  156. package/dist/src/config/index.js +6 -0
  157. package/dist/src/config/manager.js +469 -0
  158. package/dist/src/config/mode-store.js +57 -0
  159. package/dist/src/config/profiles.js +66 -0
  160. package/dist/src/config/safety-policy.js +251 -0
  161. package/dist/src/config/schema.js +107 -0
  162. package/dist/src/config/types.js +311 -0
  163. package/dist/src/config/workspace-state.js +38 -0
  164. package/dist/src/context/context-db.js +138 -0
  165. package/dist/src/demo/index.js +295 -0
  166. package/dist/src/demo/scenarios/full-journey.js +226 -0
  167. package/dist/src/demo/scenarios/getting-started.js +124 -0
  168. package/dist/src/demo/scenarios/helm-release.js +334 -0
  169. package/dist/src/demo/scenarios/k8s-deployment.js +190 -0
  170. package/dist/src/demo/scenarios/terraform-vpc.js +167 -0
  171. package/dist/src/demo/types.js +6 -0
  172. package/dist/src/engine/cost-estimator.js +334 -0
  173. package/dist/src/engine/diagram-generator.js +192 -0
  174. package/dist/src/engine/drift-detector.js +688 -0
  175. package/dist/src/engine/executor.js +832 -0
  176. package/dist/src/engine/index.js +39 -0
  177. package/dist/src/engine/orchestrator.js +436 -0
  178. package/dist/src/engine/planner.js +616 -0
  179. package/dist/src/engine/safety.js +609 -0
  180. package/dist/src/engine/verifier.js +664 -0
  181. package/dist/src/enterprise/audit.js +241 -0
  182. package/dist/src/enterprise/auth.js +189 -0
  183. package/dist/src/enterprise/billing.js +512 -0
  184. package/dist/src/enterprise/index.js +16 -0
  185. package/dist/src/enterprise/teams.js +315 -0
  186. package/dist/src/generator/best-practices.js +1375 -0
  187. package/dist/src/generator/helm.js +495 -0
  188. package/dist/src/generator/index.js +11 -0
  189. package/dist/src/generator/intent-parser.js +420 -0
  190. package/dist/src/generator/kubernetes.js +773 -0
  191. package/dist/src/generator/terraform.js +1472 -0
  192. package/dist/src/history/index.js +6 -0
  193. package/dist/src/history/manager.js +199 -0
  194. package/dist/src/history/types.js +6 -0
  195. package/dist/src/hooks/config.js +318 -0
  196. package/dist/src/hooks/engine.js +317 -0
  197. package/dist/src/hooks/index.js +2 -0
  198. package/dist/src/llm/auth-bridge.js +157 -0
  199. package/dist/src/llm/circuit-breaker.js +116 -0
  200. package/dist/src/llm/config-loader.js +172 -0
  201. package/dist/src/llm/cost-calculator.js +137 -0
  202. package/dist/src/llm/index.js +7 -0
  203. package/dist/src/llm/model-aliases.js +99 -0
  204. package/dist/src/llm/provider-registry.js +57 -0
  205. package/dist/src/llm/providers/anthropic.js +430 -0
  206. package/dist/src/llm/providers/bedrock.js +409 -0
  207. package/dist/src/llm/providers/google.js +344 -0
  208. package/dist/src/llm/providers/ollama.js +661 -0
  209. package/dist/src/llm/providers/openai-compatible.js +289 -0
  210. package/dist/src/llm/providers/openai.js +284 -0
  211. package/dist/src/llm/providers/openrouter.js +293 -0
  212. package/dist/src/llm/router.js +844 -0
  213. package/dist/src/llm/types.js +69 -0
  214. package/dist/src/lsp/client.js +239 -0
  215. package/dist/src/lsp/languages.js +95 -0
  216. package/dist/src/lsp/manager.js +243 -0
  217. package/dist/src/mcp/client.js +289 -0
  218. package/dist/src/mcp/index.js +5 -0
  219. package/dist/src/mcp/manager.js +113 -0
  220. package/dist/src/nimbus.js +212 -0
  221. package/dist/src/plugins/index.js +13 -0
  222. package/dist/src/plugins/loader.js +280 -0
  223. package/dist/src/plugins/manager.js +282 -0
  224. package/dist/src/plugins/types.js +23 -0
  225. package/dist/src/scanners/cicd-scanner.js +230 -0
  226. package/dist/src/scanners/cloud-scanner.js +415 -0
  227. package/dist/src/scanners/framework-scanner.js +430 -0
  228. package/dist/src/scanners/iac-scanner.js +350 -0
  229. package/dist/src/scanners/index.js +454 -0
  230. package/dist/src/scanners/language-scanner.js +258 -0
  231. package/dist/src/scanners/package-manager-scanner.js +252 -0
  232. package/dist/src/scanners/types.js +6 -0
  233. package/dist/src/sessions/manager.js +395 -0
  234. package/dist/src/sessions/types.js +4 -0
  235. package/dist/src/sharing/sync.js +238 -0
  236. package/dist/src/sharing/viewer.js +131 -0
  237. package/dist/src/snapshots/index.js +1 -0
  238. package/dist/src/snapshots/manager.js +432 -0
  239. package/dist/src/state/artifacts.js +94 -0
  240. package/dist/src/state/audit.js +73 -0
  241. package/dist/src/state/billing.js +126 -0
  242. package/dist/src/state/checkpoints.js +81 -0
  243. package/dist/src/state/config.js +58 -0
  244. package/dist/src/state/conversations.js +7 -0
  245. package/dist/src/state/credentials.js +96 -0
  246. package/dist/src/state/db.js +53 -0
  247. package/dist/src/state/index.js +23 -0
  248. package/dist/src/state/messages.js +76 -0
  249. package/dist/src/state/projects.js +92 -0
  250. package/dist/src/state/schema.js +233 -0
  251. package/dist/src/state/sessions.js +79 -0
  252. package/dist/src/state/teams.js +131 -0
  253. package/dist/src/telemetry.js +91 -0
  254. package/dist/src/tools/aws-ops.js +747 -0
  255. package/dist/src/tools/azure-ops.js +491 -0
  256. package/dist/src/tools/file-ops.js +451 -0
  257. package/dist/src/tools/gcp-ops.js +559 -0
  258. package/dist/src/tools/git-ops.js +557 -0
  259. package/dist/src/tools/github-ops.js +460 -0
  260. package/dist/src/tools/helm-ops.js +634 -0
  261. package/dist/src/tools/index.js +16 -0
  262. package/dist/src/tools/k8s-ops.js +579 -0
  263. package/dist/src/tools/schemas/converter.js +129 -0
  264. package/dist/src/tools/schemas/devops.js +3319 -0
  265. package/dist/src/tools/schemas/index.js +19 -0
  266. package/dist/src/tools/schemas/standard.js +966 -0
  267. package/dist/src/tools/schemas/types.js +409 -0
  268. package/dist/src/tools/spawn-exec.js +109 -0
  269. package/dist/src/tools/terraform-ops.js +627 -0
  270. package/dist/src/types/config.js +1 -0
  271. package/dist/src/types/drift.js +4 -0
  272. package/dist/src/types/enterprise.js +5 -0
  273. package/dist/src/types/index.js +14 -0
  274. package/dist/src/types/plan.js +1 -0
  275. package/dist/src/types/request.js +1 -0
  276. package/dist/src/types/response.js +1 -0
  277. package/dist/src/types/service.js +1 -0
  278. package/dist/src/ui/App.js +1672 -0
  279. package/dist/src/ui/DeployPreview.js +60 -0
  280. package/dist/src/ui/FileDiffModal.js +108 -0
  281. package/dist/src/ui/Header.js +46 -0
  282. package/dist/src/ui/HelpModal.js +9 -0
  283. package/dist/src/ui/InputBox.js +408 -0
  284. package/dist/src/ui/MessageList.js +795 -0
  285. package/dist/src/ui/PermissionPrompt.js +72 -0
  286. package/dist/src/ui/StatusBar.js +109 -0
  287. package/dist/src/ui/TerminalPane.js +31 -0
  288. package/dist/src/ui/ToolCallDisplay.js +303 -0
  289. package/dist/src/ui/TreePane.js +83 -0
  290. package/dist/src/ui/chat-ui.js +721 -0
  291. package/dist/src/ui/index.js +11 -0
  292. package/dist/src/ui/ink/index.js +1325 -0
  293. package/dist/src/ui/streaming.js +137 -0
  294. package/dist/src/ui/theme.js +78 -0
  295. package/dist/src/ui/types.js +7 -0
  296. package/dist/src/utils/analytics.js +61 -0
  297. package/dist/src/utils/cost-warning.js +25 -0
  298. package/dist/src/utils/env.js +42 -0
  299. package/dist/src/utils/errors.js +54 -0
  300. package/dist/src/utils/event-bus.js +22 -0
  301. package/dist/src/utils/index.js +16 -0
  302. package/dist/src/utils/logger.js +150 -0
  303. package/dist/src/utils/rate-limiter.js +90 -0
  304. package/dist/src/utils/service-auth.js +36 -0
  305. package/dist/src/utils/validation.js +39 -0
  306. package/dist/src/version.js +3 -0
  307. package/dist/src/watcher/index.js +192 -0
  308. package/dist/src/wizard/approval.js +275 -0
  309. package/dist/src/wizard/index.js +13 -0
  310. package/dist/src/wizard/prompts.js +273 -0
  311. package/dist/src/wizard/types.js +4 -0
  312. package/dist/src/wizard/ui.js +453 -0
  313. package/dist/src/wizard/wizard.js +227 -0
  314. package/package.json +31 -23
  315. package/src/__tests__/alias.test.ts +133 -0
  316. package/src/__tests__/app.test.ts +1 -1
  317. package/src/__tests__/audit.test.ts +1 -1
  318. package/src/__tests__/circuit-breaker.test.ts +1 -1
  319. package/src/__tests__/cli-run.test.ts +237 -1
  320. package/src/__tests__/compat-sqlite.test.ts +68 -0
  321. package/src/__tests__/context-manager.test.ts +131 -1
  322. package/src/__tests__/context.test.ts +1 -1
  323. package/src/__tests__/devops-terminal-gaps.test.ts +718 -0
  324. package/src/__tests__/doctor.test.ts +48 -0
  325. package/src/__tests__/enterprise.test.ts +1 -1
  326. package/src/__tests__/export.test.ts +236 -0
  327. package/src/__tests__/gap-11-18-20.test.ts +958 -0
  328. package/src/__tests__/generator.test.ts +1 -1
  329. package/src/__tests__/helm-streaming.test.ts +127 -0
  330. package/src/__tests__/hooks.test.ts +1 -1
  331. package/src/__tests__/incident.test.ts +179 -0
  332. package/src/__tests__/init.test.ts +55 -4
  333. package/src/__tests__/intent-parser.test.ts +1 -1
  334. package/src/__tests__/llm-router.test.ts +1 -1
  335. package/src/__tests__/logs.test.ts +107 -0
  336. package/src/__tests__/loop-errors.test.ts +244 -0
  337. package/src/__tests__/lsp.test.ts +1 -1
  338. package/src/__tests__/modes.test.ts +1 -1
  339. package/src/__tests__/perf-optimizations.test.ts +847 -0
  340. package/src/__tests__/permissions.test.ts +1 -1
  341. package/src/__tests__/pipeline.test.ts +50 -0
  342. package/src/__tests__/polish-phase3.test.ts +340 -0
  343. package/src/__tests__/profile.test.ts +237 -0
  344. package/src/__tests__/rollback.test.ts +83 -0
  345. package/src/__tests__/runbook.test.ts +219 -0
  346. package/src/__tests__/schedule.test.ts +206 -0
  347. package/src/__tests__/serve.test.ts +1 -1
  348. package/src/__tests__/sessions.test.ts +96 -1
  349. package/src/__tests__/sharing.test.ts +53 -1
  350. package/src/__tests__/snapshots.test.ts +1 -1
  351. package/src/__tests__/standalone-migration.test.ts +199 -0
  352. package/src/__tests__/state-db.test.ts +1 -1
  353. package/src/__tests__/status.test.ts +158 -0
  354. package/src/__tests__/stream-with-tools.test.ts +71 -25
  355. package/src/__tests__/subagents.test.ts +1 -1
  356. package/src/__tests__/system-prompt.test.ts +82 -3
  357. package/src/__tests__/terminal-gap-v2.test.ts +395 -0
  358. package/src/__tests__/terminal-parity.test.ts +393 -0
  359. package/src/__tests__/tf-apply.test.ts +187 -0
  360. package/src/__tests__/tool-converter.test.ts +1 -1
  361. package/src/__tests__/tool-schemas.test.ts +209 -4
  362. package/src/__tests__/tools.test.ts +4 -3
  363. package/src/__tests__/version-json.test.ts +184 -0
  364. package/src/__tests__/version.test.ts +1 -1
  365. package/src/__tests__/watch.test.ts +129 -0
  366. package/src/agent/compaction-agent.ts +40 -1
  367. package/src/agent/context-manager.ts +67 -3
  368. package/src/agent/deploy-preview.ts +62 -1
  369. package/src/agent/expand-files.ts +108 -0
  370. package/src/agent/loop.ts +1312 -31
  371. package/src/agent/permissions.ts +51 -4
  372. package/src/agent/system-prompt.ts +573 -19
  373. package/src/app.ts +58 -0
  374. package/src/audit/security-scanner.ts +45 -0
  375. package/src/auth/keychain.ts +82 -0
  376. package/src/auth/oauth.ts +15 -5
  377. package/src/cli/init.ts +378 -5
  378. package/src/cli/run.ts +407 -16
  379. package/src/cli/serve.ts +78 -1
  380. package/src/cli/web.ts +10 -6
  381. package/src/cli.ts +312 -1
  382. package/src/clients/service-discovery.ts +30 -25
  383. package/src/commands/alias.ts +100 -0
  384. package/src/commands/audit/index.ts +121 -2
  385. package/src/commands/auth-cloud.ts +113 -0
  386. package/src/commands/auth-refresh.ts +187 -0
  387. package/src/commands/aws-discover.ts +144 -251
  388. package/src/commands/aws-terraform.ts +68 -118
  389. package/src/commands/chat.ts +9 -3
  390. package/src/commands/completions.ts +268 -0
  391. package/src/commands/config.ts +26 -0
  392. package/src/commands/cost/index.ts +218 -2
  393. package/src/commands/deploy.ts +260 -0
  394. package/src/commands/doctor.ts +744 -152
  395. package/src/commands/drift/index.ts +371 -23
  396. package/src/commands/export.ts +146 -0
  397. package/src/commands/generate-k8s.ts +9 -61
  398. package/src/commands/generate-terraform.ts +191 -449
  399. package/src/commands/help.ts +212 -36
  400. package/src/commands/history.ts +8 -1
  401. package/src/commands/incident.ts +166 -0
  402. package/src/commands/init.ts +5 -0
  403. package/src/commands/login.ts +86 -1
  404. package/src/commands/logs.ts +167 -0
  405. package/src/commands/onboarding.ts +211 -34
  406. package/src/commands/pipeline.ts +186 -0
  407. package/src/commands/plugin.ts +398 -0
  408. package/src/commands/profile.ts +342 -0
  409. package/src/commands/questionnaire.ts +0 -98
  410. package/src/commands/resume.ts +26 -34
  411. package/src/commands/rollback.ts +315 -0
  412. package/src/commands/rollout.ts +88 -0
  413. package/src/commands/runbook.ts +346 -0
  414. package/src/commands/schedule.ts +236 -0
  415. package/src/commands/status.ts +252 -0
  416. package/src/commands/team-context.ts +220 -0
  417. package/src/commands/template.ts +58 -57
  418. package/src/commands/tf/index.ts +70 -11
  419. package/src/commands/upgrade.ts +57 -0
  420. package/src/commands/version.ts +54 -50
  421. package/src/commands/watch.ts +153 -0
  422. package/src/compat/runtime.ts +1 -1
  423. package/src/compat/sqlite.ts +75 -5
  424. package/src/config/mode-store.ts +62 -0
  425. package/src/config/profiles.ts +84 -0
  426. package/src/config/types.ts +83 -1
  427. package/src/config/workspace-state.ts +53 -0
  428. package/src/engine/cost-estimator.ts +52 -10
  429. package/src/engine/executor.ts +33 -2
  430. package/src/engine/planner.ts +68 -1
  431. package/src/generator/terraform.ts +8 -0
  432. package/src/history/manager.ts +2 -74
  433. package/src/hooks/engine.ts +5 -4
  434. package/src/llm/cost-calculator.ts +2 -2
  435. package/src/llm/providers/anthropic.ts +50 -21
  436. package/src/llm/router.ts +76 -7
  437. package/src/lsp/languages.ts +3 -0
  438. package/src/lsp/manager.ts +21 -5
  439. package/src/nimbus.ts +37 -18
  440. package/src/sessions/manager.ts +108 -1
  441. package/src/sharing/sync.ts +4 -0
  442. package/src/sharing/viewer.ts +66 -0
  443. package/src/tools/file-ops.ts +22 -0
  444. package/src/tools/schemas/devops.ts +3007 -117
  445. package/src/tools/schemas/standard.ts +5 -1
  446. package/src/tools/schemas/types.ts +31 -1
  447. package/src/tools/spawn-exec.ts +148 -0
  448. package/src/ui/App.tsx +1183 -66
  449. package/src/ui/DeployPreview.tsx +62 -57
  450. package/src/ui/FileDiffModal.tsx +162 -0
  451. package/src/ui/Header.tsx +87 -24
  452. package/src/ui/HelpModal.tsx +57 -0
  453. package/src/ui/InputBox.tsx +163 -10
  454. package/src/ui/MessageList.tsx +487 -40
  455. package/src/ui/PermissionPrompt.tsx +17 -5
  456. package/src/ui/StatusBar.tsx +122 -3
  457. package/src/ui/TerminalPane.tsx +84 -0
  458. package/src/ui/ToolCallDisplay.tsx +252 -18
  459. package/src/ui/TreePane.tsx +132 -0
  460. package/src/ui/chat-ui.ts +41 -44
  461. package/src/ui/ink/index.ts +771 -38
  462. package/src/ui/streaming.ts +1 -1
  463. package/src/ui/theme.ts +104 -0
  464. package/src/ui/types.ts +18 -0
  465. package/src/version.ts +1 -1
  466. package/src/watcher/index.ts +66 -15
  467. package/src/wizard/types.ts +1 -0
  468. package/src/wizard/ui.ts +1 -1
  469. package/tsconfig.json +2 -2
@@ -0,0 +1,395 @@
1
+ /**
2
+ * Session Manager — Multi-Session Lifecycle
3
+ *
4
+ * Manages creation, switching, suspension, and destruction of parallel
5
+ * agent sessions. Each session has its own conversation history, tool
6
+ * state, and working context, but shares project config (NIMBUS.md).
7
+ *
8
+ * File conflict detection warns when two sessions edit the same file.
9
+ */
10
+ import { getDb } from '../state/db';
11
+ /** Singleton session manager instance. */
12
+ let instance = null;
13
+ export class SessionManager {
14
+ db;
15
+ activeSessionId = null;
16
+ fileEdits = new Map();
17
+ eventListeners = [];
18
+ /** Pending conversation writes accumulated before the next debounced flush. */
19
+ pendingConversationFlush = new Map();
20
+ _flushTimer = null;
21
+ flushDebounceMs;
22
+ constructor(db, options) {
23
+ this.db = db || getDb();
24
+ this.flushDebounceMs = options?.flushDebounceMs ?? 5000;
25
+ this.ensureTable();
26
+ }
27
+ /** Get the singleton instance. */
28
+ static getInstance(db) {
29
+ if (!instance) {
30
+ instance = new SessionManager(db);
31
+ }
32
+ return instance;
33
+ }
34
+ /** Reset the singleton (for testing). */
35
+ static resetInstance() {
36
+ instance = null;
37
+ }
38
+ /** Ensure the sessions table exists with the status column. */
39
+ ensureTable() {
40
+ this.db.exec(`
41
+ CREATE TABLE IF NOT EXISTS sessions (
42
+ id TEXT PRIMARY KEY,
43
+ name TEXT NOT NULL,
44
+ status TEXT NOT NULL DEFAULT 'active',
45
+ mode TEXT NOT NULL DEFAULT 'plan',
46
+ model TEXT NOT NULL DEFAULT 'default',
47
+ cwd TEXT NOT NULL DEFAULT '.',
48
+ token_count INTEGER NOT NULL DEFAULT 0,
49
+ cost_usd REAL NOT NULL DEFAULT 0,
50
+ snapshot_count INTEGER NOT NULL DEFAULT 0,
51
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
52
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
53
+ metadata TEXT
54
+ )
55
+ `);
56
+ this.db.exec(`
57
+ CREATE TABLE IF NOT EXISTS conversations (
58
+ id TEXT PRIMARY KEY,
59
+ title TEXT NOT NULL DEFAULT 'Untitled',
60
+ messages TEXT NOT NULL DEFAULT '[]',
61
+ model TEXT NOT NULL DEFAULT 'default',
62
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
63
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
64
+ )
65
+ `);
66
+ }
67
+ /** Create a new session. */
68
+ create(options) {
69
+ const id = crypto.randomUUID();
70
+ const now = new Date().toISOString();
71
+ const mode = options.mode ?? 'plan';
72
+ const model = options.model ?? 'default';
73
+ const cwd = options.cwd ?? process.cwd();
74
+ this.db
75
+ .prepare(`
76
+ INSERT INTO sessions (id, name, status, mode, model, cwd, token_count, cost_usd, snapshot_count, created_at, updated_at)
77
+ VALUES (?, ?, 'active', ?, ?, ?, 0, 0, 0, ?, ?)
78
+ `)
79
+ .run(id, options.name, mode, model, cwd, now, now);
80
+ const session = {
81
+ id,
82
+ name: options.name,
83
+ status: 'active',
84
+ mode,
85
+ model,
86
+ cwd,
87
+ tokenCount: 0,
88
+ costUSD: 0,
89
+ snapshotCount: 0,
90
+ createdAt: now,
91
+ updatedAt: now,
92
+ };
93
+ this.emit({ type: 'created', sessionId: id, timestamp: new Date() });
94
+ return session;
95
+ }
96
+ /** List all sessions, optionally filtered by status. */
97
+ list(status) {
98
+ let rows;
99
+ if (status) {
100
+ rows = this.db
101
+ .prepare('SELECT * FROM sessions WHERE status = ? ORDER BY updated_at DESC')
102
+ .all(status);
103
+ }
104
+ else {
105
+ rows = this.db.prepare('SELECT * FROM sessions ORDER BY updated_at DESC').all();
106
+ }
107
+ return rows.map(rowToSession);
108
+ }
109
+ /** List only active sessions. */
110
+ listActive() {
111
+ return this.list('active');
112
+ }
113
+ /** Get a session by ID. */
114
+ get(id) {
115
+ const row = this.db.prepare('SELECT * FROM sessions WHERE id = ?').get(id);
116
+ return row ? rowToSession(row) : null;
117
+ }
118
+ /** Get the currently active session ID. */
119
+ getActiveSessionId() {
120
+ return this.activeSessionId;
121
+ }
122
+ /** Switch to a different session. Suspends the current one. */
123
+ switchTo(sessionId) {
124
+ const session = this.get(sessionId);
125
+ if (!session) {
126
+ return null;
127
+ }
128
+ // Suspend current session
129
+ if (this.activeSessionId && this.activeSessionId !== sessionId) {
130
+ this.updateStatus(this.activeSessionId, 'suspended');
131
+ this.emit({
132
+ type: 'suspended',
133
+ sessionId: this.activeSessionId,
134
+ timestamp: new Date(),
135
+ });
136
+ }
137
+ // Activate target session
138
+ this.updateStatus(sessionId, 'active');
139
+ this.activeSessionId = sessionId;
140
+ this.emit({ type: 'switched', sessionId, timestamp: new Date() });
141
+ return this.get(sessionId);
142
+ }
143
+ /** Suspend a session (keeps state, stops processing). */
144
+ suspend(sessionId) {
145
+ this.updateStatus(sessionId, 'suspended');
146
+ if (this.activeSessionId === sessionId) {
147
+ this.activeSessionId = null;
148
+ }
149
+ this.emit({ type: 'suspended', sessionId, timestamp: new Date() });
150
+ }
151
+ /** Resume a suspended session. */
152
+ resume(sessionId) {
153
+ const session = this.get(sessionId);
154
+ if (!session || session.status === 'completed') {
155
+ return null;
156
+ }
157
+ this.updateStatus(sessionId, 'active');
158
+ this.activeSessionId = sessionId;
159
+ this.emit({ type: 'resumed', sessionId, timestamp: new Date() });
160
+ return this.get(sessionId);
161
+ }
162
+ /** Mark a session as completed. */
163
+ complete(sessionId) {
164
+ this.updateStatus(sessionId, 'completed');
165
+ if (this.activeSessionId === sessionId) {
166
+ this.activeSessionId = null;
167
+ }
168
+ this.emit({ type: 'completed', sessionId, timestamp: new Date() });
169
+ }
170
+ /** Destroy a session (removes from DB). */
171
+ destroy(sessionId) {
172
+ this.db.prepare('DELETE FROM sessions WHERE id = ?').run(sessionId);
173
+ if (this.activeSessionId === sessionId) {
174
+ this.activeSessionId = null;
175
+ }
176
+ // Clean up file edits for this session
177
+ for (const [path, edits] of this.fileEdits) {
178
+ const filtered = edits.filter(e => e.sessionId !== sessionId);
179
+ if (filtered.length === 0) {
180
+ this.fileEdits.delete(path);
181
+ }
182
+ else {
183
+ this.fileEdits.set(path, filtered);
184
+ }
185
+ }
186
+ this.emit({ type: 'destroyed', sessionId, timestamp: new Date() });
187
+ }
188
+ /** M2: Rename a session (update its display name). */
189
+ rename(sessionId, name) {
190
+ this.db.prepare('UPDATE sessions SET name = ? WHERE id = ?').run(name, sessionId);
191
+ }
192
+ /** Update session metadata (tokens, cost, mode, etc.). */
193
+ updateSession(sessionId, updates) {
194
+ const parts = [];
195
+ const values = [];
196
+ if (updates.tokenCount !== undefined) {
197
+ parts.push('token_count = ?');
198
+ values.push(updates.tokenCount);
199
+ }
200
+ if (updates.costUSD !== undefined) {
201
+ parts.push('cost_usd = ?');
202
+ values.push(updates.costUSD);
203
+ }
204
+ if (updates.snapshotCount !== undefined) {
205
+ parts.push('snapshot_count = ?');
206
+ values.push(updates.snapshotCount);
207
+ }
208
+ if (updates.mode !== undefined) {
209
+ parts.push('mode = ?');
210
+ values.push(updates.mode);
211
+ }
212
+ if (updates.model !== undefined) {
213
+ parts.push('model = ?');
214
+ values.push(updates.model);
215
+ }
216
+ if (parts.length === 0) {
217
+ return;
218
+ }
219
+ parts.push("updated_at = datetime('now')");
220
+ values.push(sessionId);
221
+ this.db.prepare(`UPDATE sessions SET ${parts.join(', ')} WHERE id = ?`).run(...values);
222
+ }
223
+ /** Save conversation messages for a session. */
224
+ saveConversation(sessionId, messages) {
225
+ const existing = this.db.prepare('SELECT id FROM conversations WHERE id = ?').get(sessionId);
226
+ const session = this.get(sessionId);
227
+ const title = session?.name ?? 'Untitled';
228
+ const messagesJson = JSON.stringify(messages);
229
+ if (existing) {
230
+ this.db
231
+ .prepare("UPDATE conversations SET messages = ?, updated_at = datetime('now') WHERE id = ?")
232
+ .run(messagesJson, sessionId);
233
+ }
234
+ else {
235
+ this.db
236
+ .prepare("INSERT INTO conversations (id, title, messages, model, created_at, updated_at) VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))")
237
+ .run(sessionId, title, messagesJson, session?.model ?? 'default');
238
+ }
239
+ }
240
+ /**
241
+ * Atomically save conversation messages AND update session stats.
242
+ * Writes are debounced (default 5s) and batched for performance.
243
+ * Use `flushAll()` for immediate persistence (shutdown / completion).
244
+ */
245
+ saveConversationAndStats(sessionId, messages, stats) {
246
+ // Accumulate into the pending flush map (latest write wins per session)
247
+ const existing = this.pendingConversationFlush.get(sessionId);
248
+ this.pendingConversationFlush.set(sessionId, {
249
+ messages,
250
+ stats: existing?.stats ? { ...existing.stats, ...stats } : stats,
251
+ });
252
+ this._scheduleFlush();
253
+ }
254
+ /** Schedule a debounced flush (no-op if already scheduled). */
255
+ _scheduleFlush() {
256
+ if (this._flushTimer !== null)
257
+ return;
258
+ if (this.flushDebounceMs === 0) {
259
+ // Immediate flush path (used in tests)
260
+ this._flushNow();
261
+ return;
262
+ }
263
+ this._flushTimer = setTimeout(() => {
264
+ this._flushTimer = null;
265
+ this._flushNow();
266
+ }, this.flushDebounceMs);
267
+ }
268
+ /** Write all pending conversation entries to SQLite. */
269
+ _flushNow() {
270
+ if (this.pendingConversationFlush.size === 0)
271
+ return;
272
+ const pending = this.pendingConversationFlush;
273
+ this.pendingConversationFlush = new Map();
274
+ const txn = this.db.transaction(() => {
275
+ for (const [sessionId, { messages, stats }] of pending) {
276
+ this.saveConversation(sessionId, messages);
277
+ if (stats && Object.keys(stats).length > 0) {
278
+ this.updateSession(sessionId, stats);
279
+ }
280
+ }
281
+ });
282
+ txn();
283
+ }
284
+ /**
285
+ * Flush all pending conversation writes immediately.
286
+ * Must be called on clean shutdown and session completion.
287
+ */
288
+ flushAll() {
289
+ if (this._flushTimer !== null) {
290
+ clearTimeout(this._flushTimer);
291
+ this._flushTimer = null;
292
+ }
293
+ this._flushNow();
294
+ }
295
+ /** Load conversation messages for a session. Returns empty array if not found. */
296
+ loadConversation(sessionId) {
297
+ const row = this.db
298
+ .prepare('SELECT messages FROM conversations WHERE id = ?')
299
+ .get(sessionId);
300
+ if (!row?.messages) {
301
+ return [];
302
+ }
303
+ try {
304
+ return JSON.parse(row.messages);
305
+ }
306
+ catch {
307
+ return [];
308
+ }
309
+ }
310
+ /** Record a file edit for conflict detection. */
311
+ recordFileEdit(sessionId, filePath) {
312
+ const normalizedPath = filePath;
313
+ const edit = { sessionId, filePath: normalizedPath, timestamp: new Date() };
314
+ const existing = this.fileEdits.get(normalizedPath) || [];
315
+ existing.push(edit);
316
+ this.fileEdits.set(normalizedPath, existing);
317
+ // Check for conflicts (other sessions editing the same file)
318
+ const conflicts = [];
319
+ const otherEditors = existing.filter(e => e.sessionId !== sessionId && e.timestamp.getTime() > Date.now() - 5 * 60 * 1000 // Within last 5 minutes
320
+ );
321
+ for (const editor of otherEditors) {
322
+ conflicts.push(editor.sessionId);
323
+ }
324
+ if (conflicts.length > 0) {
325
+ this.emit({
326
+ type: 'file_conflict',
327
+ sessionId,
328
+ timestamp: new Date(),
329
+ details: `File "${filePath}" is also being edited by session(s): ${conflicts.join(', ')}`,
330
+ });
331
+ }
332
+ return conflicts;
333
+ }
334
+ /** Persist infra context (terraform workspace, kubectl context, etc.) for a session. */
335
+ setInfraContext(sessionId, ctx) {
336
+ const row = this.db.prepare('SELECT metadata FROM sessions WHERE id = ?').get(sessionId);
337
+ const existing = row?.metadata ? JSON.parse(row.metadata) : {};
338
+ const updated = { ...existing, infraContext: ctx };
339
+ this.db
340
+ .prepare("UPDATE sessions SET metadata = ?, updated_at = datetime('now') WHERE id = ?")
341
+ .run(JSON.stringify(updated), sessionId);
342
+ }
343
+ /** Retrieve infra context for a session. Returns null if not set. */
344
+ getInfraContext(sessionId) {
345
+ const row = this.db.prepare('SELECT metadata FROM sessions WHERE id = ?').get(sessionId);
346
+ if (!row?.metadata)
347
+ return null;
348
+ try {
349
+ const meta = JSON.parse(row.metadata);
350
+ return meta.infraContext ?? null;
351
+ }
352
+ catch {
353
+ return null;
354
+ }
355
+ }
356
+ /** Listen for session events. */
357
+ onEvent(listener) {
358
+ this.eventListeners.push(listener);
359
+ return () => {
360
+ this.eventListeners = this.eventListeners.filter(l => l !== listener);
361
+ };
362
+ }
363
+ emit(event) {
364
+ for (const listener of this.eventListeners) {
365
+ try {
366
+ listener(event);
367
+ }
368
+ catch {
369
+ /* ignore */
370
+ }
371
+ }
372
+ }
373
+ updateStatus(sessionId, status) {
374
+ this.db
375
+ .prepare("UPDATE sessions SET status = ?, updated_at = datetime('now') WHERE id = ?")
376
+ .run(status, sessionId);
377
+ }
378
+ }
379
+ /** Convert a raw DB row to a SessionRecord. */
380
+ function rowToSession(row) {
381
+ return {
382
+ id: row.id,
383
+ name: row.name,
384
+ status: row.status,
385
+ mode: row.mode,
386
+ model: row.model,
387
+ cwd: row.cwd,
388
+ tokenCount: row.token_count,
389
+ costUSD: row.cost_usd,
390
+ snapshotCount: row.snapshot_count,
391
+ createdAt: row.created_at,
392
+ updatedAt: row.updated_at,
393
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
394
+ };
395
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Session types for multi-session support.
3
+ */
4
+ export {};
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Session Sharing — Generate share IDs and sync session data
3
+ *
4
+ * Provides local sharing via the nimbus serve API.
5
+ * Shares are persisted to the SQLite `shares` table so they survive restarts.
6
+ * For hosted sharing via Supabase, the astron-landing web UI handles
7
+ * the sync directly using its existing Supabase integration.
8
+ */
9
+ import { getConversation as _getConversation } from '../state/conversations';
10
+ import { SessionManager as _SessionManager } from '../sessions/manager';
11
+ /**
12
+ * Dependency overrides for testing. Set these before calling shareSession/etc.
13
+ * In production code these remain undefined and the real implementations are used.
14
+ */
15
+ export const _deps = {
16
+ getConversation: undefined,
17
+ getSessionManager: undefined,
18
+ getDb: undefined,
19
+ };
20
+ function getConversation(id) {
21
+ return (_deps.getConversation ?? _getConversation)(id);
22
+ }
23
+ function getSessionManager() {
24
+ return _deps.getSessionManager ? _deps.getSessionManager() : _SessionManager.getInstance();
25
+ }
26
+ /**
27
+ * Lazily import the DB to avoid circular dependency.
28
+ */
29
+ function getDb() {
30
+ if (_deps.getDb) {
31
+ return _deps.getDb();
32
+ }
33
+ try {
34
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
35
+ const { getDb: _getDb } = require('../state/db');
36
+ return _getDb();
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ /**
43
+ * Generate a short, URL-safe share ID.
44
+ */
45
+ function generateShareId() {
46
+ const bytes = crypto.getRandomValues(new Uint8Array(9));
47
+ return btoa(String.fromCharCode(...bytes))
48
+ .replace(/\+/g, '-')
49
+ .replace(/\//g, '_')
50
+ .replace(/=/g, '');
51
+ }
52
+ /**
53
+ * Generate a secure write token for controlling write access.
54
+ */
55
+ function generateWriteToken() {
56
+ return crypto.randomUUID();
57
+ }
58
+ /**
59
+ * Create a share for a session.
60
+ */
61
+ export function shareSession(sessionId, options) {
62
+ const sessionManager = getSessionManager();
63
+ const session = sessionManager.get(sessionId);
64
+ if (!session) {
65
+ return null;
66
+ }
67
+ const conversation = getConversation(sessionId);
68
+ if (!conversation) {
69
+ return null;
70
+ }
71
+ const ttlDays = options?.ttlDays ?? 30;
72
+ const now = new Date();
73
+ const expiresAt = new Date(now.getTime() + ttlDays * 24 * 60 * 60 * 1000);
74
+ const shared = {
75
+ id: generateShareId(),
76
+ sessionId,
77
+ name: session.name,
78
+ messages: conversation.messages,
79
+ model: session.model,
80
+ mode: session.mode,
81
+ costUSD: session.costUSD,
82
+ tokenCount: session.tokenCount,
83
+ createdAt: now.toISOString(),
84
+ expiresAt: expiresAt.toISOString(),
85
+ isLive: options?.isLive ?? false,
86
+ writeToken: generateWriteToken(),
87
+ };
88
+ // Persist to SQLite
89
+ const db = getDb();
90
+ if (db) {
91
+ try {
92
+ db.run(`INSERT INTO shares (id, session_id, name, messages, model, mode, cost_usd, token_count, is_live, write_token, created_at, expires_at)
93
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
94
+ shared.id,
95
+ shared.sessionId,
96
+ shared.name,
97
+ JSON.stringify(shared.messages),
98
+ shared.model,
99
+ shared.mode,
100
+ shared.costUSD,
101
+ shared.tokenCount,
102
+ shared.isLive ? 1 : 0,
103
+ shared.writeToken,
104
+ shared.createdAt,
105
+ shared.expiresAt,
106
+ ]);
107
+ }
108
+ catch {
109
+ // Non-critical — share is still returned but won't survive restart
110
+ }
111
+ }
112
+ return shared;
113
+ }
114
+ /**
115
+ * Convert a raw SQLite row to a SharedSession object.
116
+ */
117
+ function rowToSharedSession(row, includeWriteToken = false) {
118
+ const result = {
119
+ id: row.id,
120
+ sessionId: row.session_id,
121
+ name: row.name,
122
+ messages: JSON.parse(row.messages),
123
+ model: row.model || '',
124
+ mode: row.mode || '',
125
+ costUSD: row.cost_usd || 0,
126
+ tokenCount: row.token_count || 0,
127
+ createdAt: row.created_at,
128
+ expiresAt: row.expires_at,
129
+ isLive: !!row.is_live,
130
+ };
131
+ if (includeWriteToken) {
132
+ result.writeToken = row.write_token;
133
+ }
134
+ return result;
135
+ }
136
+ /**
137
+ * Get a shared session by share ID.
138
+ * If the share is live, refresh the messages from the current session.
139
+ */
140
+ export function getSharedSession(shareId) {
141
+ const db = getDb();
142
+ if (!db) {
143
+ return null;
144
+ }
145
+ try {
146
+ const now = new Date().toISOString();
147
+ const row = db
148
+ .query(`SELECT * FROM shares WHERE id = ? AND expires_at > ?`)
149
+ .get(shareId, now);
150
+ if (!row) {
151
+ return null;
152
+ }
153
+ const shared = rowToSharedSession(row);
154
+ // Refresh messages for live shares
155
+ if (shared.isLive) {
156
+ const conversation = getConversation(shared.sessionId);
157
+ if (conversation) {
158
+ shared.messages = conversation.messages;
159
+ // Update stored messages
160
+ try {
161
+ db.run(`UPDATE shares SET messages = ? WHERE id = ?`, [
162
+ JSON.stringify(shared.messages),
163
+ shareId,
164
+ ]);
165
+ }
166
+ catch {
167
+ /* non-critical */
168
+ }
169
+ }
170
+ }
171
+ return shared;
172
+ }
173
+ catch {
174
+ return null;
175
+ }
176
+ }
177
+ /**
178
+ * List all active shares.
179
+ */
180
+ export function listShares() {
181
+ const db = getDb();
182
+ if (!db) {
183
+ return [];
184
+ }
185
+ try {
186
+ // Clean up expired shares first
187
+ const now = new Date().toISOString();
188
+ db.run(`DELETE FROM shares WHERE expires_at <= ?`, [now]);
189
+ const rows = db
190
+ .query(`SELECT id, session_id, name, messages, model, mode, cost_usd, token_count, is_live, created_at, expires_at
191
+ FROM shares ORDER BY created_at DESC`)
192
+ .all();
193
+ return rows.map(row => rowToSharedSession(row));
194
+ }
195
+ catch {
196
+ return [];
197
+ }
198
+ }
199
+ /**
200
+ * Delete a share.
201
+ */
202
+ export function deleteShare(shareId) {
203
+ const db = getDb();
204
+ if (!db) {
205
+ return false;
206
+ }
207
+ try {
208
+ const result = db.run(`DELETE FROM shares WHERE id = ?`, [shareId]);
209
+ return result.changes > 0;
210
+ }
211
+ catch {
212
+ return false;
213
+ }
214
+ }
215
+ /**
216
+ * Get the share URL for a shared session.
217
+ */
218
+ export function getShareUrl(shareId, baseUrl) {
219
+ const base = baseUrl ?? 'http://localhost:6001';
220
+ return `${base}/nimbus/share/${shareId}`;
221
+ }
222
+ /**
223
+ * Clean up expired shares.
224
+ */
225
+ export function cleanupExpiredShares() {
226
+ const db = getDb();
227
+ if (!db) {
228
+ return 0;
229
+ }
230
+ try {
231
+ const now = new Date().toISOString();
232
+ const result = db.run(`DELETE FROM shares WHERE expires_at <= ?`, [now]);
233
+ return result.changes;
234
+ }
235
+ catch {
236
+ return 0;
237
+ }
238
+ }