@hasna/microservices 0.0.2 → 0.0.3
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/bin/index.js +7 -0
- package/bin/mcp.js +8 -1
- package/dist/index.js +7 -0
- package/microservices/microservice-transcriber/package.json +28 -0
- package/microservices/microservice-transcriber/src/cli/index.ts +1347 -0
- package/microservices/microservice-transcriber/src/db/annotations.ts +37 -0
- package/microservices/microservice-transcriber/src/db/database.ts +82 -0
- package/microservices/microservice-transcriber/src/db/migrations.ts +72 -0
- package/microservices/microservice-transcriber/src/db/transcripts.ts +395 -0
- package/microservices/microservice-transcriber/src/index.ts +43 -0
- package/microservices/microservice-transcriber/src/lib/config.ts +77 -0
- package/microservices/microservice-transcriber/src/lib/diff.ts +91 -0
- package/microservices/microservice-transcriber/src/lib/downloader.ts +570 -0
- package/microservices/microservice-transcriber/src/lib/feeds.ts +62 -0
- package/microservices/microservice-transcriber/src/lib/live.ts +94 -0
- package/microservices/microservice-transcriber/src/lib/notion.ts +129 -0
- package/microservices/microservice-transcriber/src/lib/providers.ts +713 -0
- package/microservices/microservice-transcriber/src/lib/summarizer.ts +147 -0
- package/microservices/microservice-transcriber/src/lib/translator.ts +75 -0
- package/microservices/microservice-transcriber/src/lib/webhook.ts +37 -0
- package/microservices/microservice-transcriber/src/mcp/index.ts +1070 -0
- package/microservices/microservice-transcriber/src/server/index.ts +199 -0
- package/package.json +1 -1
- package/microservices/microservice-invoices/dashboard/dist/assets/index-Bngq7FNM.css +0 -1
- package/microservices/microservice-invoices/dashboard/dist/assets/index-aHW4ARZR.js +0 -124
- package/microservices/microservice-invoices/dashboard/dist/index.html +0 -13
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent config for microservice-transcriber.
|
|
3
|
+
* Stored as JSON alongside data.db in the .microservices directory.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
|
|
9
|
+
export interface TranscriberConfig {
|
|
10
|
+
defaultProvider: "elevenlabs" | "openai" | "deepgram";
|
|
11
|
+
defaultLanguage: string;
|
|
12
|
+
defaultFormat: "txt" | "srt" | "vtt" | "json";
|
|
13
|
+
diarize: boolean;
|
|
14
|
+
vocab: string[]; // custom vocabulary hints for transcription accuracy
|
|
15
|
+
feeds: Array<{ url: string; title: string | null; lastChecked: string | null }>;
|
|
16
|
+
webhook: string | null; // URL to POST when transcription completes
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const CONFIG_DEFAULTS: TranscriberConfig = {
|
|
20
|
+
defaultProvider: "elevenlabs",
|
|
21
|
+
defaultLanguage: "en",
|
|
22
|
+
defaultFormat: "txt",
|
|
23
|
+
diarize: false,
|
|
24
|
+
vocab: [],
|
|
25
|
+
feeds: [],
|
|
26
|
+
webhook: null,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function getConfigPath(): string {
|
|
30
|
+
if (process.env["MICROSERVICES_DIR"]) {
|
|
31
|
+
return join(process.env["MICROSERVICES_DIR"], "microservice-transcriber", "config.json");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let dir = resolve(process.cwd());
|
|
35
|
+
while (true) {
|
|
36
|
+
const msDir = join(dir, ".microservices");
|
|
37
|
+
if (existsSync(msDir)) return join(msDir, "microservice-transcriber", "config.json");
|
|
38
|
+
const parent = dirname(dir);
|
|
39
|
+
if (parent === dir) break;
|
|
40
|
+
dir = parent;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
44
|
+
return join(home, ".microservices", "microservice-transcriber", "config.json");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getConfig(): TranscriberConfig {
|
|
48
|
+
const path = getConfigPath();
|
|
49
|
+
if (!existsSync(path)) return { ...CONFIG_DEFAULTS };
|
|
50
|
+
try {
|
|
51
|
+
const raw = JSON.parse(readFileSync(path, "utf8"));
|
|
52
|
+
return { ...CONFIG_DEFAULTS, ...raw };
|
|
53
|
+
} catch {
|
|
54
|
+
return { ...CONFIG_DEFAULTS };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function setConfig(updates: Partial<TranscriberConfig>): TranscriberConfig {
|
|
59
|
+
const current = getConfig();
|
|
60
|
+
const next = { ...current, ...updates };
|
|
61
|
+
const path = getConfigPath();
|
|
62
|
+
const dir = dirname(path);
|
|
63
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
64
|
+
writeFileSync(path, JSON.stringify(next, null, 2), "utf8");
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function resetConfig(): TranscriberConfig {
|
|
69
|
+
const path = getConfigPath();
|
|
70
|
+
const dir = dirname(path);
|
|
71
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
72
|
+
writeFileSync(path, JSON.stringify(CONFIG_DEFAULTS, null, 2), "utf8");
|
|
73
|
+
return { ...CONFIG_DEFAULTS };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const CONFIG_KEYS = ["defaultProvider", "defaultLanguage", "defaultFormat", "diarize", "vocab", "webhook"] as const;
|
|
77
|
+
export type ConfigKey = (typeof CONFIG_KEYS)[number];
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple word-level diff for comparing two transcripts.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface DiffEntry {
|
|
6
|
+
type: "equal" | "added" | "removed";
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Compute a word-level diff between two texts.
|
|
12
|
+
* Uses a simple longest common subsequence approach.
|
|
13
|
+
*/
|
|
14
|
+
export function wordDiff(textA: string, textB: string): DiffEntry[] {
|
|
15
|
+
const wordsA = textA.split(/\s+/).filter(Boolean);
|
|
16
|
+
const wordsB = textB.split(/\s+/).filter(Boolean);
|
|
17
|
+
|
|
18
|
+
// LCS table
|
|
19
|
+
const m = wordsA.length;
|
|
20
|
+
const n = wordsB.length;
|
|
21
|
+
const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
22
|
+
|
|
23
|
+
for (let i = 1; i <= m; i++) {
|
|
24
|
+
for (let j = 1; j <= n; j++) {
|
|
25
|
+
if (wordsA[i - 1] === wordsB[j - 1]) {
|
|
26
|
+
dp[i][j] = dp[i - 1][j - 1] + 1;
|
|
27
|
+
} else {
|
|
28
|
+
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Backtrack to build diff
|
|
34
|
+
const result: DiffEntry[] = [];
|
|
35
|
+
let i = m, j = n;
|
|
36
|
+
|
|
37
|
+
while (i > 0 || j > 0) {
|
|
38
|
+
if (i > 0 && j > 0 && wordsA[i - 1] === wordsB[j - 1]) {
|
|
39
|
+
result.unshift({ type: "equal", text: wordsA[i - 1] });
|
|
40
|
+
i--; j--;
|
|
41
|
+
} else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
|
|
42
|
+
result.unshift({ type: "added", text: wordsB[j - 1] });
|
|
43
|
+
j--;
|
|
44
|
+
} else {
|
|
45
|
+
result.unshift({ type: "removed", text: wordsA[i - 1] });
|
|
46
|
+
i--;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Merge consecutive entries of the same type
|
|
51
|
+
const merged: DiffEntry[] = [];
|
|
52
|
+
for (const entry of result) {
|
|
53
|
+
const last = merged[merged.length - 1];
|
|
54
|
+
if (last && last.type === entry.type) {
|
|
55
|
+
last.text += " " + entry.text;
|
|
56
|
+
} else {
|
|
57
|
+
merged.push({ ...entry });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return merged;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Format a diff for terminal display.
|
|
66
|
+
*/
|
|
67
|
+
export function formatDiff(entries: DiffEntry[]): string {
|
|
68
|
+
return entries
|
|
69
|
+
.map((e) => {
|
|
70
|
+
if (e.type === "equal") return e.text;
|
|
71
|
+
if (e.type === "added") return `[+${e.text}+]`;
|
|
72
|
+
return `[-${e.text}-]`;
|
|
73
|
+
})
|
|
74
|
+
.join(" ");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Compute diff stats.
|
|
79
|
+
*/
|
|
80
|
+
export function diffStats(entries: DiffEntry[]): { equal: number; added: number; removed: number; similarity: number } {
|
|
81
|
+
const equalWords = entries.filter((e) => e.type === "equal").reduce((n, e) => n + e.text.split(/\s+/).length, 0);
|
|
82
|
+
const addedWords = entries.filter((e) => e.type === "added").reduce((n, e) => n + e.text.split(/\s+/).length, 0);
|
|
83
|
+
const removedWords = entries.filter((e) => e.type === "removed").reduce((n, e) => n + e.text.split(/\s+/).length, 0);
|
|
84
|
+
const total = equalWords + addedWords + removedWords;
|
|
85
|
+
return {
|
|
86
|
+
equal: equalWords,
|
|
87
|
+
added: addedWords,
|
|
88
|
+
removed: removedWords,
|
|
89
|
+
similarity: total > 0 ? Math.round((equalWords / total) * 100) : 100,
|
|
90
|
+
};
|
|
91
|
+
}
|