@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.
- package/LICENSE +21 -0
- package/README.md +628 -0
- package/bin/nimbus +38 -0
- package/package.json +80 -0
- package/src/__tests__/app.test.ts +76 -0
- package/src/__tests__/audit.test.ts +877 -0
- package/src/__tests__/circuit-breaker.test.ts +116 -0
- package/src/__tests__/cli-run.test.ts +115 -0
- package/src/__tests__/context-manager.test.ts +502 -0
- package/src/__tests__/context.test.ts +242 -0
- package/src/__tests__/enterprise.test.ts +401 -0
- package/src/__tests__/generator.test.ts +433 -0
- package/src/__tests__/hooks.test.ts +582 -0
- package/src/__tests__/init.test.ts +436 -0
- package/src/__tests__/intent-parser.test.ts +229 -0
- package/src/__tests__/llm-router.test.ts +209 -0
- package/src/__tests__/lsp.test.ts +293 -0
- package/src/__tests__/modes.test.ts +336 -0
- package/src/__tests__/permissions.test.ts +338 -0
- package/src/__tests__/serve.test.ts +275 -0
- package/src/__tests__/sessions.test.ts +227 -0
- package/src/__tests__/sharing.test.ts +288 -0
- package/src/__tests__/snapshots.test.ts +581 -0
- package/src/__tests__/state-db.test.ts +334 -0
- package/src/__tests__/stream-with-tools.test.ts +732 -0
- package/src/__tests__/subagents.test.ts +176 -0
- package/src/__tests__/system-prompt.test.ts +169 -0
- package/src/__tests__/tool-converter.test.ts +256 -0
- package/src/__tests__/tool-schemas.test.ts +397 -0
- package/src/__tests__/tools.test.ts +143 -0
- package/src/__tests__/version.test.ts +49 -0
- package/src/agent/compaction-agent.ts +227 -0
- package/src/agent/context-manager.ts +435 -0
- package/src/agent/context.ts +427 -0
- package/src/agent/deploy-preview.ts +426 -0
- package/src/agent/index.ts +68 -0
- package/src/agent/loop.ts +717 -0
- package/src/agent/modes.ts +429 -0
- package/src/agent/permissions.ts +466 -0
- package/src/agent/subagents/base.ts +116 -0
- package/src/agent/subagents/cost.ts +51 -0
- package/src/agent/subagents/explore.ts +42 -0
- package/src/agent/subagents/general.ts +54 -0
- package/src/agent/subagents/index.ts +102 -0
- package/src/agent/subagents/infra.ts +59 -0
- package/src/agent/subagents/security.ts +69 -0
- package/src/agent/system-prompt.ts +436 -0
- package/src/app.ts +122 -0
- package/src/audit/activity-log.ts +290 -0
- package/src/audit/compliance-checker.ts +540 -0
- package/src/audit/cost-tracker.ts +318 -0
- package/src/audit/index.ts +23 -0
- package/src/audit/security-scanner.ts +596 -0
- package/src/auth/guard.ts +75 -0
- package/src/auth/index.ts +56 -0
- package/src/auth/oauth.ts +455 -0
- package/src/auth/providers.ts +470 -0
- package/src/auth/sso.ts +113 -0
- package/src/auth/store.ts +505 -0
- package/src/auth/types.ts +187 -0
- package/src/build.ts +141 -0
- package/src/cli/index.ts +16 -0
- package/src/cli/init.ts +854 -0
- package/src/cli/openapi-spec.ts +356 -0
- package/src/cli/run.ts +237 -0
- package/src/cli/serve-auth.ts +80 -0
- package/src/cli/serve.ts +462 -0
- package/src/cli/web.ts +67 -0
- package/src/cli.ts +1417 -0
- package/src/clients/core-engine-client.ts +227 -0
- package/src/clients/enterprise-client.ts +334 -0
- package/src/clients/generator-client.ts +351 -0
- package/src/clients/git-client.ts +627 -0
- package/src/clients/github-client.ts +410 -0
- package/src/clients/helm-client.ts +504 -0
- package/src/clients/index.ts +80 -0
- package/src/clients/k8s-client.ts +497 -0
- package/src/clients/llm-client.ts +161 -0
- package/src/clients/rest-client.ts +130 -0
- package/src/clients/service-discovery.ts +33 -0
- package/src/clients/terraform-client.ts +482 -0
- package/src/clients/tools-client.ts +1843 -0
- package/src/clients/ws-client.ts +115 -0
- package/src/commands/analyze/index.ts +352 -0
- package/src/commands/apply/helm.ts +473 -0
- package/src/commands/apply/index.ts +213 -0
- package/src/commands/apply/k8s.ts +454 -0
- package/src/commands/apply/terraform.ts +582 -0
- package/src/commands/ask.ts +167 -0
- package/src/commands/audit/index.ts +238 -0
- package/src/commands/auth-cloud.ts +294 -0
- package/src/commands/auth-list.ts +134 -0
- package/src/commands/auth-profile.ts +121 -0
- package/src/commands/auth-status.ts +141 -0
- package/src/commands/aws/ec2.ts +501 -0
- package/src/commands/aws/iam.ts +397 -0
- package/src/commands/aws/index.ts +133 -0
- package/src/commands/aws/lambda.ts +396 -0
- package/src/commands/aws/rds.ts +439 -0
- package/src/commands/aws/s3.ts +439 -0
- package/src/commands/aws/vpc.ts +393 -0
- package/src/commands/aws-discover.ts +649 -0
- package/src/commands/aws-terraform.ts +805 -0
- package/src/commands/azure/aks.ts +376 -0
- package/src/commands/azure/functions.ts +253 -0
- package/src/commands/azure/index.ts +116 -0
- package/src/commands/azure/storage.ts +478 -0
- package/src/commands/azure/vm.ts +355 -0
- package/src/commands/billing/index.ts +256 -0
- package/src/commands/chat.ts +314 -0
- package/src/commands/config.ts +346 -0
- package/src/commands/cost/cloud-cost-estimator.ts +266 -0
- package/src/commands/cost/estimator.ts +79 -0
- package/src/commands/cost/index.ts +594 -0
- package/src/commands/cost/parsers/terraform.ts +273 -0
- package/src/commands/cost/parsers/types.ts +25 -0
- package/src/commands/cost/pricing/aws.ts +544 -0
- package/src/commands/cost/pricing/azure.ts +499 -0
- package/src/commands/cost/pricing/gcp.ts +396 -0
- package/src/commands/cost/pricing/index.ts +40 -0
- package/src/commands/demo.ts +250 -0
- package/src/commands/doctor.ts +794 -0
- package/src/commands/drift/index.ts +439 -0
- package/src/commands/explain.ts +277 -0
- package/src/commands/feedback.ts +389 -0
- package/src/commands/fix.ts +324 -0
- package/src/commands/fs/index.ts +402 -0
- package/src/commands/gcp/compute.ts +325 -0
- package/src/commands/gcp/functions.ts +271 -0
- package/src/commands/gcp/gke.ts +438 -0
- package/src/commands/gcp/iam.ts +344 -0
- package/src/commands/gcp/index.ts +129 -0
- package/src/commands/gcp/storage.ts +284 -0
- package/src/commands/generate-helm.ts +1249 -0
- package/src/commands/generate-k8s.ts +1560 -0
- package/src/commands/generate-terraform.ts +1460 -0
- package/src/commands/gh/index.ts +863 -0
- package/src/commands/git/index.ts +1343 -0
- package/src/commands/helm/index.ts +1126 -0
- package/src/commands/help.ts +539 -0
- package/src/commands/history.ts +142 -0
- package/src/commands/import.ts +868 -0
- package/src/commands/index.ts +367 -0
- package/src/commands/init.ts +1046 -0
- package/src/commands/k8s/index.ts +1137 -0
- package/src/commands/login.ts +631 -0
- package/src/commands/logout.ts +83 -0
- package/src/commands/onboarding.ts +228 -0
- package/src/commands/plan/display.ts +279 -0
- package/src/commands/plan/index.ts +599 -0
- package/src/commands/preview.ts +452 -0
- package/src/commands/questionnaire.ts +1270 -0
- package/src/commands/resume.ts +55 -0
- package/src/commands/team/index.ts +346 -0
- package/src/commands/template.ts +232 -0
- package/src/commands/tf/index.ts +1034 -0
- package/src/commands/upgrade.ts +550 -0
- package/src/commands/usage/index.ts +134 -0
- package/src/commands/version.ts +170 -0
- package/src/compat/index.ts +2 -0
- package/src/compat/runtime.ts +12 -0
- package/src/compat/sqlite.ts +107 -0
- package/src/config/index.ts +17 -0
- package/src/config/manager.ts +530 -0
- package/src/config/safety-policy.ts +358 -0
- package/src/config/schema.ts +125 -0
- package/src/config/types.ts +527 -0
- package/src/context/context-db.ts +199 -0
- package/src/demo/index.ts +349 -0
- package/src/demo/scenarios/full-journey.ts +229 -0
- package/src/demo/scenarios/getting-started.ts +127 -0
- package/src/demo/scenarios/helm-release.ts +341 -0
- package/src/demo/scenarios/k8s-deployment.ts +194 -0
- package/src/demo/scenarios/terraform-vpc.ts +170 -0
- package/src/demo/types.ts +92 -0
- package/src/engine/cost-estimator.ts +438 -0
- package/src/engine/diagram-generator.ts +256 -0
- package/src/engine/drift-detector.ts +902 -0
- package/src/engine/executor.ts +1035 -0
- package/src/engine/index.ts +76 -0
- package/src/engine/orchestrator.ts +636 -0
- package/src/engine/planner.ts +720 -0
- package/src/engine/safety.ts +743 -0
- package/src/engine/verifier.ts +770 -0
- package/src/enterprise/audit.ts +348 -0
- package/src/enterprise/auth.ts +270 -0
- package/src/enterprise/billing.ts +822 -0
- package/src/enterprise/index.ts +17 -0
- package/src/enterprise/teams.ts +443 -0
- package/src/generator/best-practices.ts +1608 -0
- package/src/generator/helm.ts +630 -0
- package/src/generator/index.ts +37 -0
- package/src/generator/intent-parser.ts +514 -0
- package/src/generator/kubernetes.ts +976 -0
- package/src/generator/terraform.ts +1867 -0
- package/src/history/index.ts +8 -0
- package/src/history/manager.ts +322 -0
- package/src/history/types.ts +34 -0
- package/src/hooks/config.ts +432 -0
- package/src/hooks/engine.ts +391 -0
- package/src/hooks/index.ts +4 -0
- package/src/llm/auth-bridge.ts +198 -0
- package/src/llm/circuit-breaker.ts +140 -0
- package/src/llm/config-loader.ts +201 -0
- package/src/llm/cost-calculator.ts +171 -0
- package/src/llm/index.ts +8 -0
- package/src/llm/model-aliases.ts +115 -0
- package/src/llm/provider-registry.ts +63 -0
- package/src/llm/providers/anthropic.ts +433 -0
- package/src/llm/providers/bedrock.ts +477 -0
- package/src/llm/providers/google.ts +405 -0
- package/src/llm/providers/ollama.ts +767 -0
- package/src/llm/providers/openai-compatible.ts +340 -0
- package/src/llm/providers/openai.ts +328 -0
- package/src/llm/providers/openrouter.ts +338 -0
- package/src/llm/router.ts +1035 -0
- package/src/llm/types.ts +232 -0
- package/src/lsp/client.ts +298 -0
- package/src/lsp/languages.ts +116 -0
- package/src/lsp/manager.ts +278 -0
- package/src/mcp/client.ts +402 -0
- package/src/mcp/index.ts +5 -0
- package/src/mcp/manager.ts +133 -0
- package/src/nimbus.ts +214 -0
- package/src/plugins/index.ts +27 -0
- package/src/plugins/loader.ts +334 -0
- package/src/plugins/manager.ts +376 -0
- package/src/plugins/types.ts +284 -0
- package/src/scanners/cicd-scanner.ts +258 -0
- package/src/scanners/cloud-scanner.ts +466 -0
- package/src/scanners/framework-scanner.ts +469 -0
- package/src/scanners/iac-scanner.ts +388 -0
- package/src/scanners/index.ts +539 -0
- package/src/scanners/language-scanner.ts +276 -0
- package/src/scanners/package-manager-scanner.ts +277 -0
- package/src/scanners/types.ts +172 -0
- package/src/sessions/manager.ts +365 -0
- package/src/sessions/types.ts +44 -0
- package/src/sharing/sync.ts +296 -0
- package/src/sharing/viewer.ts +97 -0
- package/src/snapshots/index.ts +2 -0
- package/src/snapshots/manager.ts +530 -0
- package/src/state/artifacts.ts +147 -0
- package/src/state/audit.ts +137 -0
- package/src/state/billing.ts +240 -0
- package/src/state/checkpoints.ts +117 -0
- package/src/state/config.ts +67 -0
- package/src/state/conversations.ts +14 -0
- package/src/state/credentials.ts +154 -0
- package/src/state/db.ts +58 -0
- package/src/state/index.ts +26 -0
- package/src/state/messages.ts +115 -0
- package/src/state/projects.ts +123 -0
- package/src/state/schema.ts +236 -0
- package/src/state/sessions.ts +147 -0
- package/src/state/teams.ts +200 -0
- package/src/telemetry.ts +108 -0
- package/src/tools/aws-ops.ts +952 -0
- package/src/tools/azure-ops.ts +579 -0
- package/src/tools/file-ops.ts +593 -0
- package/src/tools/gcp-ops.ts +625 -0
- package/src/tools/git-ops.ts +773 -0
- package/src/tools/github-ops.ts +799 -0
- package/src/tools/helm-ops.ts +943 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/k8s-ops.ts +819 -0
- package/src/tools/schemas/converter.ts +184 -0
- package/src/tools/schemas/devops.ts +612 -0
- package/src/tools/schemas/index.ts +73 -0
- package/src/tools/schemas/standard.ts +1144 -0
- package/src/tools/schemas/types.ts +705 -0
- package/src/tools/terraform-ops.ts +862 -0
- package/src/types/ambient.d.ts +193 -0
- package/src/types/config.ts +83 -0
- package/src/types/drift.ts +116 -0
- package/src/types/enterprise.ts +335 -0
- package/src/types/index.ts +20 -0
- package/src/types/plan.ts +44 -0
- package/src/types/request.ts +65 -0
- package/src/types/response.ts +54 -0
- package/src/types/service.ts +51 -0
- package/src/ui/App.tsx +997 -0
- package/src/ui/DeployPreview.tsx +169 -0
- package/src/ui/Header.tsx +68 -0
- package/src/ui/InputBox.tsx +350 -0
- package/src/ui/MessageList.tsx +585 -0
- package/src/ui/PermissionPrompt.tsx +151 -0
- package/src/ui/StatusBar.tsx +158 -0
- package/src/ui/ToolCallDisplay.tsx +409 -0
- package/src/ui/chat-ui.ts +853 -0
- package/src/ui/index.ts +33 -0
- package/src/ui/ink/index.ts +711 -0
- package/src/ui/streaming.ts +176 -0
- package/src/ui/types.ts +57 -0
- package/src/utils/analytics.ts +72 -0
- package/src/utils/cost-warning.ts +27 -0
- package/src/utils/env.ts +46 -0
- package/src/utils/errors.ts +69 -0
- package/src/utils/event-bus.ts +38 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/logger.ts +171 -0
- package/src/utils/rate-limiter.ts +121 -0
- package/src/utils/service-auth.ts +49 -0
- package/src/utils/validation.ts +53 -0
- package/src/version.ts +4 -0
- package/src/watcher/index.ts +163 -0
- package/src/wizard/approval.ts +383 -0
- package/src/wizard/index.ts +25 -0
- package/src/wizard/prompts.ts +338 -0
- package/src/wizard/types.ts +171 -0
- package/src/wizard/ui.ts +556 -0
- package/src/wizard/wizard.ts +304 -0
- 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';
|