fuli-context 0.5.0 → 0.7.2
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/README.en.md +2 -333
- package/README.md +370 -202
- package/README.zh-CN.md +448 -0
- package/compose.graphiti.yml +6 -6
- package/dist/web/assets/AboutPage-DXh7Fwxt.css +1 -0
- package/dist/web/assets/AboutPage-DfnZo7Lo.js +1 -0
- package/dist/web/assets/ConnectionsPage-BloaSKmt.js +1 -0
- package/dist/web/assets/ConnectionsPage-CBHAdZXT.css +1 -0
- package/dist/web/assets/KnowledgeInspector-Bjhdu2mf.css +1 -0
- package/dist/web/assets/KnowledgeInspector-QvNpv0_k.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-Cx1w1jb2.css +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-DKq1jVD5.js +1 -0
- package/dist/web/assets/KnowledgePage-BWL0jT01.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-Czq6aZ7b.js +4 -0
- package/dist/web/assets/OverviewPage-5sJoO1Gh.js +1 -0
- package/dist/web/assets/{PersonalProfilePage-S8ZcG2LU.css → PersonalProfilePage-CdmEzyvZ.css} +1 -1
- package/dist/web/assets/PersonalProfilePage-tVChM9ik.js +1 -0
- package/dist/web/assets/PersonalProjectsPage-BPFrup4V.js +1 -0
- package/dist/web/assets/PublicProjectsPage-BPJSeREN.js +1 -0
- package/dist/web/assets/ReviewPage-B7EN817i.js +1 -0
- package/dist/web/assets/SearchableSelect-ClLN0rIb.js +1 -0
- package/dist/web/assets/SettingsPage-C6ouU0sR.css +1 -0
- package/dist/web/assets/SettingsPage-Chd_B3eH.js +1 -0
- package/dist/web/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
- package/dist/web/assets/console-CbDsKrsO.js +1 -0
- package/dist/web/assets/fuli-logo-Bu9WBuQe.js +1 -0
- package/dist/web/assets/fuli-logo-ER1wQ8Kk.png +0 -0
- package/dist/web/assets/i18n-BQcns1PH.js +5 -0
- package/dist/web/assets/identity-B8K4VYg6.js +1 -0
- package/dist/web/assets/index-CB8Xmsc2.js +2 -0
- package/dist/web/assets/index-CZeuPh-U.css +1 -0
- package/dist/web/assets/model-eF52GgPF.js +1 -0
- package/dist/web/assets/paths-hwz-Ac1g.js +1 -0
- package/dist/web/assets/useMinimumLoadingDisplay-BpCyBeko.css +1 -0
- package/dist/web/assets/useMinimumLoadingDisplay-wCgGDmHg.js +1 -0
- package/dist/web/assets/vue-router-BsEp_XwI.js +1 -0
- package/dist/web/index.html +9 -6
- package/docs/external-knowledge-architecture.md +279 -0
- package/examples/external-knowledge/markdown-folder.mjs +235 -0
- package/graph-provider/fuli_graph/app.py +57 -1
- package/graph-provider/fuli_graph/graph_models.py +3 -0
- package/graph-provider/fuli_graph/graph_query.py +62 -16
- package/graph-provider/fuli_graph/knowledge_review.py +612 -0
- package/graph-provider/fuli_graph/knowledge_review_models.py +127 -0
- package/graph-provider/fuli_graph/knowledge_search.py +51 -4
- package/graph-provider/fuli_graph/models.py +19 -0
- package/graph-provider/fuli_graph/runtime.py +15 -0
- package/graph-provider/fuli_graph/store_knowledge.py +2 -1
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +5 -407
- package/package.json +7 -5
- package/skills/capturing-session-knowledge/SKILL.md +24 -4
- package/skills/flreview/SKILL.md +73 -0
- package/skills/flreview/agents/openai.yaml +6 -0
- package/src/agent-tools/graph-definitions.js +79 -4
- package/src/agent-tools/graph-handlers.js +12 -0
- package/src/cli/command-registry.js +3 -3
- package/src/cli/local-runtime-command.js +51 -28
- package/src/cli/local-runtime-options.js +14 -8
- package/src/cli/setup-agent-selection.js +12 -12
- package/src/cli/setup-command.js +41 -18
- package/src/cli/shell-argument.js +10 -0
- package/src/cli/uninstall-command.js +17 -15
- package/src/cli/update-command.js +38 -30
- package/src/external-knowledge/catalog.js +61 -0
- package/src/external-knowledge/conflict-policy.js +58 -0
- package/src/external-knowledge/connected-search.js +166 -0
- package/src/external-knowledge/connectors/custom.js +133 -0
- package/src/external-knowledge/connectors/feishu.js +461 -0
- package/src/external-knowledge/connectors/mcp.js +438 -0
- package/src/external-knowledge/connectors/notion.js +307 -0
- package/src/external-knowledge/connectors/retrieval-api.js +222 -0
- package/src/external-knowledge/document-mapping.js +151 -0
- package/src/external-knowledge/graph-projection.js +17 -0
- package/src/external-knowledge/index.js +8 -0
- package/src/external-knowledge/registry.js +91 -0
- package/src/external-knowledge/runtime.js +68 -0
- package/src/external-knowledge/safe-diagnostic.js +14 -0
- package/src/external-knowledge/service.js +811 -0
- package/src/graphiti/agent-knowledge-workflows.js +7 -2
- package/src/graphiti/federated-application.js +66 -41
- package/src/graphiti/knowledge-item-role.js +21 -0
- package/src/graphiti/knowledge-review.js +43 -0
- package/src/graphiti/project-profile-mapping.js +32 -0
- package/src/graphiti/provider-client.js +22 -1
- package/src/graphiti/source-marker.js +54 -3
- package/src/graphiti/task-knowledge-recall.js +250 -0
- package/src/http/api-router.js +23 -108
- package/src/http/external-knowledge-api-router.js +104 -0
- package/src/http/graph-api-router.js +12 -1
- package/src/http/request-policy.js +1 -1
- package/src/local-runtime/lifecycle.js +26 -15
- package/src/mcp/create-mcp-server.js +1 -0
- package/src/mcp/instructions.js +7 -7
- package/src/mcp/tool-annotations.js +6 -0
- package/src/mcp/tool-result.js +1 -0
- package/src/server/application-lifecycle.js +2 -11
- package/src/server/blocked-ports.js +11 -0
- package/src/server.js +36 -17
- package/src/setup/agent-installation-status.js +2 -1
- package/src/setup/agents.js +2 -1
- package/src/setup/claude-code-config.js +2 -1
- package/src/setup/codex-bootstrap.js +12 -7
- package/src/setup/container-runtime.js +25 -21
- package/src/setup/graph-runtime.js +148 -56
- package/src/setup/node-runtime.js +3 -3
- package/src/setup/options.js +2 -4
- package/src/setup/paths.js +18 -3
- package/src/setup/review-skill-adapters.js +49 -0
- package/src/setup/setup.js +26 -2
- package/src/setup/uninstall.js +4 -2
- package/src/system/resource-monitor.js +317 -0
- package/src/system/runtime-settings.js +131 -0
- package/src/system/system-service.js +119 -0
- package/dist/web/assets/ConnectionsPage-CCKWm-6p.js +0 -1
- package/dist/web/assets/KnowledgeInspector-BZkB2wJn.js +0 -1
- package/dist/web/assets/KnowledgeInspector-CrGYxEob.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-CZAF_OlN.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-DlvcLwlL.js +0 -1
- package/dist/web/assets/KnowledgePage-CNQXd15y.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-CXPE4GCD.js +0 -4
- package/dist/web/assets/OverviewPage-ul7g7HXh.js +0 -1
- package/dist/web/assets/PersonalProfilePage-CC40Pw2L.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-CPIEcShD.js +0 -1
- package/dist/web/assets/PublicProjectsPage-C700sR7z.js +0 -1
- package/dist/web/assets/ReviewPage-7MDDUDgA.js +0 -1
- package/dist/web/assets/console-B_Ena2DS.js +0 -5
- package/dist/web/assets/fuli-logo-DGuyqchQ.png +0 -0
- package/dist/web/assets/identity-CjBwYSMX.js +0 -1
- package/dist/web/assets/index-CpLDx3of.css +0 -1
- package/dist/web/assets/index-DAzeFJSc.js +0 -2
- package/dist/web/assets/model-C-5RVIJ2.js +0 -1
- package/dist/web/assets/paths-BkULqmGI.js +0 -1
- package/dist/web/assets/vue-router-DnD3hUm7.js +0 -1
- package/src/agent/candidate-read-service.js +0 -53
- package/src/agent/context-pack-service.js +0 -83
- package/src/agent/public-read-policy.js +0 -39
- package/src/agent/public-read-service.js +0 -69
- package/src/agent/read-projection.js +0 -107
- package/src/agent/search-service.js +0 -61
- package/src/agent/space-boundary.js +0 -33
- package/src/app/bootstrap.js +0 -8
- package/src/app/create-application.js +0 -143
- package/src/app/state-service.js +0 -38
- package/src/candidates.js +0 -78
- package/src/classifier.js +0 -63
- package/src/cli/command-arguments.js +0 -14
- package/src/context-pack.js +0 -206
- package/src/extractor.js +0 -77
- package/src/fact-history.js +0 -39
- package/src/facts/fact-service.js +0 -89
- package/src/http/bounded-json-array.js +0 -19
- package/src/http/lens-route.js +0 -50
- package/src/ingestion.js +0 -161
- package/src/lens/lens-correction-service.js +0 -81
- package/src/lens/lens-fact-writer.js +0 -85
- package/src/lens/lens-input.js +0 -62
- package/src/lens/lens-query-projection.js +0 -87
- package/src/lens/lens-query-ranking.js +0 -62
- package/src/lens/lens-query.js +0 -143
- package/src/lens/lens-resource-projection.js +0 -93
- package/src/lens/lens-resource-selection.js +0 -45
- package/src/lens/lens-resource-service.js +0 -103
- package/src/lens/lens-search-query.js +0 -122
- package/src/lens/lens-service.js +0 -183
- package/src/models.js +0 -44
- package/src/observer.js +0 -83
- package/src/project-rules.js +0 -33
- package/src/publication/outbox-service.js +0 -46
- package/src/publication/publication-envelope-schema.js +0 -75
- package/src/publication/publication-envelope.js +0 -37
- package/src/publication/publication-policy.js +0 -82
- package/src/publication/publication-service.js +0 -48
- package/src/router.js +0 -58
- package/src/runtime-options.js +0 -138
- package/src/security/episode-visibility.js +0 -14
- package/src/security/fact-visibility.js +0 -15
- package/src/security/source-metadata-policy.js +0 -8
- package/src/storage/episode-evidence-preview.js +0 -100
- package/src/storage/fact-search-order.js +0 -18
- package/src/storage/file/episode-evidence.js +0 -37
- package/src/storage/file/episode-index.js +0 -59
- package/src/storage/file/fact-search.js +0 -60
- package/src/storage/file-store.js +0 -437
- package/src/storage/migrate.js +0 -128
- package/src/storage/migrations/001-initial.sql +0 -83
- package/src/storage/migrations/002-storage-integrity.sql +0 -113
- package/src/storage/record-validation.js +0 -46
- package/src/storage/snapshot-defaults.js +0 -54
- package/src/storage/sqlite/candidates-repository.js +0 -84
- package/src/storage/sqlite/episode-evidence-repository.js +0 -154
- package/src/storage/sqlite/episode-privacy-sql.js +0 -33
- package/src/storage/sqlite/episodes-repository.js +0 -52
- package/src/storage/sqlite/fact-search-repository.js +0 -162
- package/src/storage/sqlite/facts-repository.js +0 -146
- package/src/storage/sqlite/imports-repository.js +0 -39
- package/src/storage/sqlite/mapper.js +0 -84
- package/src/storage/sqlite/open-database.js +0 -15
- package/src/storage/sqlite/outbox-repository.js +0 -103
- package/src/storage/sqlite/snapshot-repository.js +0 -69
- package/src/storage/sqlite/spaces-repository.js +0 -52
- package/src/storage/sqlite/subscriptions-repository.js +0 -54
- package/src/storage/sqlite-store.js +0 -159
- package/src/storage/stable-json.js +0 -24
- package/src/storage/store-port.js +0 -20
- package/src/store.js +0 -2
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
import { FactStatus, nowIso } from '../../models.js';
|
|
4
|
-
import { validateFactRecord } from '../record-validation.js';
|
|
5
|
-
import { FactSearchRepository } from './fact-search-repository.js';
|
|
6
|
-
import { mapFact } from './mapper.js';
|
|
7
|
-
|
|
8
|
-
export class FactsRepository {
|
|
9
|
-
constructor(db) {
|
|
10
|
-
this.factSearch = new FactSearchRepository(db);
|
|
11
|
-
this.insertStatement = db.prepare(`
|
|
12
|
-
INSERT INTO facts (
|
|
13
|
-
id, space_id, subject, predicate, object, source_episode_id, status,
|
|
14
|
-
confidence, sensitivity, scope, valid_at, invalid_at, replaced_by_fact_id
|
|
15
|
-
) VALUES (
|
|
16
|
-
@id, @spaceId, @subject, @predicate, @object, @sourceEpisodeId, @status,
|
|
17
|
-
@confidence, @sensitivity, @scope, @validAt, @invalidAt, @replacedByFactId
|
|
18
|
-
)
|
|
19
|
-
`);
|
|
20
|
-
this.getStatement = db.prepare('SELECT * FROM facts WHERE id = ?');
|
|
21
|
-
this.updateStatement = db.prepare(`
|
|
22
|
-
UPDATE facts SET
|
|
23
|
-
space_id = @spaceId,
|
|
24
|
-
subject = @subject,
|
|
25
|
-
predicate = @predicate,
|
|
26
|
-
object = @object,
|
|
27
|
-
source_episode_id = @sourceEpisodeId,
|
|
28
|
-
status = @status,
|
|
29
|
-
confidence = @confidence,
|
|
30
|
-
sensitivity = @sensitivity,
|
|
31
|
-
scope = @scope,
|
|
32
|
-
valid_at = @validAt,
|
|
33
|
-
invalid_at = @invalidAt,
|
|
34
|
-
replaced_by_fact_id = @replacedByFactId
|
|
35
|
-
WHERE id = @id
|
|
36
|
-
`);
|
|
37
|
-
this.currentStatement = db.prepare(`
|
|
38
|
-
SELECT * FROM facts
|
|
39
|
-
WHERE space_id = ?
|
|
40
|
-
AND invalid_at IS NULL
|
|
41
|
-
AND status NOT IN ('rejected', 'deprecated')
|
|
42
|
-
ORDER BY rowid
|
|
43
|
-
`);
|
|
44
|
-
this.currentListStatement = db.prepare(`
|
|
45
|
-
SELECT * FROM facts
|
|
46
|
-
WHERE invalid_at IS NULL AND status NOT IN ('rejected', 'deprecated')
|
|
47
|
-
ORDER BY rowid
|
|
48
|
-
`);
|
|
49
|
-
this.fullListStatement = db.prepare('SELECT * FROM facts ORDER BY rowid');
|
|
50
|
-
this.timelineStatement = db.prepare(`
|
|
51
|
-
SELECT * FROM facts
|
|
52
|
-
WHERE space_id = ? AND subject = ?
|
|
53
|
-
ORDER BY valid_at, rowid
|
|
54
|
-
`);
|
|
55
|
-
this.replaceLinkStatement = db.prepare(`
|
|
56
|
-
UPDATE facts SET replaced_by_fact_id = ? WHERE id = ?
|
|
57
|
-
`);
|
|
58
|
-
this.deleteStatement = db.prepare('DELETE FROM facts');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
add(fact) {
|
|
62
|
-
if (fact.id && this.get(fact.id)) {
|
|
63
|
-
throw new Error(`Duplicate fact id: ${fact.id}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const stored = {
|
|
67
|
-
id: fact.id || randomUUID(),
|
|
68
|
-
spaceId: fact.spaceId,
|
|
69
|
-
subject: fact.subject,
|
|
70
|
-
predicate: fact.predicate,
|
|
71
|
-
object: fact.object,
|
|
72
|
-
sourceEpisodeId: fact.sourceEpisodeId,
|
|
73
|
-
status: fact.status ?? FactStatus.CONFIRMED,
|
|
74
|
-
confidence: fact.confidence ?? 1,
|
|
75
|
-
sensitivity: fact.sensitivity ?? 'normal',
|
|
76
|
-
scope: fact.scope ?? 'personal',
|
|
77
|
-
validAt: fact.validAt ?? nowIso(),
|
|
78
|
-
invalidAt: fact.invalidAt ?? null,
|
|
79
|
-
replacedByFactId: fact.replacedByFactId ?? null
|
|
80
|
-
};
|
|
81
|
-
validateFactRecord(stored);
|
|
82
|
-
this.insertStatement.run(stored);
|
|
83
|
-
return this.get(stored.id);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
get(id) {
|
|
87
|
-
return mapFact(this.getStatement.get(id));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
update(id, updates) {
|
|
91
|
-
const existing = this.get(id);
|
|
92
|
-
if (!existing) throw new Error(`Fact not found: ${id}`);
|
|
93
|
-
|
|
94
|
-
const stored = { ...existing, ...updates, id };
|
|
95
|
-
validateFactRecord(stored);
|
|
96
|
-
this.updateStatement.run(stored);
|
|
97
|
-
return this.get(id);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
invalidate(id, replacedByFactId = null) {
|
|
101
|
-
const fact = this.get(id);
|
|
102
|
-
if (!fact || fact.invalidAt) return;
|
|
103
|
-
this.updateStatement.run({
|
|
104
|
-
...fact,
|
|
105
|
-
invalidAt: nowIso(),
|
|
106
|
-
replacedByFactId
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
current(spaceId) {
|
|
111
|
-
return this.currentStatement.all(spaceId).map(mapFact);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
list({ includeHistorical = false } = {}) {
|
|
115
|
-
const statement = includeHistorical ? this.fullListStatement : this.currentListStatement;
|
|
116
|
-
return statement.all().map(mapFact);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
timeline(spaceId, subject) {
|
|
120
|
-
return this.timelineStatement.all(spaceId, subject).map(mapFact);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
search(spaceIds, query, options = {}) {
|
|
124
|
-
return this.factSearch.search(spaceIds, query, options);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
searchPage(spaceIds, query, options = {}) {
|
|
128
|
-
return this.factSearch.searchPage(spaceIds, query, options);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
insertSnapshot(fact, { deferReplacement = false } = {}) {
|
|
132
|
-
validateFactRecord(fact);
|
|
133
|
-
this.insertStatement.run({
|
|
134
|
-
...fact,
|
|
135
|
-
replacedByFactId: deferReplacement ? null : fact.replacedByFactId
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
restoreReplacement(id, replacedByFactId) {
|
|
140
|
-
this.replaceLinkStatement.run(replacedByFactId, id);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
deleteAll() {
|
|
144
|
-
this.deleteStatement.run();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { mapImport } from './mapper.js';
|
|
2
|
-
|
|
3
|
-
export class ImportsRepository {
|
|
4
|
-
constructor(db) {
|
|
5
|
-
this.insertStatement = db.prepare(`
|
|
6
|
-
INSERT INTO imports (content_hash, source_path, imported_at)
|
|
7
|
-
VALUES (@contentHash, @sourcePath, @importedAt)
|
|
8
|
-
`);
|
|
9
|
-
this.hasStatement = db.prepare(`
|
|
10
|
-
SELECT 1 FROM imports WHERE content_hash = ?
|
|
11
|
-
`);
|
|
12
|
-
this.listStatement = db.prepare('SELECT * FROM imports ORDER BY rowid');
|
|
13
|
-
this.deleteStatement = db.prepare('DELETE FROM imports');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
has(contentHash) {
|
|
17
|
-
return this.hasStatement.get(contentHash) !== undefined;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
record(record) {
|
|
21
|
-
if (this.has(record.contentHash)) {
|
|
22
|
-
throw new Error(`Duplicate import content hash: ${record.contentHash}`);
|
|
23
|
-
}
|
|
24
|
-
this.insertStatement.run(record);
|
|
25
|
-
return { ...record };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
listAll() {
|
|
29
|
-
return this.listStatement.all().map(mapImport);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
insertSnapshot(record) {
|
|
33
|
-
this.insertStatement.run(record);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
deleteAll() {
|
|
37
|
-
this.deleteStatement.run();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
export function mapSpace(row) {
|
|
2
|
-
return row ? {
|
|
3
|
-
id: row.id,
|
|
4
|
-
name: row.name,
|
|
5
|
-
kind: row.kind,
|
|
6
|
-
description: row.description,
|
|
7
|
-
createdAt: row.created_at
|
|
8
|
-
} : null;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function mapSubscription(row) {
|
|
12
|
-
return row ? {
|
|
13
|
-
personalSpaceId: row.personal_space_id,
|
|
14
|
-
spaceId: row.space_id,
|
|
15
|
-
mode: row.mode,
|
|
16
|
-
createdAt: row.created_at
|
|
17
|
-
} : null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function mapEpisode(row) {
|
|
21
|
-
return row ? {
|
|
22
|
-
id: row.id,
|
|
23
|
-
spaceId: row.space_id,
|
|
24
|
-
sourceKind: row.source_kind,
|
|
25
|
-
body: row.body,
|
|
26
|
-
sourceUri: row.source_uri,
|
|
27
|
-
metadata: JSON.parse(row.metadata_json),
|
|
28
|
-
createdAt: row.created_at
|
|
29
|
-
} : null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function mapFact(row) {
|
|
33
|
-
return row ? {
|
|
34
|
-
id: row.id,
|
|
35
|
-
spaceId: row.space_id,
|
|
36
|
-
subject: row.subject,
|
|
37
|
-
predicate: row.predicate,
|
|
38
|
-
object: row.object,
|
|
39
|
-
sourceEpisodeId: row.source_episode_id,
|
|
40
|
-
status: row.status,
|
|
41
|
-
confidence: row.confidence,
|
|
42
|
-
sensitivity: row.sensitivity,
|
|
43
|
-
scope: row.scope,
|
|
44
|
-
validAt: row.valid_at,
|
|
45
|
-
invalidAt: row.invalid_at,
|
|
46
|
-
replacedByFactId: row.replaced_by_fact_id
|
|
47
|
-
} : null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function mapCandidate(row) {
|
|
51
|
-
return row ? {
|
|
52
|
-
id: row.id,
|
|
53
|
-
personalSpaceId: row.personal_space_id,
|
|
54
|
-
targetSpaceId: row.target_space_id,
|
|
55
|
-
episodeId: row.episode_id,
|
|
56
|
-
reason: row.reason,
|
|
57
|
-
status: row.status,
|
|
58
|
-
createdAt: row.created_at,
|
|
59
|
-
decidedAt: row.decided_at
|
|
60
|
-
} : null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function mapOutbox(row) {
|
|
64
|
-
return row ? {
|
|
65
|
-
id: row.id,
|
|
66
|
-
kind: row.kind,
|
|
67
|
-
aggregateId: row.aggregate_id,
|
|
68
|
-
payload: JSON.parse(row.payload_json),
|
|
69
|
-
status: row.status,
|
|
70
|
-
attempts: row.attempts,
|
|
71
|
-
nextAttemptAt: row.next_attempt_at,
|
|
72
|
-
createdAt: row.created_at,
|
|
73
|
-
sentAt: row.sent_at,
|
|
74
|
-
lastError: row.last_error
|
|
75
|
-
} : null;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function mapImport(row) {
|
|
79
|
-
return row ? {
|
|
80
|
-
contentHash: row.content_hash,
|
|
81
|
-
sourcePath: row.source_path,
|
|
82
|
-
importedAt: row.imported_at
|
|
83
|
-
} : null;
|
|
84
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { mkdirSync } from 'node:fs';
|
|
2
|
-
import { dirname, resolve } from 'node:path';
|
|
3
|
-
|
|
4
|
-
import Database from 'better-sqlite3';
|
|
5
|
-
|
|
6
|
-
export function openDatabase(filePath) {
|
|
7
|
-
if (isFileDatabase(filePath)) {
|
|
8
|
-
mkdirSync(dirname(resolve(filePath)), { recursive: true });
|
|
9
|
-
}
|
|
10
|
-
return new Database(filePath);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function isFileDatabase(filePath) {
|
|
14
|
-
return typeof filePath === 'string' && filePath !== ':memory:' && filePath !== '';
|
|
15
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
import { nowIso } from '../../models.js';
|
|
4
|
-
import { validateOutboxRecord } from '../record-validation.js';
|
|
5
|
-
import { mapOutbox } from './mapper.js';
|
|
6
|
-
|
|
7
|
-
export class OutboxRepository {
|
|
8
|
-
constructor(db) {
|
|
9
|
-
this.insertStatement = db.prepare(`
|
|
10
|
-
INSERT INTO outbox (
|
|
11
|
-
id, kind, aggregate_id, payload_json, status, attempts,
|
|
12
|
-
next_attempt_at, created_at, sent_at, last_error
|
|
13
|
-
) VALUES (
|
|
14
|
-
@id, @kind, @aggregateId, @payloadJson, @status, @attempts,
|
|
15
|
-
@nextAttemptAt, @createdAt, @sentAt, @lastError
|
|
16
|
-
)
|
|
17
|
-
`);
|
|
18
|
-
this.getStatement = db.prepare('SELECT * FROM outbox WHERE id = ?');
|
|
19
|
-
this.listStatement = db.prepare('SELECT * FROM outbox ORDER BY rowid');
|
|
20
|
-
this.pendingStatement = db.prepare(`
|
|
21
|
-
SELECT * FROM outbox
|
|
22
|
-
WHERE status = 'pending'
|
|
23
|
-
AND (next_attempt_at IS NULL OR next_attempt_at <= ?)
|
|
24
|
-
ORDER BY created_at, rowid
|
|
25
|
-
`);
|
|
26
|
-
this.markSentStatement = db.prepare(`
|
|
27
|
-
UPDATE outbox SET
|
|
28
|
-
status = 'sent', next_attempt_at = NULL, sent_at = ?, last_error = NULL
|
|
29
|
-
WHERE id = ?
|
|
30
|
-
`);
|
|
31
|
-
this.markFailedStatement = db.prepare(`
|
|
32
|
-
UPDATE outbox SET
|
|
33
|
-
status = 'pending', attempts = attempts + 1,
|
|
34
|
-
next_attempt_at = ?, last_error = ?
|
|
35
|
-
WHERE id = ?
|
|
36
|
-
`);
|
|
37
|
-
this.deleteStatement = db.prepare('DELETE FROM outbox');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
enqueue(entry) {
|
|
41
|
-
if (entry.id && this.get(entry.id)) {
|
|
42
|
-
throw new Error(`Duplicate outbox id: ${entry.id}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const stored = {
|
|
46
|
-
id: entry.id || randomUUID(),
|
|
47
|
-
kind: entry.kind,
|
|
48
|
-
aggregateId: entry.aggregateId,
|
|
49
|
-
payloadJson: JSON.stringify(entry.payload),
|
|
50
|
-
status: entry.status ?? 'pending',
|
|
51
|
-
attempts: entry.attempts ?? 0,
|
|
52
|
-
nextAttemptAt: entry.nextAttemptAt ?? null,
|
|
53
|
-
createdAt: entry.createdAt ?? nowIso(),
|
|
54
|
-
sentAt: entry.sentAt ?? null,
|
|
55
|
-
lastError: entry.lastError ?? null
|
|
56
|
-
};
|
|
57
|
-
validateOutboxRecord(stored);
|
|
58
|
-
this.insertStatement.run(stored);
|
|
59
|
-
return this.get(stored.id);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
listPending(at = nowIso()) {
|
|
63
|
-
return this.pendingStatement.all(at).map(mapOutbox);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
markSent(id, sentAt = nowIso()) {
|
|
67
|
-
this.require(id);
|
|
68
|
-
this.markSentStatement.run(sentAt, id);
|
|
69
|
-
return this.get(id);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
markFailed(id, error, nextAttemptAt = null) {
|
|
73
|
-
this.require(id);
|
|
74
|
-
this.markFailedStatement.run(nextAttemptAt, error, id);
|
|
75
|
-
return this.get(id);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
get(id) {
|
|
79
|
-
return mapOutbox(this.getStatement.get(id));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
listAll() {
|
|
83
|
-
return this.listStatement.all().map(mapOutbox);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
insertSnapshot(entry) {
|
|
87
|
-
validateOutboxRecord(entry);
|
|
88
|
-
this.insertStatement.run({
|
|
89
|
-
...entry,
|
|
90
|
-
payloadJson: JSON.stringify(entry.payload)
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
deleteAll() {
|
|
95
|
-
this.deleteStatement.run();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
require(id) {
|
|
99
|
-
const row = this.get(id);
|
|
100
|
-
if (!row) throw new Error(`Outbox row not found: ${id}`);
|
|
101
|
-
return row;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { cloneSnapshot, normalizeSnapshot } from '../snapshot-defaults.js';
|
|
2
|
-
|
|
3
|
-
export class SnapshotRepository {
|
|
4
|
-
constructor(db, repositories) {
|
|
5
|
-
this.db = db;
|
|
6
|
-
this.repositories = repositories;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export() {
|
|
10
|
-
const repositories = this.repositories;
|
|
11
|
-
return cloneSnapshot({
|
|
12
|
-
spaces: repositories.spaces.list(),
|
|
13
|
-
subscriptions: repositories.subscriptions.list(),
|
|
14
|
-
episodes: repositories.episodes.list(),
|
|
15
|
-
facts: repositories.facts.list({ includeHistorical: true }),
|
|
16
|
-
candidates: repositories.candidates.list(),
|
|
17
|
-
outbox: repositories.outbox.listAll(),
|
|
18
|
-
imports: repositories.imports.listAll()
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
import(snapshot) {
|
|
23
|
-
const current = this.export();
|
|
24
|
-
const normalized = normalizeSnapshot(snapshot, {
|
|
25
|
-
outbox: current.outbox,
|
|
26
|
-
imports: current.imports
|
|
27
|
-
});
|
|
28
|
-
const replaceOutbox = Object.hasOwn(snapshot, 'outbox');
|
|
29
|
-
const replaceImports = Object.hasOwn(snapshot, 'imports');
|
|
30
|
-
const apply = () => this.replace(normalized, { replaceOutbox, replaceImports });
|
|
31
|
-
|
|
32
|
-
if (this.db.inTransaction) apply();
|
|
33
|
-
else this.db.transaction(apply)();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
replace(snapshot, { replaceOutbox, replaceImports }) {
|
|
37
|
-
const repositories = this.repositories;
|
|
38
|
-
repositories.subscriptions.deleteAll();
|
|
39
|
-
repositories.candidates.deleteAll();
|
|
40
|
-
repositories.facts.deleteAll();
|
|
41
|
-
repositories.episodes.deleteAll();
|
|
42
|
-
if (replaceOutbox) repositories.outbox.deleteAll();
|
|
43
|
-
if (replaceImports) repositories.imports.deleteAll();
|
|
44
|
-
repositories.spaces.deleteAll();
|
|
45
|
-
|
|
46
|
-
for (const space of snapshot.spaces) repositories.spaces.insertSnapshot(space);
|
|
47
|
-
for (const subscription of snapshot.subscriptions) {
|
|
48
|
-
repositories.subscriptions.insertSnapshot(subscription);
|
|
49
|
-
}
|
|
50
|
-
for (const episode of snapshot.episodes) repositories.episodes.insertSnapshot(episode);
|
|
51
|
-
for (const fact of snapshot.facts) {
|
|
52
|
-
repositories.facts.insertSnapshot(fact, { deferReplacement: true });
|
|
53
|
-
}
|
|
54
|
-
for (const fact of snapshot.facts) {
|
|
55
|
-
if (fact.replacedByFactId) {
|
|
56
|
-
repositories.facts.restoreReplacement(fact.id, fact.replacedByFactId);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
for (const candidate of snapshot.candidates) {
|
|
60
|
-
repositories.candidates.insertSnapshot(candidate);
|
|
61
|
-
}
|
|
62
|
-
if (replaceOutbox) {
|
|
63
|
-
for (const entry of snapshot.outbox) repositories.outbox.insertSnapshot(entry);
|
|
64
|
-
}
|
|
65
|
-
if (replaceImports) {
|
|
66
|
-
for (const record of snapshot.imports) repositories.imports.insertSnapshot(record);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
import { nowIso } from '../../models.js';
|
|
4
|
-
import { mapSpace } from './mapper.js';
|
|
5
|
-
|
|
6
|
-
export class SpacesRepository {
|
|
7
|
-
constructor(db) {
|
|
8
|
-
this.insertStatement = db.prepare(`
|
|
9
|
-
INSERT INTO spaces (id, name, kind, description, created_at)
|
|
10
|
-
VALUES (@id, @name, @kind, @description, @createdAt)
|
|
11
|
-
`);
|
|
12
|
-
this.listStatement = db.prepare('SELECT * FROM spaces ORDER BY rowid');
|
|
13
|
-
this.findByNameStatement = db.prepare('SELECT * FROM spaces WHERE name = ?');
|
|
14
|
-
this.getStatement = db.prepare('SELECT * FROM spaces WHERE id = ?');
|
|
15
|
-
this.deleteStatement = db.prepare('DELETE FROM spaces');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
create(name, kind, description = null) {
|
|
19
|
-
const existing = this.findByName(name);
|
|
20
|
-
if (existing) return existing;
|
|
21
|
-
|
|
22
|
-
const space = {
|
|
23
|
-
id: randomUUID(),
|
|
24
|
-
name,
|
|
25
|
-
kind,
|
|
26
|
-
description,
|
|
27
|
-
createdAt: nowIso()
|
|
28
|
-
};
|
|
29
|
-
this.insertStatement.run(space);
|
|
30
|
-
return this.get(space.id);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
list() {
|
|
34
|
-
return this.listStatement.all().map(mapSpace);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
findByName(name) {
|
|
38
|
-
return mapSpace(this.findByNameStatement.get(name));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
get(id) {
|
|
42
|
-
return mapSpace(this.getStatement.get(id));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
insertSnapshot(space) {
|
|
46
|
-
this.insertStatement.run(space);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
deleteAll() {
|
|
50
|
-
this.deleteStatement.run();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { nowIso } from '../../models.js';
|
|
2
|
-
import { mapSubscription } from './mapper.js';
|
|
3
|
-
|
|
4
|
-
export class SubscriptionsRepository {
|
|
5
|
-
constructor(db) {
|
|
6
|
-
this.insertStatement = db.prepare(`
|
|
7
|
-
INSERT INTO subscriptions (personal_space_id, space_id, mode, created_at)
|
|
8
|
-
VALUES (@personalSpaceId, @spaceId, @mode, @createdAt)
|
|
9
|
-
`);
|
|
10
|
-
this.getStatement = db.prepare(`
|
|
11
|
-
SELECT * FROM subscriptions
|
|
12
|
-
WHERE personal_space_id = ? AND space_id = ?
|
|
13
|
-
`);
|
|
14
|
-
this.listStatement = db.prepare('SELECT * FROM subscriptions ORDER BY rowid');
|
|
15
|
-
this.forPersonalSpaceStatement = db.prepare(`
|
|
16
|
-
SELECT * FROM subscriptions WHERE personal_space_id = ? ORDER BY rowid
|
|
17
|
-
`);
|
|
18
|
-
this.deleteStatement = db.prepare('DELETE FROM subscriptions');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
subscribe(personalSpaceId, spaceId, mode = 'latest') {
|
|
22
|
-
const existing = this.get(personalSpaceId, spaceId);
|
|
23
|
-
if (existing) return existing;
|
|
24
|
-
|
|
25
|
-
const subscription = {
|
|
26
|
-
personalSpaceId,
|
|
27
|
-
spaceId,
|
|
28
|
-
mode,
|
|
29
|
-
createdAt: nowIso()
|
|
30
|
-
};
|
|
31
|
-
this.insertStatement.run(subscription);
|
|
32
|
-
return this.get(personalSpaceId, spaceId);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
list() {
|
|
36
|
-
return this.listStatement.all().map(mapSubscription);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
forPersonalSpace(personalSpaceId) {
|
|
40
|
-
return this.forPersonalSpaceStatement.all(personalSpaceId).map(mapSubscription);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
insertSnapshot(subscription) {
|
|
44
|
-
this.insertStatement.run(subscription);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
deleteAll() {
|
|
48
|
-
this.deleteStatement.run();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
get(personalSpaceId, spaceId) {
|
|
52
|
-
return mapSubscription(this.getStatement.get(personalSpaceId, spaceId));
|
|
53
|
-
}
|
|
54
|
-
}
|