@wirecat/cli-core 0.372.0 → 0.374.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/CHANGELOG.md +31 -0
- package/dist/files/bounded-utf8.d.ts +4 -0
- package/dist/files/bounded-utf8.js +32 -0
- package/dist/files/index.d.ts +1 -0
- package/dist/files/index.js +1 -0
- package/dist/store/index.d.ts +4 -0
- package/dist/store/index.js +3 -0
- package/dist/store/project-transfer/manifest.d.ts +1 -0
- package/dist/store/project-transfer/manifest.js +6 -2
- package/dist/store/project-transfer/plan.d.ts +17 -0
- package/dist/store/project-transfer/plan.js +18 -7
- package/dist/store/project-transfer/preview.d.ts +11 -0
- package/dist/store/project-transfer/preview.js +29 -0
- package/dist/store/project-transfer/request.d.ts +76 -0
- package/dist/store/project-transfer/request.js +22 -0
- package/dist/store/project-transfer/snapshot-model.d.ts +117 -0
- package/dist/store/project-transfer/snapshot-model.js +99 -0
- package/dist/store/project-transfer/snapshot-plan.d.ts +12 -0
- package/dist/store/project-transfer/snapshot-plan.js +4 -0
- package/dist/store/project-transfer/snapshot.d.ts +17 -0
- package/dist/store/project-transfer/snapshot.js +29 -0
- package/dist/store/project-transfer/store.d.ts +25 -0
- package/dist/store/project-transfer/store.js +1 -0
- package/dist/store/sqlite/project-transfer/budget.d.ts +9 -0
- package/dist/store/sqlite/project-transfer/budget.js +27 -0
- package/dist/store/sqlite/project-transfer/preview.d.ts +12 -0
- package/dist/store/sqlite/project-transfer/preview.js +74 -0
- package/dist/store/sqlite/project-transfer/records.d.ts +5 -0
- package/dist/store/sqlite/project-transfer/records.js +53 -0
- package/dist/store/sqlite/project-transfer/snapshot.d.ts +78 -0
- package/dist/store/sqlite/project-transfer/snapshot.js +183 -0
- package/dist/store/sqlite/project-transfer/sources.d.ts +18 -0
- package/dist/store/sqlite/project-transfer/sources.js +148 -0
- package/dist/store/sqlite/project-transfer/store.d.ts +3 -0
- package/dist/store/sqlite/project-transfer/store.js +35 -0
- package/dist/store/store.d.ts +2 -0
- package/dist/store/store.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { CliError } from "@wirecat/cli-base";
|
|
3
|
+
import { referenceOfThing, storedThing } from "../things.js";
|
|
4
|
+
const invalid = () => {
|
|
5
|
+
throw new CliError("configuration_error", "project snapshot has an unavailable or unsupported dependency");
|
|
6
|
+
};
|
|
7
|
+
const scalar = new Set([
|
|
8
|
+
"person",
|
|
9
|
+
"bot",
|
|
10
|
+
"organization",
|
|
11
|
+
"project",
|
|
12
|
+
"fact",
|
|
13
|
+
"memory",
|
|
14
|
+
"task",
|
|
15
|
+
"note",
|
|
16
|
+
"document",
|
|
17
|
+
"message",
|
|
18
|
+
"email",
|
|
19
|
+
"chat",
|
|
20
|
+
"identity",
|
|
21
|
+
"account",
|
|
22
|
+
"meeting",
|
|
23
|
+
"meeting_transcript",
|
|
24
|
+
"meeting_transcript_row",
|
|
25
|
+
]);
|
|
26
|
+
const noText = new Set(["person", "bot", "organization", "project", "chat", "identity", "account"]);
|
|
27
|
+
const texts = {
|
|
28
|
+
document: {
|
|
29
|
+
table: "documents",
|
|
30
|
+
fields: ["n.title", "n.body"],
|
|
31
|
+
where: "n.deleted_at IS NULL AND n.body IS NOT NULL AND CASE WHEN json_valid(n.metadata) THEN coalesce(json_extract(n.metadata,'$.catalog.active'),1) ELSE 1 END=1",
|
|
32
|
+
},
|
|
33
|
+
note: { table: "notes", fields: ["n.title", "n.body"], where: "n.deleted_at IS NULL" },
|
|
34
|
+
message: {
|
|
35
|
+
table: "messages",
|
|
36
|
+
fields: ["n.text", "n.sent_at", "n.sender_name", "n.sender_chat_external_id", "i.provider", "i.external_id"],
|
|
37
|
+
join: "LEFT JOIN identities i ON i.id=n.sender_identity_id",
|
|
38
|
+
where: "n.deleted_at IS NULL",
|
|
39
|
+
},
|
|
40
|
+
email: {
|
|
41
|
+
table: "emails",
|
|
42
|
+
fields: ["n.subject", "coalesce(n.body_text,'')", "n.sent_at", "n.from_address", "n.from_name"],
|
|
43
|
+
where: "n.deleted_at IS NULL",
|
|
44
|
+
},
|
|
45
|
+
meeting: {
|
|
46
|
+
table: "meetings",
|
|
47
|
+
fields: ["n.title", "n.description", "n.location", "n.started_at", "n.ended_at", "n.duration_ms", "n.timezone"],
|
|
48
|
+
where: "n.deleted_at IS NULL",
|
|
49
|
+
},
|
|
50
|
+
meeting_transcript_row: {
|
|
51
|
+
table: "meeting_transcript_rows",
|
|
52
|
+
fields: ["n.position", "n.start_ms", "n.end_ms", "n.speaker_name", "n.text"],
|
|
53
|
+
join: "JOIN meeting_transcripts t ON t.id=n.meeting_transcript_id JOIN meetings m ON m.id=t.meeting_id",
|
|
54
|
+
where: "t.deleted_at IS NULL AND m.deleted_at IS NULL",
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
export class ProjectSnapshotSources {
|
|
58
|
+
database;
|
|
59
|
+
budget;
|
|
60
|
+
references = new Map();
|
|
61
|
+
byThing = new Map();
|
|
62
|
+
constructor(database, budget) {
|
|
63
|
+
this.database = database;
|
|
64
|
+
this.budget = budget;
|
|
65
|
+
}
|
|
66
|
+
ref(type, id) {
|
|
67
|
+
if (type === null && id === null)
|
|
68
|
+
return null;
|
|
69
|
+
if (typeof type !== "string" || !scalar.has(type) || typeof id !== "number" || !Number.isSafeInteger(id) || id < 1)
|
|
70
|
+
return invalid();
|
|
71
|
+
const key = `${type}:${id}`, cached = this.byThing.get(key);
|
|
72
|
+
if (cached)
|
|
73
|
+
return cached;
|
|
74
|
+
if (type === "project" || type === "organization") {
|
|
75
|
+
const table = type === "project" ? "projects" : "organizations";
|
|
76
|
+
if (!this.database.prepare(`SELECT 1 FROM ${table} WHERE id=? AND deleted_at IS NULL`).get(id))
|
|
77
|
+
return invalid();
|
|
78
|
+
}
|
|
79
|
+
const identifier = {
|
|
80
|
+
message: [
|
|
81
|
+
"FROM messages n JOIN chats c ON c.id=n.chat_id JOIN accounts a ON a.id=n.account_id WHERE n.id=?",
|
|
82
|
+
["n.external_id", "c.external_id", "a.external_id", "a.provider"],
|
|
83
|
+
],
|
|
84
|
+
email: [
|
|
85
|
+
"FROM emails n JOIN email_threads c ON c.id=n.email_thread_id JOIN accounts a ON a.id=n.account_id WHERE n.id=?",
|
|
86
|
+
["n.external_id", "c.external_id", "a.external_id", "a.provider"],
|
|
87
|
+
],
|
|
88
|
+
chat: [
|
|
89
|
+
"FROM chats n JOIN accounts a ON a.id=n.account_id WHERE n.id=?",
|
|
90
|
+
["n.external_id", "a.external_id", "a.provider"],
|
|
91
|
+
],
|
|
92
|
+
identity: ["FROM identities n WHERE n.id=?", ["n.external_id", "n.provider"]],
|
|
93
|
+
};
|
|
94
|
+
const selection = identifier[type];
|
|
95
|
+
if (selection)
|
|
96
|
+
this.budget.rows(this.database, selection[0], [...selection[1]], [id]);
|
|
97
|
+
const thing = storedThing(type, id), ref = referenceOfThing(this.database, thing);
|
|
98
|
+
if (!ref)
|
|
99
|
+
return invalid();
|
|
100
|
+
this.byThing.set(key, ref);
|
|
101
|
+
this.references.set(ref, thing);
|
|
102
|
+
return ref;
|
|
103
|
+
}
|
|
104
|
+
external(owned) {
|
|
105
|
+
return [...this.references]
|
|
106
|
+
.filter(([ref]) => !owned.has(ref))
|
|
107
|
+
.map(([ref, thing]) => ({
|
|
108
|
+
ref,
|
|
109
|
+
content_sha256: this.fingerprint(thing),
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
fingerprint(thing) {
|
|
113
|
+
this.budget.reserve(1, 128);
|
|
114
|
+
if (noText.has(thing.type))
|
|
115
|
+
return null;
|
|
116
|
+
if (thing.type === "meeting_transcript")
|
|
117
|
+
return this.transcript(thing.id);
|
|
118
|
+
const spec = texts[thing.type];
|
|
119
|
+
if (!spec)
|
|
120
|
+
return invalid();
|
|
121
|
+
const from = `FROM ${spec.table} n ${spec.join ?? ""} WHERE n.id=? AND ${spec.where}`;
|
|
122
|
+
this.budget.rows(this.database, from, spec.fields, [thing.id]);
|
|
123
|
+
const row = this.database
|
|
124
|
+
.prepare(`SELECT ${spec.fields.map((f, i) => `${f} AS v${i}`).join(",")} ${from}`)
|
|
125
|
+
.get(thing.id);
|
|
126
|
+
if (!row)
|
|
127
|
+
return invalid();
|
|
128
|
+
return this.digest(thing.type, spec.fields.map((_f, i) => row[`v${i}`]));
|
|
129
|
+
}
|
|
130
|
+
transcript(id) {
|
|
131
|
+
const from = "FROM meeting_transcripts n JOIN meetings m ON m.id=n.meeting_id WHERE n.id=? AND n.deleted_at IS NULL AND m.deleted_at IS NULL";
|
|
132
|
+
this.budget.rows(this.database, from, ["n.format", "n.language", "n.source"], [id]);
|
|
133
|
+
const header = this.database.prepare(`SELECT n.format,n.language,n.source ${from}`).get(id);
|
|
134
|
+
if (!header)
|
|
135
|
+
return invalid();
|
|
136
|
+
const rows = "FROM meeting_transcript_rows n WHERE n.meeting_transcript_id=?";
|
|
137
|
+
this.budget.rows(this.database, rows, ["n.position", "n.start_ms", "n.end_ms", "n.speaker_name", "n.text"], [id]);
|
|
138
|
+
const cues = this.database
|
|
139
|
+
.prepare(`SELECT n.position,n.start_ms,n.end_ms,n.speaker_name,n.text ${rows} ORDER BY n.position LIMIT 1001`)
|
|
140
|
+
.all(id);
|
|
141
|
+
return this.digest("meeting_transcript", [header, cues]);
|
|
142
|
+
}
|
|
143
|
+
digest(kind, content) {
|
|
144
|
+
return createHash("sha256")
|
|
145
|
+
.update(JSON.stringify(["wirecat.project-source", 1, kind, content]))
|
|
146
|
+
.digest("hex");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { projectAnnotationRequest } from "../../project-transfer/request.js";
|
|
3
|
+
import { write } from "../transaction.js";
|
|
4
|
+
import { SnapshotBudget } from "./budget.js";
|
|
5
|
+
import { previewAnnotationSnapshot } from "./preview.js";
|
|
6
|
+
import { restoreAnnotationRecords } from "./records.js";
|
|
7
|
+
export const projectAnnotationStoreOver = ({ database }) => ({
|
|
8
|
+
async preview(request) {
|
|
9
|
+
const { snapshot, projectId } = projectAnnotationRequest(request);
|
|
10
|
+
return previewAnnotationSnapshot(database, projectId, snapshot, request.bindings, new SnapshotBudget(request.maxReadBytes ?? 4 * 1024 * 1024));
|
|
11
|
+
},
|
|
12
|
+
async restore(request) {
|
|
13
|
+
const { snapshot, projectId } = projectAnnotationRequest(request, true);
|
|
14
|
+
const budget = new SnapshotBudget(request.maxReadBytes ?? 4 * 1024 * 1024);
|
|
15
|
+
try {
|
|
16
|
+
return write(database, () => {
|
|
17
|
+
const preview = previewAnnotationSnapshot(database, projectId, snapshot, request.bindings, budget);
|
|
18
|
+
if (!preview.ready || preview.fingerprint !== request.fingerprint)
|
|
19
|
+
throw new CliError("validation_error", "project annotation preview is stale or has conflicts; preview again");
|
|
20
|
+
const mappings = restoreAnnotationRecords(database, projectId, snapshot, request.bindings);
|
|
21
|
+
return {
|
|
22
|
+
project: request.project,
|
|
23
|
+
fingerprint: preview.fingerprint,
|
|
24
|
+
mappings,
|
|
25
|
+
counts: { aliases: snapshot.aliases.length, facts: snapshot.facts.length, links: snapshot.links.length },
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (error instanceof CliError)
|
|
31
|
+
throw error;
|
|
32
|
+
throw new CliError("configuration_error", "project annotation restore failed; its transaction was rolled back");
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
});
|
package/dist/store/store.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { MeetingFindingsStore } from "./meeting-findings.js";
|
|
|
10
10
|
import { type MentionsStore } from "./mentions.js";
|
|
11
11
|
import { type MessagesStore } from "./messages.js";
|
|
12
12
|
import { type PeopleStore } from "./people.js";
|
|
13
|
+
import type { ProjectAnnotationStore } from "./project-transfer/store.js";
|
|
13
14
|
import { type QueriesStore } from "./queries.js";
|
|
14
15
|
import { type RosterStore } from "./roster.js";
|
|
15
16
|
import { type SearchIndexStore } from "./search-index.js";
|
|
@@ -34,6 +35,7 @@ import { type TagsStore } from "./tags.js";
|
|
|
34
35
|
import type { AccountKey } from "./types.js";
|
|
35
36
|
import { type VectorsStore } from "./vectors.js";
|
|
36
37
|
export interface MessageStore {
|
|
38
|
+
readonly projectAnnotations?: ProjectAnnotationStore;
|
|
37
39
|
readonly contextEvidence?: ContextEvidenceReader;
|
|
38
40
|
readonly contextIndex?: ContextIndex;
|
|
39
41
|
readonly contextRecords?: ContextRecordReader;
|
package/dist/store/store.js
CHANGED
|
@@ -31,6 +31,7 @@ import { sourceMentionStoreOver } from "./sqlite/mention-sources.js";
|
|
|
31
31
|
import { noteSearchOver } from "./sqlite/note-search.js";
|
|
32
32
|
import { notesStoreOver } from "./sqlite/notes.js";
|
|
33
33
|
import { openStoreContext } from "./sqlite/open-store.js";
|
|
34
|
+
import { projectAnnotationStoreOver } from "./sqlite/project-transfer/store.js";
|
|
34
35
|
import { proposedActionsStoreOver } from "./sqlite/proposed-actions.js";
|
|
35
36
|
import * as stems from "./sqlite/stems.js";
|
|
36
37
|
import { taskStoreOver } from "./sqlite/tasks.js";
|
|
@@ -82,6 +83,7 @@ const storeOver = (context) => {
|
|
|
82
83
|
mentions: areaOf(() => accountMentionsOver(context), (keyed) => mentionsStoreOver(context, keyed)),
|
|
83
84
|
};
|
|
84
85
|
const contextEvidence = once(() => contextEvidenceOver(context));
|
|
86
|
+
const projectAnnotations = once(() => projectAnnotationStoreOver(context));
|
|
85
87
|
const contextIndex = once(() => contextIndexOver(context, stemmerFor));
|
|
86
88
|
const contextRecords = once(() => contextRecordsOver(context));
|
|
87
89
|
const searches = once(() => searchesStoreOver(context));
|
|
@@ -102,6 +104,9 @@ const storeOver = (context) => {
|
|
|
102
104
|
const agentActions = once(() => agentActionsStoreOver(context, pruneLogsWhenDue));
|
|
103
105
|
const meetings = once(() => meetingStoreOver(context));
|
|
104
106
|
const store = {
|
|
107
|
+
get projectAnnotations() {
|
|
108
|
+
return projectAnnotations();
|
|
109
|
+
},
|
|
105
110
|
get contextEvidence() {
|
|
106
111
|
return contextEvidence();
|
|
107
112
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.374.0",
|
|
4
4
|
"description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|