@rkarim08/sia 1.0.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/.claude-plugin/marketplace.json +35 -0
- package/.claude-plugin/plugin.json +27 -0
- package/.mcp.json +13 -0
- package/CLAUDE.md +226 -0
- package/LICENSE +202 -0
- package/PLUGIN_README.md +253 -0
- package/README.md +1013 -0
- package/agents/sia-changelog-writer.md +89 -0
- package/agents/sia-code-reviewer.md +86 -0
- package/agents/sia-conflict-resolver.md +100 -0
- package/agents/sia-convention-enforcer.md +69 -0
- package/agents/sia-debug.md +106 -0
- package/agents/sia-decision-reviewer.md +101 -0
- package/agents/sia-dependency-tracker.md +80 -0
- package/agents/sia-explain.md +126 -0
- package/agents/sia-feature.md +116 -0
- package/agents/sia-knowledge-capture.md +117 -0
- package/agents/sia-lead-architecture-advisor.md +93 -0
- package/agents/sia-lead-team-health.md +107 -0
- package/agents/sia-migration.md +100 -0
- package/agents/sia-onboarding.md +115 -0
- package/agents/sia-orientation.md +99 -0
- package/agents/sia-pm-briefing.md +106 -0
- package/agents/sia-pm-risk-advisor.md +82 -0
- package/agents/sia-qa-analyst.md +116 -0
- package/agents/sia-qa-regression-map.md +94 -0
- package/agents/sia-refactor.md +115 -0
- package/agents/sia-regression.md +112 -0
- package/agents/sia-security-audit.md +125 -0
- package/agents/sia-test-advisor.md +91 -0
- package/hooks/hooks.json +98 -0
- package/migrations/bridge/001_initial.sql +34 -0
- package/migrations/episodic/001_initial.sql +35 -0
- package/migrations/meta/001_initial.sql +68 -0
- package/migrations/semantic/001_initial.sql +292 -0
- package/migrations/semantic/002_ontology.sql +89 -0
- package/migrations/semantic/003_freshness.sql +63 -0
- package/migrations/semantic/004_v5_unified_schema.sql +194 -0
- package/migrations/semantic/005_backfill_event_kinds.sql +8 -0
- package/migrations/semantic/006_tree_sitter.sql +6 -0
- package/migrations/semantic/007_branch_snapshots.sql +22 -0
- package/package.json +110 -0
- package/scripts/branch-switch.sh +13 -0
- package/scripts/build-wasm-grammars.sh +81 -0
- package/scripts/post-compact.sh +8 -0
- package/scripts/post-tool-use.sh +10 -0
- package/scripts/pre-compact.sh +8 -0
- package/scripts/session-end.sh +8 -0
- package/scripts/session-start.sh +8 -0
- package/scripts/start-mcp.ts +45 -0
- package/scripts/stop-hook.sh +8 -0
- package/scripts/user-prompt-submit.sh +8 -0
- package/scripts/viz-server.ts +152 -0
- package/skills/sia-brainstorm/SKILL.md +156 -0
- package/skills/sia-brainstorm/scripts/frame-template.html +214 -0
- package/skills/sia-brainstorm/scripts/helper.js +95 -0
- package/skills/sia-brainstorm/scripts/server.cjs +338 -0
- package/skills/sia-brainstorm/scripts/start-server.sh +153 -0
- package/skills/sia-brainstorm/scripts/stop-server.sh +55 -0
- package/skills/sia-brainstorm/spec-document-reviewer-prompt.md +49 -0
- package/skills/sia-brainstorm/visual-companion.md +286 -0
- package/skills/sia-capture/SKILL.md +64 -0
- package/skills/sia-compare/SKILL.md +33 -0
- package/skills/sia-conflicts/SKILL.md +38 -0
- package/skills/sia-debug-workflow/SKILL.md +120 -0
- package/skills/sia-debug-workflow/root-cause-tracing.md +70 -0
- package/skills/sia-debug-workflow/scripts/find-polluter.sh +64 -0
- package/skills/sia-debug-workflow/temporal-investigation.md +72 -0
- package/skills/sia-digest/SKILL.md +23 -0
- package/skills/sia-dispatch/SKILL.md +69 -0
- package/skills/sia-dispatch/agent-task-template.md +99 -0
- package/skills/sia-doctor/SKILL.md +39 -0
- package/skills/sia-execute/SKILL.md +70 -0
- package/skills/sia-execute-plan/SKILL.md +85 -0
- package/skills/sia-export-import/SKILL.md +49 -0
- package/skills/sia-export-knowledge/SKILL.md +46 -0
- package/skills/sia-finish/SKILL.md +100 -0
- package/skills/sia-finish/pr-summary-template.md +54 -0
- package/skills/sia-freshness/SKILL.md +38 -0
- package/skills/sia-history/SKILL.md +42 -0
- package/skills/sia-impact/SKILL.md +70 -0
- package/skills/sia-index/SKILL.md +54 -0
- package/skills/sia-install/SKILL.md +39 -0
- package/skills/sia-lead-compliance/SKILL.md +16 -0
- package/skills/sia-lead-drift-report/SKILL.md +16 -0
- package/skills/sia-lead-knowledge-map/SKILL.md +16 -0
- package/skills/sia-learn/SKILL.md +58 -0
- package/skills/sia-plan/SKILL.md +68 -0
- package/skills/sia-plan/plan-reviewer-prompt.md +63 -0
- package/skills/sia-playbooks/SKILL.md +29 -0
- package/skills/sia-playbooks/reference-feature.md +100 -0
- package/skills/sia-playbooks/reference-flagging.md +50 -0
- package/skills/sia-playbooks/reference-orientation.md +92 -0
- package/skills/sia-playbooks/reference-regression.md +115 -0
- package/skills/sia-playbooks/reference-review.md +64 -0
- package/skills/sia-playbooks/reference-tools.md +239 -0
- package/skills/sia-pm-decision-log/SKILL.md +28 -0
- package/skills/sia-pm-risk-dashboard/SKILL.md +24 -0
- package/skills/sia-pm-sprint-summary/SKILL.md +27 -0
- package/skills/sia-prune/SKILL.md +45 -0
- package/skills/sia-qa-coverage/SKILL.md +28 -0
- package/skills/sia-qa-flaky/SKILL.md +20 -0
- package/skills/sia-qa-report/SKILL.md +26 -0
- package/skills/sia-reindex/SKILL.md +30 -0
- package/skills/sia-review-respond/SKILL.md +88 -0
- package/skills/sia-review-respond/pushback-patterns.md +90 -0
- package/skills/sia-search/SKILL.md +47 -0
- package/skills/sia-setup/SKILL.md +82 -0
- package/skills/sia-setup/setup-checklist.md +97 -0
- package/skills/sia-stats/SKILL.md +36 -0
- package/skills/sia-status/SKILL.md +44 -0
- package/skills/sia-sync/SKILL.md +46 -0
- package/skills/sia-team/SKILL.md +64 -0
- package/skills/sia-test/SKILL.md +92 -0
- package/skills/sia-test/testing-anti-patterns.md +104 -0
- package/skills/sia-tour/SKILL.md +29 -0
- package/skills/sia-upgrade/SKILL.md +43 -0
- package/skills/sia-verify/SKILL.md +81 -0
- package/skills/sia-visualize/SKILL.md +28 -0
- package/skills/sia-visualize-live/SKILL.md +55 -0
- package/skills/sia-visualize-live/scripts/graph-template.html +389 -0
- package/skills/sia-visualize-live/scripts/start-visualizer.sh +161 -0
- package/skills/sia-visualize-live/scripts/stop-visualizer.sh +55 -0
- package/skills/sia-visualize-live/scripts/visualizer-server.cjs +264 -0
- package/skills/sia-workspace/SKILL.md +57 -0
- package/src/agent/claude-md-template-flagging.md +219 -0
- package/src/agent/claude-md-template.md +213 -0
- package/src/agent/modules/sia-feature.md +100 -0
- package/src/agent/modules/sia-flagging.md +50 -0
- package/src/agent/modules/sia-orientation.md +92 -0
- package/src/agent/modules/sia-regression.md +115 -0
- package/src/agent/modules/sia-review.md +64 -0
- package/src/agent/modules/sia-tools.md +239 -0
- package/src/ast/extractors/c-include.ts +189 -0
- package/src/ast/extractors/csharp-project.ts +260 -0
- package/src/ast/extractors/prisma-schema.ts +44 -0
- package/src/ast/extractors/project-manifest.ts +111 -0
- package/src/ast/extractors/sql-schema.ts +67 -0
- package/src/ast/extractors/tier-a.ts +423 -0
- package/src/ast/extractors/tier-b.ts +289 -0
- package/src/ast/extractors/tier-dispatch.ts +247 -0
- package/src/ast/index-worker.ts +108 -0
- package/src/ast/indexer.ts +484 -0
- package/src/ast/languages.ts +408 -0
- package/src/ast/pagerank-builder.ts +125 -0
- package/src/ast/path-utils.ts +137 -0
- package/src/ast/tree-sitter/backends/native.ts +57 -0
- package/src/ast/tree-sitter/backends/wasm.ts +39 -0
- package/src/ast/tree-sitter/call-walker.ts +44 -0
- package/src/ast/tree-sitter/edit-computer.ts +55 -0
- package/src/ast/tree-sitter/query-runner.ts +46 -0
- package/src/ast/tree-sitter/service.ts +174 -0
- package/src/ast/tree-sitter/tree-cache.ts +39 -0
- package/src/ast/tree-sitter/types.ts +79 -0
- package/src/ast/watcher.ts +322 -0
- package/src/capture/chunker.ts +169 -0
- package/src/capture/consolidate.ts +127 -0
- package/src/capture/edge-inferrer.ts +161 -0
- package/src/capture/embedder.ts +166 -0
- package/src/capture/embedding-cache.ts +73 -0
- package/src/capture/flag-processor.ts +64 -0
- package/src/capture/hook.ts +67 -0
- package/src/capture/pipeline.ts +450 -0
- package/src/capture/prompts/consolidate.ts +25 -0
- package/src/capture/prompts/edge-infer.ts +29 -0
- package/src/capture/prompts/extract-flagged.ts +36 -0
- package/src/capture/prompts/extract.ts +42 -0
- package/src/capture/tokenizer.ts +147 -0
- package/src/capture/track-a-ast.ts +93 -0
- package/src/capture/track-b-llm.ts +149 -0
- package/src/capture/types.ts +64 -0
- package/src/cli/commands/community.ts +137 -0
- package/src/cli/commands/compare.ts +123 -0
- package/src/cli/commands/conflicts.ts +41 -0
- package/src/cli/commands/digest.ts +197 -0
- package/src/cli/commands/disable-flagging.ts +34 -0
- package/src/cli/commands/doctor.ts +240 -0
- package/src/cli/commands/download-model.ts +161 -0
- package/src/cli/commands/enable-flagging.ts +34 -0
- package/src/cli/commands/export-knowledge.ts +208 -0
- package/src/cli/commands/export.ts +85 -0
- package/src/cli/commands/freshness.ts +164 -0
- package/src/cli/commands/graph.ts +51 -0
- package/src/cli/commands/history.ts +139 -0
- package/src/cli/commands/import.ts +335 -0
- package/src/cli/commands/install.ts +156 -0
- package/src/cli/commands/lead-report.ts +241 -0
- package/src/cli/commands/learn.ts +321 -0
- package/src/cli/commands/pm-report.ts +413 -0
- package/src/cli/commands/prune.ts +75 -0
- package/src/cli/commands/qa-report.ts +278 -0
- package/src/cli/commands/reindex.ts +104 -0
- package/src/cli/commands/rollback.ts +70 -0
- package/src/cli/commands/search.ts +103 -0
- package/src/cli/commands/server.ts +91 -0
- package/src/cli/commands/share.ts +33 -0
- package/src/cli/commands/stats.ts +79 -0
- package/src/cli/commands/status.ts +176 -0
- package/src/cli/commands/sync.ts +96 -0
- package/src/cli/commands/team.ts +118 -0
- package/src/cli/commands/tour.ts +157 -0
- package/src/cli/commands/visualize-live.ts +162 -0
- package/src/cli/commands/workspace.ts +117 -0
- package/src/cli/index.ts +424 -0
- package/src/cli/learn-progress.ts +87 -0
- package/src/community/detection-bridge.ts +344 -0
- package/src/community/leiden.ts +462 -0
- package/src/community/raptor.ts +210 -0
- package/src/community/scheduler.ts +74 -0
- package/src/community/summarize.ts +115 -0
- package/src/decay/archiver.ts +73 -0
- package/src/decay/bridge-orphan-cleanup.ts +212 -0
- package/src/decay/consolidation-sweep.ts +112 -0
- package/src/decay/decay.ts +116 -0
- package/src/decay/deep-validator.ts +62 -0
- package/src/decay/episodic-promoter.ts +132 -0
- package/src/decay/maintenance-scheduler.ts +326 -0
- package/src/decay/scheduler.ts +6 -0
- package/src/decay/session-sweeper.ts +79 -0
- package/src/decay/types.ts +17 -0
- package/src/freshness/confidence-decay.ts +122 -0
- package/src/freshness/cuckoo-filter.ts +176 -0
- package/src/freshness/deep-validation.ts +345 -0
- package/src/freshness/dirty-tracker.ts +237 -0
- package/src/freshness/file-watcher-layer.ts +119 -0
- package/src/freshness/firewall.ts +64 -0
- package/src/freshness/git-reconcile-layer.ts +161 -0
- package/src/freshness/inverted-index.ts +158 -0
- package/src/freshness/stale-read-layer.ts +222 -0
- package/src/graph/audit.ts +69 -0
- package/src/graph/bridge-db.ts +141 -0
- package/src/graph/communities.ts +195 -0
- package/src/graph/db-interface.ts +259 -0
- package/src/graph/edges.ts +163 -0
- package/src/graph/entities.ts +327 -0
- package/src/graph/episodic-db.ts +113 -0
- package/src/graph/flags.ts +31 -0
- package/src/graph/meta-db.ts +200 -0
- package/src/graph/semantic-db.ts +101 -0
- package/src/graph/session-resume.ts +56 -0
- package/src/graph/snapshots.ts +342 -0
- package/src/graph/staging.ts +151 -0
- package/src/graph/types.ts +128 -0
- package/src/hooks/adapters/claude-code.ts +21 -0
- package/src/hooks/adapters/cline.ts +43 -0
- package/src/hooks/adapters/cursor.ts +65 -0
- package/src/hooks/adapters/generic.ts +12 -0
- package/src/hooks/agent-detect.ts +34 -0
- package/src/hooks/claude-md-directives.ts +32 -0
- package/src/hooks/event-router.ts +182 -0
- package/src/hooks/extractors/pattern-detector.ts +111 -0
- package/src/hooks/handlers/post-compact.ts +30 -0
- package/src/hooks/handlers/post-tool-use.ts +403 -0
- package/src/hooks/handlers/pre-compact.ts +100 -0
- package/src/hooks/handlers/session-end.ts +47 -0
- package/src/hooks/handlers/session-start.ts +154 -0
- package/src/hooks/handlers/stop.ts +128 -0
- package/src/hooks/handlers/user-prompt-submit.ts +68 -0
- package/src/hooks/plugin-branch-switch.ts +68 -0
- package/src/hooks/plugin-common.ts +47 -0
- package/src/hooks/plugin-post-compact.ts +28 -0
- package/src/hooks/plugin-post-tool-use.ts +38 -0
- package/src/hooks/plugin-pre-compact.ts +37 -0
- package/src/hooks/plugin-session-end.ts +37 -0
- package/src/hooks/plugin-session-start.ts +75 -0
- package/src/hooks/plugin-stop.ts +61 -0
- package/src/hooks/plugin-user-prompt-submit.ts +47 -0
- package/src/hooks/types.ts +43 -0
- package/src/knowledge/discovery.ts +238 -0
- package/src/knowledge/external-refs.ts +98 -0
- package/src/knowledge/freshness.ts +221 -0
- package/src/knowledge/ingest.ts +330 -0
- package/src/knowledge/markdown-export.ts +229 -0
- package/src/knowledge/markdown-import.ts +359 -0
- package/src/knowledge/patterns.ts +74 -0
- package/src/knowledge/templates.ts +307 -0
- package/src/llm/ai-sdk-adapter.ts +46 -0
- package/src/llm/config.ts +88 -0
- package/src/llm/cost-tracker.ts +110 -0
- package/src/llm/prompts/extraction.ts +55 -0
- package/src/llm/prompts/summarization.ts +36 -0
- package/src/llm/prompts/validation.ts +37 -0
- package/src/llm/provider-registry.ts +68 -0
- package/src/llm/reliability.ts +179 -0
- package/src/llm/schemas.ts +52 -0
- package/src/mcp/freshness-annotator.ts +69 -0
- package/src/mcp/server.ts +949 -0
- package/src/mcp/tools/sia-ast-query.ts +225 -0
- package/src/mcp/tools/sia-at-time.ts +151 -0
- package/src/mcp/tools/sia-backlinks.ts +87 -0
- package/src/mcp/tools/sia-batch-execute.ts +169 -0
- package/src/mcp/tools/sia-by-file.ts +89 -0
- package/src/mcp/tools/sia-community.ts +113 -0
- package/src/mcp/tools/sia-doctor.ts +73 -0
- package/src/mcp/tools/sia-execute-file.ts +122 -0
- package/src/mcp/tools/sia-execute.ts +104 -0
- package/src/mcp/tools/sia-expand.ts +158 -0
- package/src/mcp/tools/sia-fetch-and-index.ts +241 -0
- package/src/mcp/tools/sia-flag.ts +65 -0
- package/src/mcp/tools/sia-index.ts +111 -0
- package/src/mcp/tools/sia-note.ts +134 -0
- package/src/mcp/tools/sia-search.ts +105 -0
- package/src/mcp/tools/sia-stats.ts +63 -0
- package/src/mcp/tools/sia-sync-status.ts +44 -0
- package/src/mcp/tools/sia-upgrade.ts +247 -0
- package/src/mcp/truncate.ts +231 -0
- package/src/native/bridge.ts +167 -0
- package/src/native/fallback-ast-diff.ts +144 -0
- package/src/native/fallback-graph.ts +325 -0
- package/src/ontology/constraints.ts +56 -0
- package/src/ontology/errors.ts +8 -0
- package/src/ontology/middleware.ts +266 -0
- package/src/retrieval/bm25-search.ts +151 -0
- package/src/retrieval/context-assembly.ts +76 -0
- package/src/retrieval/graph-traversal.ts +168 -0
- package/src/retrieval/pagerank.ts +40 -0
- package/src/retrieval/query-classifier.ts +106 -0
- package/src/retrieval/reranker.ts +156 -0
- package/src/retrieval/search.ts +236 -0
- package/src/retrieval/throttle.ts +102 -0
- package/src/retrieval/vector-search.ts +203 -0
- package/src/retrieval/workspace-search.ts +130 -0
- package/src/sandbox/context-mode.ts +285 -0
- package/src/sandbox/credential-pass.ts +55 -0
- package/src/sandbox/executor.ts +235 -0
- package/src/security/pattern-detector.ts +127 -0
- package/src/security/rule-of-two.ts +50 -0
- package/src/security/sanitize.ts +46 -0
- package/src/security/semantic-consistency.ts +93 -0
- package/src/security/staging-promoter.ts +154 -0
- package/src/shared/config.ts +302 -0
- package/src/shared/diagnostics.ts +210 -0
- package/src/shared/errors.ts +48 -0
- package/src/shared/git-utils.ts +143 -0
- package/src/shared/llm-client.ts +120 -0
- package/src/shared/logger.ts +99 -0
- package/src/shared/types.ts +79 -0
- package/src/sync/client.ts +43 -0
- package/src/sync/conflict.ts +106 -0
- package/src/sync/dedup.ts +183 -0
- package/src/sync/hlc.ts +117 -0
- package/src/sync/keychain.ts +144 -0
- package/src/sync/pull.ts +232 -0
- package/src/sync/push.ts +131 -0
- package/src/types/chokidar.d.ts +23 -0
- package/src/visualization/graph-renderer.ts +312 -0
- package/src/visualization/subgraph-extract.ts +208 -0
- package/src/visualization/views/community-clusters.ts +246 -0
- package/src/visualization/views/dependency-map.ts +189 -0
- package/src/visualization/views/graph-explorer.ts +364 -0
- package/src/visualization/views/timeline.ts +247 -0
- package/src/workspace/api-contracts.ts +226 -0
- package/src/workspace/cross-repo.ts +61 -0
- package/src/workspace/detector.ts +190 -0
- package/src/workspace/manifest.ts +141 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// Module: entities — Entity CRUD layer with bi-temporal invalidation
|
|
2
|
+
|
|
3
|
+
import { v4 as uuid } from "uuid";
|
|
4
|
+
import { writeAuditEntry } from "@/graph/audit";
|
|
5
|
+
import type { SiaDb } from "@/graph/db-interface";
|
|
6
|
+
|
|
7
|
+
/** Shape of an entity row as stored in the database. */
|
|
8
|
+
export interface Entity {
|
|
9
|
+
id: string;
|
|
10
|
+
type: string;
|
|
11
|
+
name: string;
|
|
12
|
+
content: string;
|
|
13
|
+
summary: string;
|
|
14
|
+
package_path: string | null;
|
|
15
|
+
tags: string;
|
|
16
|
+
file_paths: string;
|
|
17
|
+
trust_tier: number;
|
|
18
|
+
confidence: number;
|
|
19
|
+
base_confidence: number;
|
|
20
|
+
importance: number;
|
|
21
|
+
base_importance: number;
|
|
22
|
+
access_count: number;
|
|
23
|
+
edge_count: number;
|
|
24
|
+
last_accessed: number;
|
|
25
|
+
created_at: number;
|
|
26
|
+
t_created: number;
|
|
27
|
+
t_expired: number | null;
|
|
28
|
+
t_valid_from: number | null;
|
|
29
|
+
t_valid_until: number | null;
|
|
30
|
+
visibility: string;
|
|
31
|
+
created_by: string;
|
|
32
|
+
workspace_scope: string | null;
|
|
33
|
+
hlc_created: number | null;
|
|
34
|
+
hlc_modified: number | null;
|
|
35
|
+
synced_at: number | null;
|
|
36
|
+
conflict_group_id: string | null;
|
|
37
|
+
source_episode: string | null;
|
|
38
|
+
extraction_method: string | null;
|
|
39
|
+
extraction_model: string | null;
|
|
40
|
+
embedding: Uint8Array | null;
|
|
41
|
+
archived_at: number | null;
|
|
42
|
+
session_id: string | null;
|
|
43
|
+
kind: string | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Fields the caller must or may provide when inserting an entity. */
|
|
47
|
+
export interface InsertEntityInput {
|
|
48
|
+
type: string;
|
|
49
|
+
name: string;
|
|
50
|
+
content: string;
|
|
51
|
+
summary: string;
|
|
52
|
+
package_path?: string | null;
|
|
53
|
+
tags?: string;
|
|
54
|
+
file_paths?: string;
|
|
55
|
+
trust_tier?: number;
|
|
56
|
+
confidence?: number;
|
|
57
|
+
base_confidence?: number;
|
|
58
|
+
importance?: number;
|
|
59
|
+
base_importance?: number;
|
|
60
|
+
access_count?: number;
|
|
61
|
+
edge_count?: number;
|
|
62
|
+
last_accessed?: number;
|
|
63
|
+
created_at?: number;
|
|
64
|
+
t_valid_from?: number | null;
|
|
65
|
+
visibility?: string;
|
|
66
|
+
created_by?: string;
|
|
67
|
+
workspace_scope?: string | null;
|
|
68
|
+
hlc_created?: number | null;
|
|
69
|
+
hlc_modified?: number | null;
|
|
70
|
+
source_episode?: string | null;
|
|
71
|
+
extraction_method?: string | null;
|
|
72
|
+
extraction_model?: string | null;
|
|
73
|
+
embedding?: Uint8Array | null;
|
|
74
|
+
session_id?: string | null;
|
|
75
|
+
kind?: string | null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Fields that can be partially updated on an existing entity. */
|
|
79
|
+
export type UpdateEntityInput = Partial<Omit<Entity, "id" | "t_created" | "created_at">>;
|
|
80
|
+
|
|
81
|
+
/** Options for getActiveEntities. */
|
|
82
|
+
export interface GetActiveEntitiesOpts {
|
|
83
|
+
limit?: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Insert a new entity into the graph database.
|
|
88
|
+
* Generates a UUID, sets t_created=now, t_valid_from as provided (default null),
|
|
89
|
+
* t_valid_until=null. Writes an ADD entry to the audit log.
|
|
90
|
+
*/
|
|
91
|
+
export async function insertEntity(db: SiaDb, input: InsertEntityInput): Promise<Entity> {
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
const id = uuid();
|
|
94
|
+
|
|
95
|
+
const entity: Entity = {
|
|
96
|
+
id,
|
|
97
|
+
type: input.type,
|
|
98
|
+
name: input.name,
|
|
99
|
+
content: input.content,
|
|
100
|
+
summary: input.summary,
|
|
101
|
+
package_path: input.package_path ?? null,
|
|
102
|
+
tags: input.tags ?? "[]",
|
|
103
|
+
file_paths: input.file_paths ?? "[]",
|
|
104
|
+
trust_tier: input.trust_tier ?? 3,
|
|
105
|
+
confidence: input.confidence ?? 0.7,
|
|
106
|
+
base_confidence: input.base_confidence ?? 0.7,
|
|
107
|
+
importance: input.importance ?? 0.5,
|
|
108
|
+
base_importance: input.base_importance ?? 0.5,
|
|
109
|
+
access_count: input.access_count ?? 0,
|
|
110
|
+
edge_count: input.edge_count ?? 0,
|
|
111
|
+
last_accessed: input.last_accessed ?? now,
|
|
112
|
+
created_at: input.created_at ?? now,
|
|
113
|
+
t_created: now,
|
|
114
|
+
t_expired: null,
|
|
115
|
+
t_valid_from: input.t_valid_from ?? null,
|
|
116
|
+
t_valid_until: null,
|
|
117
|
+
visibility: input.visibility ?? "private",
|
|
118
|
+
created_by: input.created_by ?? "local",
|
|
119
|
+
workspace_scope: input.workspace_scope ?? null,
|
|
120
|
+
hlc_created: input.hlc_created ?? null,
|
|
121
|
+
hlc_modified: input.hlc_modified ?? null,
|
|
122
|
+
synced_at: null,
|
|
123
|
+
conflict_group_id: null,
|
|
124
|
+
source_episode: input.source_episode ?? null,
|
|
125
|
+
extraction_method: input.extraction_method ?? null,
|
|
126
|
+
extraction_model: input.extraction_model ?? null,
|
|
127
|
+
embedding: input.embedding ?? null,
|
|
128
|
+
archived_at: null,
|
|
129
|
+
session_id: input.session_id ?? null,
|
|
130
|
+
kind: input.kind ?? input.type,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
await db.execute(
|
|
134
|
+
`INSERT INTO graph_nodes (
|
|
135
|
+
id, type, name, content, summary,
|
|
136
|
+
package_path, tags, file_paths,
|
|
137
|
+
trust_tier, confidence, base_confidence,
|
|
138
|
+
importance, base_importance,
|
|
139
|
+
access_count, edge_count,
|
|
140
|
+
last_accessed, created_at,
|
|
141
|
+
t_created, t_expired, t_valid_from, t_valid_until,
|
|
142
|
+
visibility, created_by, workspace_scope,
|
|
143
|
+
hlc_created, hlc_modified, synced_at,
|
|
144
|
+
conflict_group_id,
|
|
145
|
+
source_episode, extraction_method, extraction_model,
|
|
146
|
+
embedding, archived_at,
|
|
147
|
+
session_id, kind
|
|
148
|
+
) VALUES (
|
|
149
|
+
?, ?, ?, ?, ?,
|
|
150
|
+
?, ?, ?,
|
|
151
|
+
?, ?, ?,
|
|
152
|
+
?, ?,
|
|
153
|
+
?, ?,
|
|
154
|
+
?, ?,
|
|
155
|
+
?, ?, ?, ?,
|
|
156
|
+
?, ?, ?,
|
|
157
|
+
?, ?, ?,
|
|
158
|
+
?,
|
|
159
|
+
?, ?, ?,
|
|
160
|
+
?, ?,
|
|
161
|
+
?, ?
|
|
162
|
+
)`,
|
|
163
|
+
[
|
|
164
|
+
entity.id,
|
|
165
|
+
entity.type,
|
|
166
|
+
entity.name,
|
|
167
|
+
entity.content,
|
|
168
|
+
entity.summary,
|
|
169
|
+
entity.package_path,
|
|
170
|
+
entity.tags,
|
|
171
|
+
entity.file_paths,
|
|
172
|
+
entity.trust_tier,
|
|
173
|
+
entity.confidence,
|
|
174
|
+
entity.base_confidence,
|
|
175
|
+
entity.importance,
|
|
176
|
+
entity.base_importance,
|
|
177
|
+
entity.access_count,
|
|
178
|
+
entity.edge_count,
|
|
179
|
+
entity.last_accessed,
|
|
180
|
+
entity.created_at,
|
|
181
|
+
entity.t_created,
|
|
182
|
+
entity.t_expired,
|
|
183
|
+
entity.t_valid_from,
|
|
184
|
+
entity.t_valid_until,
|
|
185
|
+
entity.visibility,
|
|
186
|
+
entity.created_by,
|
|
187
|
+
entity.workspace_scope,
|
|
188
|
+
entity.hlc_created,
|
|
189
|
+
entity.hlc_modified,
|
|
190
|
+
entity.synced_at,
|
|
191
|
+
entity.conflict_group_id,
|
|
192
|
+
entity.source_episode,
|
|
193
|
+
entity.extraction_method,
|
|
194
|
+
entity.extraction_model,
|
|
195
|
+
entity.embedding,
|
|
196
|
+
entity.archived_at,
|
|
197
|
+
entity.session_id,
|
|
198
|
+
entity.kind,
|
|
199
|
+
],
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
await writeAuditEntry(db, "ADD", { entity_id: id });
|
|
203
|
+
|
|
204
|
+
return entity;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Retrieve a single entity by id.
|
|
209
|
+
* Returns the entity or undefined if not found.
|
|
210
|
+
*/
|
|
211
|
+
export async function getEntity(db: SiaDb, id: string): Promise<Entity | undefined> {
|
|
212
|
+
const result = await db.execute("SELECT * FROM graph_nodes WHERE id = ?", [id]);
|
|
213
|
+
return (result.rows[0] as unknown as Entity | undefined) ?? undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Update partial fields on an existing entity.
|
|
218
|
+
* Writes an UPDATE entry to the audit log.
|
|
219
|
+
*/
|
|
220
|
+
export async function updateEntity(
|
|
221
|
+
db: SiaDb,
|
|
222
|
+
id: string,
|
|
223
|
+
updates: UpdateEntityInput,
|
|
224
|
+
): Promise<void> {
|
|
225
|
+
const entries = Object.entries(updates).filter(([, v]) => v !== undefined);
|
|
226
|
+
if (entries.length === 0) return;
|
|
227
|
+
|
|
228
|
+
const setClauses = entries.map(([key]) => `${key} = ?`).join(", ");
|
|
229
|
+
const values = entries.map(([, v]) => v);
|
|
230
|
+
|
|
231
|
+
await db.execute(`UPDATE graph_nodes SET ${setClauses} WHERE id = ?`, [...values, id]);
|
|
232
|
+
|
|
233
|
+
await writeAuditEntry(db, "UPDATE", { entity_id: id });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Touch an entity: update last_accessed to now and increment access_count.
|
|
238
|
+
*/
|
|
239
|
+
export async function touchEntity(db: SiaDb, id: string): Promise<void> {
|
|
240
|
+
await db.execute(
|
|
241
|
+
"UPDATE graph_nodes SET last_accessed = ?, access_count = access_count + 1 WHERE id = ?",
|
|
242
|
+
[Date.now(), id],
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Invalidate an entity (bi-temporal): sets both t_valid_until AND t_expired.
|
|
248
|
+
* Used when a fact is superseded or proven wrong.
|
|
249
|
+
* Does NOT set archived_at.
|
|
250
|
+
*/
|
|
251
|
+
export async function invalidateEntity(db: SiaDb, id: string, tValidUntil?: number): Promise<void> {
|
|
252
|
+
const ts = tValidUntil ?? Date.now();
|
|
253
|
+
await db.execute("UPDATE graph_nodes SET t_valid_until = ?, t_expired = ? WHERE id = ?", [
|
|
254
|
+
ts,
|
|
255
|
+
ts,
|
|
256
|
+
id,
|
|
257
|
+
]);
|
|
258
|
+
|
|
259
|
+
await writeAuditEntry(db, "INVALIDATE", { entity_id: id });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Archive an entity (soft delete for decayed/irrelevant entities).
|
|
264
|
+
* Sets archived_at only. Does NOT touch t_valid_until or t_expired.
|
|
265
|
+
*/
|
|
266
|
+
export async function archiveEntity(db: SiaDb, id: string): Promise<void> {
|
|
267
|
+
await db.execute("UPDATE graph_nodes SET archived_at = ? WHERE id = ?", [Date.now(), id]);
|
|
268
|
+
|
|
269
|
+
await writeAuditEntry(db, "ARCHIVE", { entity_id: id });
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Retrieve active entities: those that are neither invalidated nor archived.
|
|
274
|
+
* Filters WHERE t_valid_until IS NULL AND archived_at IS NULL.
|
|
275
|
+
*/
|
|
276
|
+
export async function getActiveEntities(
|
|
277
|
+
db: SiaDb,
|
|
278
|
+
opts?: GetActiveEntitiesOpts,
|
|
279
|
+
): Promise<Entity[]> {
|
|
280
|
+
const limit = opts?.limit;
|
|
281
|
+
const sql = limit
|
|
282
|
+
? "SELECT * FROM graph_nodes WHERE t_valid_until IS NULL AND archived_at IS NULL LIMIT ?"
|
|
283
|
+
: "SELECT * FROM graph_nodes WHERE t_valid_until IS NULL AND archived_at IS NULL";
|
|
284
|
+
const params = limit ? [limit] : [];
|
|
285
|
+
|
|
286
|
+
const result = await db.execute(sql, params);
|
|
287
|
+
return result.rows as unknown as Entity[];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Retrieve active entities scoped to a specific package path.
|
|
292
|
+
* Filters WHERE package_path = ? AND t_valid_until IS NULL AND archived_at IS NULL.
|
|
293
|
+
*/
|
|
294
|
+
export async function getEntitiesByPackage(db: SiaDb, packagePath: string): Promise<Entity[]> {
|
|
295
|
+
const result = await db.execute(
|
|
296
|
+
"SELECT * FROM graph_nodes WHERE package_path = ? AND t_valid_until IS NULL AND archived_at IS NULL",
|
|
297
|
+
[packagePath],
|
|
298
|
+
);
|
|
299
|
+
return result.rows as unknown as Entity[];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Retrieve active nodes belonging to a specific session.
|
|
304
|
+
* Filters WHERE session_id = ? AND archived_at IS NULL AND t_valid_until IS NULL.
|
|
305
|
+
*/
|
|
306
|
+
export async function getNodesBySession(
|
|
307
|
+
db: SiaDb,
|
|
308
|
+
sessionId: string,
|
|
309
|
+
): Promise<Record<string, unknown>[]> {
|
|
310
|
+
const { rows } = await db.execute(
|
|
311
|
+
"SELECT * FROM graph_nodes WHERE session_id = ? AND archived_at IS NULL AND t_valid_until IS NULL",
|
|
312
|
+
[sessionId],
|
|
313
|
+
);
|
|
314
|
+
return rows;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Retrieve active nodes of a specific semantic kind.
|
|
319
|
+
* Filters WHERE kind = ? AND archived_at IS NULL AND t_valid_until IS NULL.
|
|
320
|
+
*/
|
|
321
|
+
export async function getNodesByKind(db: SiaDb, kind: string): Promise<Record<string, unknown>[]> {
|
|
322
|
+
const { rows } = await db.execute(
|
|
323
|
+
"SELECT * FROM graph_nodes WHERE kind = ? AND archived_at IS NULL AND t_valid_until IS NULL",
|
|
324
|
+
[kind],
|
|
325
|
+
);
|
|
326
|
+
return rows;
|
|
327
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Module: episodic-db — Episode CRUD and session tracking
|
|
2
|
+
|
|
3
|
+
import { v4 as uuid } from "uuid";
|
|
4
|
+
import type { SiaDb } from "@/graph/db-interface";
|
|
5
|
+
import type { Episode, InsertEpisodeInput } from "@/graph/types";
|
|
6
|
+
|
|
7
|
+
// Re-export the opener (canonical source: semantic-db.ts)
|
|
8
|
+
export { openEpisodicDb } from "@/graph/semantic-db";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Insert a new episode into the episodic database.
|
|
12
|
+
* Generates a UUID and sets ts=Date.now().
|
|
13
|
+
*/
|
|
14
|
+
export async function insertEpisode(db: SiaDb, input: InsertEpisodeInput): Promise<Episode> {
|
|
15
|
+
const id = uuid();
|
|
16
|
+
const ts = Date.now();
|
|
17
|
+
|
|
18
|
+
const episode: Episode = {
|
|
19
|
+
id,
|
|
20
|
+
session_id: input.session_id,
|
|
21
|
+
ts,
|
|
22
|
+
type: input.type,
|
|
23
|
+
role: input.role ?? null,
|
|
24
|
+
content: input.content,
|
|
25
|
+
tool_name: input.tool_name ?? null,
|
|
26
|
+
file_path: input.file_path ?? null,
|
|
27
|
+
trust_tier: input.trust_tier ?? 3,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
await db.execute(
|
|
31
|
+
`INSERT INTO episodes (id, session_id, ts, type, role, content, tool_name, file_path, trust_tier)
|
|
32
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
33
|
+
[
|
|
34
|
+
episode.id,
|
|
35
|
+
episode.session_id,
|
|
36
|
+
episode.ts,
|
|
37
|
+
episode.type,
|
|
38
|
+
episode.role,
|
|
39
|
+
episode.content,
|
|
40
|
+
episode.tool_name,
|
|
41
|
+
episode.file_path,
|
|
42
|
+
episode.trust_tier,
|
|
43
|
+
],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return episode;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Retrieve all episodes for a given session, ordered by ts ASC.
|
|
51
|
+
*/
|
|
52
|
+
export async function getEpisodesBySession(db: SiaDb, sessionId: string): Promise<Episode[]> {
|
|
53
|
+
const result = await db.execute(
|
|
54
|
+
"SELECT id, session_id, ts, type, role, content, tool_name, file_path, trust_tier FROM episodes WHERE session_id = ? ORDER BY ts ASC",
|
|
55
|
+
[sessionId],
|
|
56
|
+
);
|
|
57
|
+
return result.rows as unknown as Episode[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Retrieve the most recent episodes across all sessions, ordered by ts DESC.
|
|
62
|
+
* Defaults to a limit of 20.
|
|
63
|
+
*/
|
|
64
|
+
export async function getRecentEpisodes(db: SiaDb, limit = 20): Promise<Episode[]> {
|
|
65
|
+
const result = await db.execute(
|
|
66
|
+
"SELECT id, session_id, ts, type, role, content, tool_name, file_path, trust_tier FROM episodes ORDER BY ts DESC LIMIT ?",
|
|
67
|
+
[limit],
|
|
68
|
+
);
|
|
69
|
+
return result.rows as unknown as Episode[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Mark a session as processed in sessions_processed.
|
|
74
|
+
* Uses an UPSERT so calling this multiple times updates the record.
|
|
75
|
+
* Defaults pipeline_version to "1.0.0" if not provided.
|
|
76
|
+
*/
|
|
77
|
+
export async function markSessionProcessed(
|
|
78
|
+
db: SiaDb,
|
|
79
|
+
sessionId: string,
|
|
80
|
+
status: "complete" | "partial" | "failed",
|
|
81
|
+
entityCount: number,
|
|
82
|
+
pipelineVersion?: string,
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
const processedAt = Date.now();
|
|
85
|
+
const version = pipelineVersion ?? "1.0.0";
|
|
86
|
+
|
|
87
|
+
await db.execute(
|
|
88
|
+
`INSERT INTO sessions_processed (session_id, processing_status, processed_at, entity_count, pipeline_version)
|
|
89
|
+
VALUES (?, ?, ?, ?, ?)
|
|
90
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
91
|
+
processing_status = excluded.processing_status,
|
|
92
|
+
processed_at = excluded.processed_at,
|
|
93
|
+
entity_count = excluded.entity_count,
|
|
94
|
+
pipeline_version = excluded.pipeline_version`,
|
|
95
|
+
[sessionId, status, processedAt, entityCount, version],
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Return session IDs that have not been successfully processed.
|
|
101
|
+
* Includes sessions that have never been processed (no entry in sessions_processed)
|
|
102
|
+
* and sessions whose processing_status is 'failed'.
|
|
103
|
+
*/
|
|
104
|
+
export async function getUnprocessedSessions(db: SiaDb): Promise<string[]> {
|
|
105
|
+
const result = await db.execute(
|
|
106
|
+
`SELECT DISTINCT e.session_id
|
|
107
|
+
FROM episodes e
|
|
108
|
+
LEFT JOIN sessions_processed sp ON sp.session_id = e.session_id
|
|
109
|
+
WHERE sp.session_id IS NULL OR sp.processing_status = 'failed'`,
|
|
110
|
+
[],
|
|
111
|
+
);
|
|
112
|
+
return (result.rows as Array<{ session_id: string }>).map((r) => r.session_id);
|
|
113
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Module: flags — Session flag CRUD operations
|
|
2
|
+
|
|
3
|
+
import type { SiaDb } from "@/graph/db-interface";
|
|
4
|
+
|
|
5
|
+
/** A session flag row from the session_flags table. */
|
|
6
|
+
export interface SessionFlag {
|
|
7
|
+
id: string;
|
|
8
|
+
session_id: string;
|
|
9
|
+
reason: string;
|
|
10
|
+
transcript_position: number | null;
|
|
11
|
+
created_at: number;
|
|
12
|
+
consumed: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Retrieve all unconsumed flags for a given session, ordered by creation time.
|
|
17
|
+
*/
|
|
18
|
+
export async function getUnconsumedFlags(db: SiaDb, sessionId: string): Promise<SessionFlag[]> {
|
|
19
|
+
const result = await db.execute(
|
|
20
|
+
"SELECT id, session_id, reason, transcript_position, created_at, consumed FROM session_flags WHERE session_id = ? AND consumed = 0 ORDER BY created_at",
|
|
21
|
+
[sessionId],
|
|
22
|
+
);
|
|
23
|
+
return result.rows as unknown as SessionFlag[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Mark a single flag as consumed so it is not returned by getUnconsumedFlags.
|
|
28
|
+
*/
|
|
29
|
+
export async function markFlagConsumed(db: SiaDb, flagId: string): Promise<void> {
|
|
30
|
+
await db.execute("UPDATE session_flags SET consumed = 1 WHERE id = ?", [flagId]);
|
|
31
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// Module: meta-db — Meta database opener and workspace/repo registry CRUD
|
|
2
|
+
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import type { BunSqliteDb, SiaDb } from "@/graph/db-interface";
|
|
6
|
+
import { runMigrations } from "@/graph/semantic-db";
|
|
7
|
+
import { SIA_HOME } from "@/shared/config";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Open (or create) the global meta database.
|
|
11
|
+
* Resolves to `{siaHome}/meta.db` (not under repos/) and applies
|
|
12
|
+
* migrations from the `migrations/meta` directory.
|
|
13
|
+
*/
|
|
14
|
+
export function openMetaDb(siaHome?: string): BunSqliteDb {
|
|
15
|
+
const home = siaHome ?? SIA_HOME;
|
|
16
|
+
const dbPath = join(home, "meta.db");
|
|
17
|
+
const migrationsDir = resolve(import.meta.dirname, "../../migrations/meta");
|
|
18
|
+
return runMigrations(dbPath, migrationsDir);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Repo registry
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register a repository by its absolute path.
|
|
27
|
+
* Computes a SHA-256 hash of the resolved path as the repo id.
|
|
28
|
+
* Idempotent — if the repo already exists, updates `last_accessed`.
|
|
29
|
+
* Returns the repo id (the hash).
|
|
30
|
+
*/
|
|
31
|
+
export async function registerRepo(db: SiaDb, path: string): Promise<string> {
|
|
32
|
+
const resolved = resolve(path);
|
|
33
|
+
const id = createHash("sha256").update(resolved).digest("hex");
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
|
|
36
|
+
await db.execute(
|
|
37
|
+
`INSERT INTO repos (id, path, created_at, last_accessed)
|
|
38
|
+
VALUES (?, ?, ?, ?)
|
|
39
|
+
ON CONFLICT(id) DO UPDATE SET last_accessed = ?`,
|
|
40
|
+
[id, resolved, now, now, now],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Look up a repo by its absolute path.
|
|
48
|
+
* Hashes the path and returns the row, or null if not found.
|
|
49
|
+
*/
|
|
50
|
+
export async function getRepoByPath(
|
|
51
|
+
db: SiaDb,
|
|
52
|
+
path: string,
|
|
53
|
+
): Promise<Record<string, unknown> | null> {
|
|
54
|
+
const resolved = resolve(path);
|
|
55
|
+
const id = createHash("sha256").update(resolved).digest("hex");
|
|
56
|
+
|
|
57
|
+
const result = await db.execute("SELECT * FROM repos WHERE id = ?", [id]);
|
|
58
|
+
return result.rows[0] ?? null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Workspace CRUD
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create a new workspace with the given name.
|
|
67
|
+
* Generates a UUID v4 as the workspace id. Returns the id.
|
|
68
|
+
*/
|
|
69
|
+
export async function createWorkspace(db: SiaDb, name: string): Promise<string> {
|
|
70
|
+
const id = randomUUID();
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
|
|
73
|
+
await db.execute("INSERT INTO workspaces (id, name, created_at) VALUES (?, ?, ?)", [
|
|
74
|
+
id,
|
|
75
|
+
name,
|
|
76
|
+
now,
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
return id;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolve a workspace name to its UUID.
|
|
84
|
+
* Returns the id if found, or null if no workspace with that name exists.
|
|
85
|
+
*/
|
|
86
|
+
export async function resolveWorkspaceName(db: SiaDb, name: string): Promise<string | null> {
|
|
87
|
+
const result = await db.execute("SELECT id FROM workspaces WHERE name = ?", [name]);
|
|
88
|
+
if (result.rows.length === 0) return null;
|
|
89
|
+
return result.rows[0]?.id as string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Workspace-repo associations
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Add a repo to a workspace (workspace_repos join table).
|
|
98
|
+
*/
|
|
99
|
+
export async function addRepoToWorkspace(
|
|
100
|
+
db: SiaDb,
|
|
101
|
+
workspaceId: string,
|
|
102
|
+
repoId: string,
|
|
103
|
+
): Promise<void> {
|
|
104
|
+
await db.execute("INSERT INTO workspace_repos (workspace_id, repo_id) VALUES (?, ?)", [
|
|
105
|
+
workspaceId,
|
|
106
|
+
repoId,
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Remove a repo from a workspace.
|
|
112
|
+
*/
|
|
113
|
+
export async function removeRepoFromWorkspace(
|
|
114
|
+
db: SiaDb,
|
|
115
|
+
workspaceId: string,
|
|
116
|
+
repoId: string,
|
|
117
|
+
): Promise<void> {
|
|
118
|
+
await db.execute("DELETE FROM workspace_repos WHERE workspace_id = ? AND repo_id = ?", [
|
|
119
|
+
workspaceId,
|
|
120
|
+
repoId,
|
|
121
|
+
]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get all repos belonging to a workspace (joined with the repos table).
|
|
126
|
+
*/
|
|
127
|
+
export async function getWorkspaceRepos(
|
|
128
|
+
db: SiaDb,
|
|
129
|
+
workspaceId: string,
|
|
130
|
+
): Promise<Record<string, unknown>[]> {
|
|
131
|
+
const result = await db.execute(
|
|
132
|
+
`SELECT r.*, wr.role
|
|
133
|
+
FROM repos r
|
|
134
|
+
INNER JOIN workspace_repos wr ON wr.repo_id = r.id
|
|
135
|
+
WHERE wr.workspace_id = ?`,
|
|
136
|
+
[workspaceId],
|
|
137
|
+
);
|
|
138
|
+
return result.rows;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Workspace listing
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
/** Row shape returned by listWorkspaces. */
|
|
146
|
+
export interface WorkspaceListItem {
|
|
147
|
+
id: string;
|
|
148
|
+
name: string;
|
|
149
|
+
created_at: number;
|
|
150
|
+
member_count: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* List all workspaces with their member counts.
|
|
155
|
+
*/
|
|
156
|
+
export async function listWorkspaces(db: SiaDb): Promise<WorkspaceListItem[]> {
|
|
157
|
+
const result = await db.execute(
|
|
158
|
+
`SELECT w.id, w.name, w.created_at,
|
|
159
|
+
COUNT(wr.repo_id) as member_count
|
|
160
|
+
FROM workspaces w
|
|
161
|
+
LEFT JOIN workspace_repos wr ON wr.workspace_id = w.id
|
|
162
|
+
GROUP BY w.id
|
|
163
|
+
ORDER BY w.name`,
|
|
164
|
+
);
|
|
165
|
+
return result.rows as unknown as WorkspaceListItem[];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get the count of api_contracts for repos in a workspace.
|
|
170
|
+
*/
|
|
171
|
+
export async function getWorkspaceContractCount(db: SiaDb, workspaceId: string): Promise<number> {
|
|
172
|
+
const result = await db.execute(
|
|
173
|
+
`SELECT COUNT(*) as cnt FROM api_contracts ac
|
|
174
|
+
WHERE ac.provider_repo_id IN (
|
|
175
|
+
SELECT repo_id FROM workspace_repos WHERE workspace_id = ?
|
|
176
|
+
)`,
|
|
177
|
+
[workspaceId],
|
|
178
|
+
);
|
|
179
|
+
return (result.rows[0]?.cnt as number) ?? 0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Sharing rules
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get sharing rules for a workspace.
|
|
188
|
+
* Returns rules that match the given workspace_id OR have a NULL workspace_id
|
|
189
|
+
* (global rules that apply to all workspaces).
|
|
190
|
+
*/
|
|
191
|
+
export async function getSharingRules(
|
|
192
|
+
db: SiaDb,
|
|
193
|
+
workspaceId: string,
|
|
194
|
+
): Promise<Record<string, unknown>[]> {
|
|
195
|
+
const result = await db.execute(
|
|
196
|
+
"SELECT * FROM sharing_rules WHERE workspace_id = ? OR workspace_id IS NULL",
|
|
197
|
+
[workspaceId],
|
|
198
|
+
);
|
|
199
|
+
return result.rows;
|
|
200
|
+
}
|