@build-astron-co/nimbus 0.2.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 (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +628 -0
  3. package/bin/nimbus +38 -0
  4. package/package.json +80 -0
  5. package/src/__tests__/app.test.ts +76 -0
  6. package/src/__tests__/audit.test.ts +877 -0
  7. package/src/__tests__/circuit-breaker.test.ts +116 -0
  8. package/src/__tests__/cli-run.test.ts +115 -0
  9. package/src/__tests__/context-manager.test.ts +502 -0
  10. package/src/__tests__/context.test.ts +242 -0
  11. package/src/__tests__/enterprise.test.ts +401 -0
  12. package/src/__tests__/generator.test.ts +433 -0
  13. package/src/__tests__/hooks.test.ts +582 -0
  14. package/src/__tests__/init.test.ts +436 -0
  15. package/src/__tests__/intent-parser.test.ts +229 -0
  16. package/src/__tests__/llm-router.test.ts +209 -0
  17. package/src/__tests__/lsp.test.ts +293 -0
  18. package/src/__tests__/modes.test.ts +336 -0
  19. package/src/__tests__/permissions.test.ts +338 -0
  20. package/src/__tests__/serve.test.ts +275 -0
  21. package/src/__tests__/sessions.test.ts +227 -0
  22. package/src/__tests__/sharing.test.ts +288 -0
  23. package/src/__tests__/snapshots.test.ts +581 -0
  24. package/src/__tests__/state-db.test.ts +334 -0
  25. package/src/__tests__/stream-with-tools.test.ts +732 -0
  26. package/src/__tests__/subagents.test.ts +176 -0
  27. package/src/__tests__/system-prompt.test.ts +169 -0
  28. package/src/__tests__/tool-converter.test.ts +256 -0
  29. package/src/__tests__/tool-schemas.test.ts +397 -0
  30. package/src/__tests__/tools.test.ts +143 -0
  31. package/src/__tests__/version.test.ts +49 -0
  32. package/src/agent/compaction-agent.ts +227 -0
  33. package/src/agent/context-manager.ts +435 -0
  34. package/src/agent/context.ts +427 -0
  35. package/src/agent/deploy-preview.ts +426 -0
  36. package/src/agent/index.ts +68 -0
  37. package/src/agent/loop.ts +717 -0
  38. package/src/agent/modes.ts +429 -0
  39. package/src/agent/permissions.ts +466 -0
  40. package/src/agent/subagents/base.ts +116 -0
  41. package/src/agent/subagents/cost.ts +51 -0
  42. package/src/agent/subagents/explore.ts +42 -0
  43. package/src/agent/subagents/general.ts +54 -0
  44. package/src/agent/subagents/index.ts +102 -0
  45. package/src/agent/subagents/infra.ts +59 -0
  46. package/src/agent/subagents/security.ts +69 -0
  47. package/src/agent/system-prompt.ts +436 -0
  48. package/src/app.ts +122 -0
  49. package/src/audit/activity-log.ts +290 -0
  50. package/src/audit/compliance-checker.ts +540 -0
  51. package/src/audit/cost-tracker.ts +318 -0
  52. package/src/audit/index.ts +23 -0
  53. package/src/audit/security-scanner.ts +596 -0
  54. package/src/auth/guard.ts +75 -0
  55. package/src/auth/index.ts +56 -0
  56. package/src/auth/oauth.ts +455 -0
  57. package/src/auth/providers.ts +470 -0
  58. package/src/auth/sso.ts +113 -0
  59. package/src/auth/store.ts +505 -0
  60. package/src/auth/types.ts +187 -0
  61. package/src/build.ts +141 -0
  62. package/src/cli/index.ts +16 -0
  63. package/src/cli/init.ts +854 -0
  64. package/src/cli/openapi-spec.ts +356 -0
  65. package/src/cli/run.ts +237 -0
  66. package/src/cli/serve-auth.ts +80 -0
  67. package/src/cli/serve.ts +462 -0
  68. package/src/cli/web.ts +67 -0
  69. package/src/cli.ts +1417 -0
  70. package/src/clients/core-engine-client.ts +227 -0
  71. package/src/clients/enterprise-client.ts +334 -0
  72. package/src/clients/generator-client.ts +351 -0
  73. package/src/clients/git-client.ts +627 -0
  74. package/src/clients/github-client.ts +410 -0
  75. package/src/clients/helm-client.ts +504 -0
  76. package/src/clients/index.ts +80 -0
  77. package/src/clients/k8s-client.ts +497 -0
  78. package/src/clients/llm-client.ts +161 -0
  79. package/src/clients/rest-client.ts +130 -0
  80. package/src/clients/service-discovery.ts +33 -0
  81. package/src/clients/terraform-client.ts +482 -0
  82. package/src/clients/tools-client.ts +1843 -0
  83. package/src/clients/ws-client.ts +115 -0
  84. package/src/commands/analyze/index.ts +352 -0
  85. package/src/commands/apply/helm.ts +473 -0
  86. package/src/commands/apply/index.ts +213 -0
  87. package/src/commands/apply/k8s.ts +454 -0
  88. package/src/commands/apply/terraform.ts +582 -0
  89. package/src/commands/ask.ts +167 -0
  90. package/src/commands/audit/index.ts +238 -0
  91. package/src/commands/auth-cloud.ts +294 -0
  92. package/src/commands/auth-list.ts +134 -0
  93. package/src/commands/auth-profile.ts +121 -0
  94. package/src/commands/auth-status.ts +141 -0
  95. package/src/commands/aws/ec2.ts +501 -0
  96. package/src/commands/aws/iam.ts +397 -0
  97. package/src/commands/aws/index.ts +133 -0
  98. package/src/commands/aws/lambda.ts +396 -0
  99. package/src/commands/aws/rds.ts +439 -0
  100. package/src/commands/aws/s3.ts +439 -0
  101. package/src/commands/aws/vpc.ts +393 -0
  102. package/src/commands/aws-discover.ts +649 -0
  103. package/src/commands/aws-terraform.ts +805 -0
  104. package/src/commands/azure/aks.ts +376 -0
  105. package/src/commands/azure/functions.ts +253 -0
  106. package/src/commands/azure/index.ts +116 -0
  107. package/src/commands/azure/storage.ts +478 -0
  108. package/src/commands/azure/vm.ts +355 -0
  109. package/src/commands/billing/index.ts +256 -0
  110. package/src/commands/chat.ts +314 -0
  111. package/src/commands/config.ts +346 -0
  112. package/src/commands/cost/cloud-cost-estimator.ts +266 -0
  113. package/src/commands/cost/estimator.ts +79 -0
  114. package/src/commands/cost/index.ts +594 -0
  115. package/src/commands/cost/parsers/terraform.ts +273 -0
  116. package/src/commands/cost/parsers/types.ts +25 -0
  117. package/src/commands/cost/pricing/aws.ts +544 -0
  118. package/src/commands/cost/pricing/azure.ts +499 -0
  119. package/src/commands/cost/pricing/gcp.ts +396 -0
  120. package/src/commands/cost/pricing/index.ts +40 -0
  121. package/src/commands/demo.ts +250 -0
  122. package/src/commands/doctor.ts +794 -0
  123. package/src/commands/drift/index.ts +439 -0
  124. package/src/commands/explain.ts +277 -0
  125. package/src/commands/feedback.ts +389 -0
  126. package/src/commands/fix.ts +324 -0
  127. package/src/commands/fs/index.ts +402 -0
  128. package/src/commands/gcp/compute.ts +325 -0
  129. package/src/commands/gcp/functions.ts +271 -0
  130. package/src/commands/gcp/gke.ts +438 -0
  131. package/src/commands/gcp/iam.ts +344 -0
  132. package/src/commands/gcp/index.ts +129 -0
  133. package/src/commands/gcp/storage.ts +284 -0
  134. package/src/commands/generate-helm.ts +1249 -0
  135. package/src/commands/generate-k8s.ts +1560 -0
  136. package/src/commands/generate-terraform.ts +1460 -0
  137. package/src/commands/gh/index.ts +863 -0
  138. package/src/commands/git/index.ts +1343 -0
  139. package/src/commands/helm/index.ts +1126 -0
  140. package/src/commands/help.ts +539 -0
  141. package/src/commands/history.ts +142 -0
  142. package/src/commands/import.ts +868 -0
  143. package/src/commands/index.ts +367 -0
  144. package/src/commands/init.ts +1046 -0
  145. package/src/commands/k8s/index.ts +1137 -0
  146. package/src/commands/login.ts +631 -0
  147. package/src/commands/logout.ts +83 -0
  148. package/src/commands/onboarding.ts +228 -0
  149. package/src/commands/plan/display.ts +279 -0
  150. package/src/commands/plan/index.ts +599 -0
  151. package/src/commands/preview.ts +452 -0
  152. package/src/commands/questionnaire.ts +1270 -0
  153. package/src/commands/resume.ts +55 -0
  154. package/src/commands/team/index.ts +346 -0
  155. package/src/commands/template.ts +232 -0
  156. package/src/commands/tf/index.ts +1034 -0
  157. package/src/commands/upgrade.ts +550 -0
  158. package/src/commands/usage/index.ts +134 -0
  159. package/src/commands/version.ts +170 -0
  160. package/src/compat/index.ts +2 -0
  161. package/src/compat/runtime.ts +12 -0
  162. package/src/compat/sqlite.ts +107 -0
  163. package/src/config/index.ts +17 -0
  164. package/src/config/manager.ts +530 -0
  165. package/src/config/safety-policy.ts +358 -0
  166. package/src/config/schema.ts +125 -0
  167. package/src/config/types.ts +527 -0
  168. package/src/context/context-db.ts +199 -0
  169. package/src/demo/index.ts +349 -0
  170. package/src/demo/scenarios/full-journey.ts +229 -0
  171. package/src/demo/scenarios/getting-started.ts +127 -0
  172. package/src/demo/scenarios/helm-release.ts +341 -0
  173. package/src/demo/scenarios/k8s-deployment.ts +194 -0
  174. package/src/demo/scenarios/terraform-vpc.ts +170 -0
  175. package/src/demo/types.ts +92 -0
  176. package/src/engine/cost-estimator.ts +438 -0
  177. package/src/engine/diagram-generator.ts +256 -0
  178. package/src/engine/drift-detector.ts +902 -0
  179. package/src/engine/executor.ts +1035 -0
  180. package/src/engine/index.ts +76 -0
  181. package/src/engine/orchestrator.ts +636 -0
  182. package/src/engine/planner.ts +720 -0
  183. package/src/engine/safety.ts +743 -0
  184. package/src/engine/verifier.ts +770 -0
  185. package/src/enterprise/audit.ts +348 -0
  186. package/src/enterprise/auth.ts +270 -0
  187. package/src/enterprise/billing.ts +822 -0
  188. package/src/enterprise/index.ts +17 -0
  189. package/src/enterprise/teams.ts +443 -0
  190. package/src/generator/best-practices.ts +1608 -0
  191. package/src/generator/helm.ts +630 -0
  192. package/src/generator/index.ts +37 -0
  193. package/src/generator/intent-parser.ts +514 -0
  194. package/src/generator/kubernetes.ts +976 -0
  195. package/src/generator/terraform.ts +1867 -0
  196. package/src/history/index.ts +8 -0
  197. package/src/history/manager.ts +322 -0
  198. package/src/history/types.ts +34 -0
  199. package/src/hooks/config.ts +432 -0
  200. package/src/hooks/engine.ts +391 -0
  201. package/src/hooks/index.ts +4 -0
  202. package/src/llm/auth-bridge.ts +198 -0
  203. package/src/llm/circuit-breaker.ts +140 -0
  204. package/src/llm/config-loader.ts +201 -0
  205. package/src/llm/cost-calculator.ts +171 -0
  206. package/src/llm/index.ts +8 -0
  207. package/src/llm/model-aliases.ts +115 -0
  208. package/src/llm/provider-registry.ts +63 -0
  209. package/src/llm/providers/anthropic.ts +433 -0
  210. package/src/llm/providers/bedrock.ts +477 -0
  211. package/src/llm/providers/google.ts +405 -0
  212. package/src/llm/providers/ollama.ts +767 -0
  213. package/src/llm/providers/openai-compatible.ts +340 -0
  214. package/src/llm/providers/openai.ts +328 -0
  215. package/src/llm/providers/openrouter.ts +338 -0
  216. package/src/llm/router.ts +1035 -0
  217. package/src/llm/types.ts +232 -0
  218. package/src/lsp/client.ts +298 -0
  219. package/src/lsp/languages.ts +116 -0
  220. package/src/lsp/manager.ts +278 -0
  221. package/src/mcp/client.ts +402 -0
  222. package/src/mcp/index.ts +5 -0
  223. package/src/mcp/manager.ts +133 -0
  224. package/src/nimbus.ts +214 -0
  225. package/src/plugins/index.ts +27 -0
  226. package/src/plugins/loader.ts +334 -0
  227. package/src/plugins/manager.ts +376 -0
  228. package/src/plugins/types.ts +284 -0
  229. package/src/scanners/cicd-scanner.ts +258 -0
  230. package/src/scanners/cloud-scanner.ts +466 -0
  231. package/src/scanners/framework-scanner.ts +469 -0
  232. package/src/scanners/iac-scanner.ts +388 -0
  233. package/src/scanners/index.ts +539 -0
  234. package/src/scanners/language-scanner.ts +276 -0
  235. package/src/scanners/package-manager-scanner.ts +277 -0
  236. package/src/scanners/types.ts +172 -0
  237. package/src/sessions/manager.ts +365 -0
  238. package/src/sessions/types.ts +44 -0
  239. package/src/sharing/sync.ts +296 -0
  240. package/src/sharing/viewer.ts +97 -0
  241. package/src/snapshots/index.ts +2 -0
  242. package/src/snapshots/manager.ts +530 -0
  243. package/src/state/artifacts.ts +147 -0
  244. package/src/state/audit.ts +137 -0
  245. package/src/state/billing.ts +240 -0
  246. package/src/state/checkpoints.ts +117 -0
  247. package/src/state/config.ts +67 -0
  248. package/src/state/conversations.ts +14 -0
  249. package/src/state/credentials.ts +154 -0
  250. package/src/state/db.ts +58 -0
  251. package/src/state/index.ts +26 -0
  252. package/src/state/messages.ts +115 -0
  253. package/src/state/projects.ts +123 -0
  254. package/src/state/schema.ts +236 -0
  255. package/src/state/sessions.ts +147 -0
  256. package/src/state/teams.ts +200 -0
  257. package/src/telemetry.ts +108 -0
  258. package/src/tools/aws-ops.ts +952 -0
  259. package/src/tools/azure-ops.ts +579 -0
  260. package/src/tools/file-ops.ts +593 -0
  261. package/src/tools/gcp-ops.ts +625 -0
  262. package/src/tools/git-ops.ts +773 -0
  263. package/src/tools/github-ops.ts +799 -0
  264. package/src/tools/helm-ops.ts +943 -0
  265. package/src/tools/index.ts +17 -0
  266. package/src/tools/k8s-ops.ts +819 -0
  267. package/src/tools/schemas/converter.ts +184 -0
  268. package/src/tools/schemas/devops.ts +612 -0
  269. package/src/tools/schemas/index.ts +73 -0
  270. package/src/tools/schemas/standard.ts +1144 -0
  271. package/src/tools/schemas/types.ts +705 -0
  272. package/src/tools/terraform-ops.ts +862 -0
  273. package/src/types/ambient.d.ts +193 -0
  274. package/src/types/config.ts +83 -0
  275. package/src/types/drift.ts +116 -0
  276. package/src/types/enterprise.ts +335 -0
  277. package/src/types/index.ts +20 -0
  278. package/src/types/plan.ts +44 -0
  279. package/src/types/request.ts +65 -0
  280. package/src/types/response.ts +54 -0
  281. package/src/types/service.ts +51 -0
  282. package/src/ui/App.tsx +997 -0
  283. package/src/ui/DeployPreview.tsx +169 -0
  284. package/src/ui/Header.tsx +68 -0
  285. package/src/ui/InputBox.tsx +350 -0
  286. package/src/ui/MessageList.tsx +585 -0
  287. package/src/ui/PermissionPrompt.tsx +151 -0
  288. package/src/ui/StatusBar.tsx +158 -0
  289. package/src/ui/ToolCallDisplay.tsx +409 -0
  290. package/src/ui/chat-ui.ts +853 -0
  291. package/src/ui/index.ts +33 -0
  292. package/src/ui/ink/index.ts +711 -0
  293. package/src/ui/streaming.ts +176 -0
  294. package/src/ui/types.ts +57 -0
  295. package/src/utils/analytics.ts +72 -0
  296. package/src/utils/cost-warning.ts +27 -0
  297. package/src/utils/env.ts +46 -0
  298. package/src/utils/errors.ts +69 -0
  299. package/src/utils/event-bus.ts +38 -0
  300. package/src/utils/index.ts +24 -0
  301. package/src/utils/logger.ts +171 -0
  302. package/src/utils/rate-limiter.ts +121 -0
  303. package/src/utils/service-auth.ts +49 -0
  304. package/src/utils/validation.ts +53 -0
  305. package/src/version.ts +4 -0
  306. package/src/watcher/index.ts +163 -0
  307. package/src/wizard/approval.ts +383 -0
  308. package/src/wizard/index.ts +25 -0
  309. package/src/wizard/prompts.ts +338 -0
  310. package/src/wizard/types.ts +171 -0
  311. package/src/wizard/ui.ts +556 -0
  312. package/src/wizard/wizard.ts +304 -0
  313. package/tsconfig.json +24 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Artifact CRUD helpers.
3
+ *
4
+ * Refactored from SQLiteAdapter.saveArtifact, getArtifact, listArtifacts,
5
+ * and deleteArtifact.
6
+ */
7
+
8
+ import type { Database } from '../compat/sqlite';
9
+ import { getDb } from './db';
10
+
11
+ /** Shape returned by artifact query helpers. */
12
+ export interface ArtifactRecord {
13
+ id: string;
14
+ conversationId: string | null;
15
+ name: string;
16
+ type: string;
17
+ content: string;
18
+ language: string | null;
19
+ createdAt: string;
20
+ updatedAt: string;
21
+ metadata: any | null;
22
+ }
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Public API
26
+ // ---------------------------------------------------------------------------
27
+
28
+ /**
29
+ * Persist or update an artifact. Uses INSERT ... ON CONFLICT to upsert.
30
+ */
31
+ export function saveArtifact(
32
+ id: string,
33
+ conversationId: string | null,
34
+ name: string,
35
+ type: string,
36
+ content: string,
37
+ language?: string,
38
+ metadata?: any,
39
+ db?: Database
40
+ ): void {
41
+ const d = db || getDb();
42
+ const stmt = d.prepare(`
43
+ INSERT INTO artifacts (id, conversation_id, name, type, content, language, created_at, updated_at, metadata)
44
+ VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?)
45
+ ON CONFLICT(id) DO UPDATE SET
46
+ conversation_id = excluded.conversation_id,
47
+ name = excluded.name,
48
+ type = excluded.type,
49
+ content = excluded.content,
50
+ language = excluded.language,
51
+ updated_at = CURRENT_TIMESTAMP,
52
+ metadata = excluded.metadata
53
+ `);
54
+
55
+ stmt.run(
56
+ id,
57
+ conversationId || null,
58
+ name,
59
+ type,
60
+ content,
61
+ language || null,
62
+ metadata ? JSON.stringify(metadata) : null
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Retrieve a single artifact by id.
68
+ */
69
+ export function getArtifact(id: string, db?: Database): ArtifactRecord | null {
70
+ const d = db || getDb();
71
+ const stmt = d.prepare('SELECT * FROM artifacts WHERE id = ?');
72
+ const row: any = stmt.get(id);
73
+
74
+ if (!row) {
75
+ return null;
76
+ }
77
+
78
+ return {
79
+ id: row.id,
80
+ conversationId: row.conversation_id,
81
+ name: row.name,
82
+ type: row.type,
83
+ content: row.content,
84
+ language: row.language,
85
+ createdAt: row.created_at,
86
+ updatedAt: row.updated_at,
87
+ metadata: row.metadata ? JSON.parse(row.metadata) : null,
88
+ };
89
+ }
90
+
91
+ /**
92
+ * List artifacts with optional type and conversation filters.
93
+ */
94
+ export function listArtifacts(
95
+ type?: string,
96
+ conversationId?: string,
97
+ limit: number = 50,
98
+ offset: number = 0,
99
+ db?: Database
100
+ ): ArtifactRecord[] {
101
+ const d = db || getDb();
102
+
103
+ let query = 'SELECT * FROM artifacts';
104
+ const params: any[] = [];
105
+ const conditions: string[] = [];
106
+
107
+ if (type) {
108
+ conditions.push('type = ?');
109
+ params.push(type);
110
+ }
111
+
112
+ if (conversationId) {
113
+ conditions.push('conversation_id = ?');
114
+ params.push(conversationId);
115
+ }
116
+
117
+ if (conditions.length > 0) {
118
+ query += ` WHERE ${conditions.join(' AND ')}`;
119
+ }
120
+
121
+ query += ' ORDER BY created_at DESC LIMIT ? OFFSET ?';
122
+ params.push(limit, offset);
123
+
124
+ const stmt = d.prepare(query);
125
+ const rows: any[] = stmt.all(...params) as any[];
126
+
127
+ return rows.map(row => ({
128
+ id: row.id,
129
+ conversationId: row.conversation_id,
130
+ name: row.name,
131
+ type: row.type,
132
+ content: row.content,
133
+ language: row.language,
134
+ createdAt: row.created_at,
135
+ updatedAt: row.updated_at,
136
+ metadata: row.metadata ? JSON.parse(row.metadata) : null,
137
+ }));
138
+ }
139
+
140
+ /**
141
+ * Delete an artifact by id.
142
+ */
143
+ export function deleteArtifact(id: string, db?: Database): void {
144
+ const d = db || getDb();
145
+ const stmt = d.prepare('DELETE FROM artifacts WHERE id = ?');
146
+ stmt.run(id);
147
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Audit log helpers.
3
+ *
4
+ * Refactored from SQLiteAdapter.logAuditEvent and getAuditLogs.
5
+ */
6
+
7
+ import type { Database } from '../compat/sqlite';
8
+ import { getDb } from './db';
9
+
10
+ /** Shape accepted when writing an audit event. */
11
+ export interface AuditEventInput {
12
+ id: string;
13
+ userId?: string;
14
+ action: string;
15
+ resourceType?: string;
16
+ resourceId?: string;
17
+ input?: any;
18
+ output?: any;
19
+ status: string;
20
+ durationMs?: number;
21
+ metadata?: any;
22
+ }
23
+
24
+ /** Shape returned by audit log queries. */
25
+ export interface AuditLogRecord {
26
+ id: string;
27
+ timestamp: string;
28
+ userId: string | null;
29
+ action: string;
30
+ resourceType: string | null;
31
+ resourceId: string | null;
32
+ input: any | null;
33
+ output: any | null;
34
+ status: string;
35
+ durationMs: number | null;
36
+ metadata: any | null;
37
+ }
38
+
39
+ /** Filter criteria for querying audit logs. */
40
+ export interface AuditLogFilter {
41
+ userId?: string;
42
+ action?: string;
43
+ resourceType?: string;
44
+ status?: string;
45
+ startDate?: Date;
46
+ endDate?: Date;
47
+ limit?: number;
48
+ offset?: number;
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Public API
53
+ // ---------------------------------------------------------------------------
54
+
55
+ /**
56
+ * Persist an audit event.
57
+ */
58
+ export function logAuditEvent(event: AuditEventInput, db?: Database): void {
59
+ const d = db || getDb();
60
+ const stmt = d.prepare(`
61
+ INSERT INTO audit_logs (id, timestamp, user_id, action, resource_type, resource_id, input, output, status, duration_ms, metadata)
62
+ VALUES (?, CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ?, ?, ?, ?)
63
+ `);
64
+
65
+ stmt.run(
66
+ event.id,
67
+ event.userId || null,
68
+ event.action,
69
+ event.resourceType || null,
70
+ event.resourceId || null,
71
+ event.input ? JSON.stringify(event.input) : null,
72
+ event.output ? JSON.stringify(event.output) : null,
73
+ event.status,
74
+ event.durationMs || null,
75
+ event.metadata ? JSON.stringify(event.metadata) : null
76
+ );
77
+ }
78
+
79
+ /**
80
+ * Query audit logs with optional filters.
81
+ */
82
+ export function getAuditLogs(filter?: AuditLogFilter, db?: Database): AuditLogRecord[] {
83
+ const d = db || getDb();
84
+
85
+ let query = 'SELECT * FROM audit_logs';
86
+ const params: any[] = [];
87
+ const conditions: string[] = [];
88
+
89
+ if (filter?.userId) {
90
+ conditions.push('user_id = ?');
91
+ params.push(filter.userId);
92
+ }
93
+ if (filter?.action) {
94
+ conditions.push('action = ?');
95
+ params.push(filter.action);
96
+ }
97
+ if (filter?.resourceType) {
98
+ conditions.push('resource_type = ?');
99
+ params.push(filter.resourceType);
100
+ }
101
+ if (filter?.status) {
102
+ conditions.push('status = ?');
103
+ params.push(filter.status);
104
+ }
105
+ if (filter?.startDate) {
106
+ conditions.push('timestamp >= ?');
107
+ params.push(filter.startDate.toISOString());
108
+ }
109
+ if (filter?.endDate) {
110
+ conditions.push('timestamp <= ?');
111
+ params.push(filter.endDate.toISOString());
112
+ }
113
+
114
+ if (conditions.length > 0) {
115
+ query += ` WHERE ${conditions.join(' AND ')}`;
116
+ }
117
+
118
+ query += ' ORDER BY timestamp DESC LIMIT ? OFFSET ?';
119
+ params.push(filter?.limit || 100, filter?.offset || 0);
120
+
121
+ const stmt = d.prepare(query);
122
+ const rows: any[] = stmt.all(...params) as any[];
123
+
124
+ return rows.map(row => ({
125
+ id: row.id,
126
+ timestamp: row.timestamp,
127
+ userId: row.user_id,
128
+ action: row.action,
129
+ resourceType: row.resource_type,
130
+ resourceId: row.resource_id,
131
+ input: row.input ? JSON.parse(row.input) : null,
132
+ output: row.output ? JSON.parse(row.output) : null,
133
+ status: row.status,
134
+ durationMs: row.duration_ms,
135
+ metadata: row.metadata ? JSON.parse(row.metadata) : null,
136
+ }));
137
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Billing / usage tracking helpers.
3
+ *
4
+ * Refactored from the billing-service database adapter
5
+ * (services/billing-service/src/db/adapter.ts) into standalone functions
6
+ * that operate against the unified Nimbus database.
7
+ */
8
+
9
+ import type { Database } from '../compat/sqlite';
10
+ import { getDb } from './db';
11
+
12
+ /** Shape returned by subscription queries. */
13
+ export interface SubscriptionRecord {
14
+ id: string;
15
+ teamId: string;
16
+ plan: string;
17
+ status: string;
18
+ currentPeriodStart: string | null;
19
+ currentPeriodEnd: string | null;
20
+ createdAt: string;
21
+ updatedAt: string;
22
+ }
23
+
24
+ /** Shape returned by usage queries. */
25
+ export interface UsageRecord {
26
+ id: string;
27
+ teamId: string | null;
28
+ userId: string | null;
29
+ type: string;
30
+ quantity: number;
31
+ unit: string;
32
+ costUsd: number | null;
33
+ metadata: any | null;
34
+ createdAt: string;
35
+ }
36
+
37
+ /** Aggregated usage summary row. */
38
+ export interface UsageSummary {
39
+ type: string;
40
+ totalQuantity: number;
41
+ totalCost: number;
42
+ count: number;
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Subscription helpers
47
+ // ---------------------------------------------------------------------------
48
+
49
+ /**
50
+ * Create a new subscription.
51
+ */
52
+ export function createSubscription(
53
+ id: string,
54
+ teamId: string,
55
+ plan: string = 'free',
56
+ status: string = 'active',
57
+ currentPeriodStart?: string,
58
+ currentPeriodEnd?: string,
59
+ db?: Database
60
+ ): void {
61
+ const d = db || getDb();
62
+ const stmt = d.prepare(`
63
+ INSERT INTO subscriptions (id, team_id, plan, status, current_period_start, current_period_end, created_at, updated_at)
64
+ VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
65
+ `);
66
+
67
+ stmt.run(id, teamId, plan, status, currentPeriodStart || null, currentPeriodEnd || null);
68
+ }
69
+
70
+ /**
71
+ * Retrieve a subscription by team id.
72
+ */
73
+ export function getSubscription(teamId: string, db?: Database): SubscriptionRecord | null {
74
+ const d = db || getDb();
75
+ const stmt = d.prepare('SELECT * FROM subscriptions WHERE team_id = ?');
76
+ const row: any = stmt.get(teamId);
77
+
78
+ if (!row) {
79
+ return null;
80
+ }
81
+
82
+ return {
83
+ id: row.id,
84
+ teamId: row.team_id,
85
+ plan: row.plan,
86
+ status: row.status,
87
+ currentPeriodStart: row.current_period_start,
88
+ currentPeriodEnd: row.current_period_end,
89
+ createdAt: row.created_at,
90
+ updatedAt: row.updated_at,
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Update plan, status, or period boundaries of an existing subscription.
96
+ */
97
+ export function updateSubscription(
98
+ teamId: string,
99
+ updates: {
100
+ plan?: string;
101
+ status?: string;
102
+ currentPeriodStart?: string;
103
+ currentPeriodEnd?: string;
104
+ },
105
+ db?: Database
106
+ ): void {
107
+ const d = db || getDb();
108
+ const stmt = d.prepare(`
109
+ UPDATE subscriptions
110
+ SET plan = COALESCE(?, plan),
111
+ status = COALESCE(?, status),
112
+ current_period_start = COALESCE(?, current_period_start),
113
+ current_period_end = COALESCE(?, current_period_end),
114
+ updated_at = CURRENT_TIMESTAMP
115
+ WHERE team_id = ?
116
+ `);
117
+
118
+ stmt.run(
119
+ updates.plan || null,
120
+ updates.status || null,
121
+ updates.currentPeriodStart || null,
122
+ updates.currentPeriodEnd || null,
123
+ teamId
124
+ );
125
+ }
126
+
127
+ // ---------------------------------------------------------------------------
128
+ // Usage helpers
129
+ // ---------------------------------------------------------------------------
130
+
131
+ /**
132
+ * Record a single usage event.
133
+ */
134
+ export function recordUsage(
135
+ id: string,
136
+ type: string,
137
+ quantity: number,
138
+ unit: string = 'tokens',
139
+ costUsd?: number,
140
+ teamId?: string,
141
+ userId?: string,
142
+ metadata?: any,
143
+ db?: Database
144
+ ): void {
145
+ const d = db || getDb();
146
+ const stmt = d.prepare(`
147
+ INSERT INTO usage (id, team_id, user_id, type, quantity, unit, cost_usd, metadata, created_at)
148
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
149
+ `);
150
+
151
+ stmt.run(
152
+ id,
153
+ teamId || null,
154
+ userId || null,
155
+ type,
156
+ quantity,
157
+ unit,
158
+ costUsd ?? 0,
159
+ metadata ? JSON.stringify(metadata) : null
160
+ );
161
+ }
162
+
163
+ /**
164
+ * Retrieve usage records for a team within a date range.
165
+ */
166
+ export function getUsage(
167
+ teamId: string,
168
+ since: Date,
169
+ until?: Date,
170
+ limit: number = 100,
171
+ offset: number = 0,
172
+ db?: Database
173
+ ): UsageRecord[] {
174
+ const d = db || getDb();
175
+ const untilDate = until || new Date();
176
+
177
+ const stmt = d.prepare(`
178
+ SELECT * FROM usage
179
+ WHERE team_id = ?
180
+ AND created_at >= ?
181
+ AND created_at <= ?
182
+ ORDER BY created_at DESC
183
+ LIMIT ? OFFSET ?
184
+ `);
185
+
186
+ const rows: any[] = stmt.all(
187
+ teamId,
188
+ since.toISOString(),
189
+ untilDate.toISOString(),
190
+ limit,
191
+ offset
192
+ ) as any[];
193
+
194
+ return rows.map(row => ({
195
+ id: row.id,
196
+ teamId: row.team_id,
197
+ userId: row.user_id,
198
+ type: row.type,
199
+ quantity: row.quantity,
200
+ unit: row.unit,
201
+ costUsd: row.cost_usd,
202
+ metadata: row.metadata ? JSON.parse(row.metadata) : null,
203
+ createdAt: row.created_at,
204
+ }));
205
+ }
206
+
207
+ /**
208
+ * Aggregate usage by type for a team within a date range.
209
+ */
210
+ export function getUsageSummary(
211
+ teamId: string,
212
+ since: Date,
213
+ until?: Date,
214
+ db?: Database
215
+ ): UsageSummary[] {
216
+ const d = db || getDb();
217
+ const untilDate = until || new Date();
218
+
219
+ const stmt = d.prepare(`
220
+ SELECT
221
+ type,
222
+ SUM(quantity) as total_quantity,
223
+ SUM(cost_usd) as total_cost,
224
+ COUNT(*) as count
225
+ FROM usage
226
+ WHERE team_id = ?
227
+ AND created_at >= ?
228
+ AND created_at <= ?
229
+ GROUP BY type
230
+ `);
231
+
232
+ const rows: any[] = stmt.all(teamId, since.toISOString(), untilDate.toISOString()) as any[];
233
+
234
+ return rows.map(row => ({
235
+ type: row.type,
236
+ totalQuantity: row.total_quantity,
237
+ totalCost: row.total_cost,
238
+ count: row.count,
239
+ }));
240
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Checkpoint save / restore helpers for resumable operations.
3
+ *
4
+ * Refactored from SQLiteAdapter.saveCheckpoint, getCheckpoint,
5
+ * getLatestCheckpoint, listCheckpoints, and deleteCheckpoints.
6
+ */
7
+
8
+ import type { Database } from '../compat/sqlite';
9
+ import { getDb } from './db';
10
+
11
+ /** Shape returned by checkpoint queries. */
12
+ export interface CheckpointRecord {
13
+ id: string;
14
+ operationId: string;
15
+ step: number;
16
+ state: Record<string, unknown>;
17
+ createdAt: string;
18
+ }
19
+
20
+ /** Abbreviated shape used by listCheckpoints. */
21
+ export interface CheckpointListItem {
22
+ id: string;
23
+ step: number;
24
+ createdAt: string;
25
+ }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Public API
29
+ // ---------------------------------------------------------------------------
30
+
31
+ /**
32
+ * Persist or overwrite a checkpoint for a given operation and step.
33
+ */
34
+ export function saveCheckpoint(
35
+ id: string,
36
+ operationId: string,
37
+ step: number,
38
+ state: Record<string, unknown>,
39
+ db?: Database
40
+ ): void {
41
+ const d = db || getDb();
42
+ const stmt = d.prepare(`
43
+ INSERT OR REPLACE INTO checkpoints (id, operation_id, step, state, created_at)
44
+ VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
45
+ `);
46
+
47
+ stmt.run(id, operationId, step, JSON.stringify(state));
48
+ }
49
+
50
+ /**
51
+ * Retrieve a single checkpoint by its id.
52
+ */
53
+ export function getCheckpoint(id: string, db?: Database): CheckpointRecord | null {
54
+ const d = db || getDb();
55
+ const stmt = d.prepare('SELECT * FROM checkpoints WHERE id = ?');
56
+ const row: any = stmt.get(id);
57
+
58
+ if (!row) {
59
+ return null;
60
+ }
61
+
62
+ return {
63
+ id: row.id,
64
+ operationId: row.operation_id,
65
+ step: row.step,
66
+ state: JSON.parse(row.state),
67
+ createdAt: row.created_at,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Retrieve the most recent checkpoint (highest step) for an operation.
73
+ */
74
+ export function getLatestCheckpoint(operationId: string, db?: Database): CheckpointRecord | null {
75
+ const d = db || getDb();
76
+ const stmt = d.prepare(`
77
+ SELECT * FROM checkpoints WHERE operation_id = ? ORDER BY step DESC LIMIT 1
78
+ `);
79
+ const row: any = stmt.get(operationId);
80
+
81
+ if (!row) {
82
+ return null;
83
+ }
84
+
85
+ return {
86
+ id: row.id,
87
+ operationId: row.operation_id,
88
+ step: row.step,
89
+ state: JSON.parse(row.state),
90
+ createdAt: row.created_at,
91
+ };
92
+ }
93
+
94
+ /**
95
+ * List all checkpoints for an operation ordered by step ascending.
96
+ */
97
+ export function listCheckpoints(operationId: string, db?: Database): CheckpointListItem[] {
98
+ const d = db || getDb();
99
+ const stmt = d.prepare(`
100
+ SELECT id, step, created_at FROM checkpoints WHERE operation_id = ? ORDER BY step ASC
101
+ `);
102
+
103
+ return (stmt.all(operationId) as any[]).map(row => ({
104
+ id: row.id,
105
+ step: row.step,
106
+ createdAt: row.created_at,
107
+ }));
108
+ }
109
+
110
+ /**
111
+ * Delete all checkpoints associated with an operation.
112
+ */
113
+ export function deleteCheckpoints(operationId: string, db?: Database): void {
114
+ const d = db || getDb();
115
+ const stmt = d.prepare('DELETE FROM checkpoints WHERE operation_id = ?');
116
+ stmt.run(operationId);
117
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Config key-value store helpers.
3
+ *
4
+ * Refactored from SQLiteAdapter.setConfig, getConfig, and getAllConfig.
5
+ */
6
+
7
+ import type { Database } from '../compat/sqlite';
8
+ import { getDb } from './db';
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Public API
12
+ // ---------------------------------------------------------------------------
13
+
14
+ /**
15
+ * Set (insert or replace) a configuration value.
16
+ * The value is JSON-serialised before storage.
17
+ */
18
+ export function setConfig(key: string, value: any, db?: Database): void {
19
+ const d = db || getDb();
20
+ const stmt = d.prepare(`
21
+ INSERT OR REPLACE INTO config (key, value, updated_at)
22
+ VALUES (?, ?, CURRENT_TIMESTAMP)
23
+ `);
24
+
25
+ stmt.run(key, JSON.stringify(value));
26
+ }
27
+
28
+ /**
29
+ * Retrieve a single configuration value by key.
30
+ * Returns `null` when the key does not exist.
31
+ */
32
+ export function getConfig(key: string, db?: Database): any | null {
33
+ const d = db || getDb();
34
+ const stmt = d.prepare('SELECT value FROM config WHERE key = ?');
35
+ const row: any = stmt.get(key);
36
+
37
+ if (!row) {
38
+ return null;
39
+ }
40
+
41
+ try {
42
+ return JSON.parse(row.value);
43
+ } catch {
44
+ // Corrupted config value — treat as missing
45
+ return null;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Retrieve every configuration entry as a flat key-value object.
51
+ */
52
+ export function getAllConfig(db?: Database): Record<string, any> {
53
+ const d = db || getDb();
54
+ const stmt = d.prepare('SELECT key, value FROM config');
55
+ const rows: any[] = stmt.all() as any[];
56
+
57
+ const config: Record<string, any> = {};
58
+ for (const row of rows) {
59
+ try {
60
+ config[row.key] = JSON.parse(row.value);
61
+ } catch {
62
+ // Skip corrupted config entries
63
+ }
64
+ }
65
+
66
+ return config;
67
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Re-export conversation helpers from messages.ts for clarity.
3
+ *
4
+ * Consumers can import from either `./messages` or `./conversations`
5
+ * depending on which name reads better at the call-site.
6
+ */
7
+
8
+ export {
9
+ saveConversation,
10
+ getConversation,
11
+ listConversations,
12
+ deleteConversation,
13
+ } from './messages';
14
+ export type { ConversationRecord } from './messages';