@hasna/recordings 0.1.20 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +191 -170
- package/README.md +8 -1
- package/dist/cli/index.js +155 -4
- package/dist/db/database.d.ts.map +1 -1
- package/dist/index.js +26 -1
- package/dist/mcp/http.d.ts +12 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +2 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +444 -354
- package/dist/version.d.ts +1 -1
- package/package.json +5 -1
- package/scripts/install_macos_app.sh +32 -0
- package/src/native/Recordings/RecordingsLib/MenuBarPopover.swift +2 -3
- package/src/native/Recordings/RecordingsLib/RealtimeTranscriptionClient.swift +76 -12
- package/src/native/Recordings/RecordingsLib/SettingsView.swift +27 -1
- package/src/native/Recordings/RecordingsTests/NativeAppDiagnosticsTests.swift +23 -0
- package/src/native/Recordings/RecordingsTests/RealtimeTranscriptionTests.swift +13 -0
package/dist/mcp/index.js
CHANGED
|
@@ -21,9 +21,13 @@ var __require = import.meta.require;
|
|
|
21
21
|
var require_package = __commonJS((exports, module) => {
|
|
22
22
|
module.exports = {
|
|
23
23
|
name: "@hasna/recordings",
|
|
24
|
-
version: "0.1.
|
|
24
|
+
version: "0.1.21",
|
|
25
25
|
type: "module",
|
|
26
26
|
description: "Speech-to-text recording tool with MCP and CLI \u2014 records, transcribes, and optionally enhances text using AI",
|
|
27
|
+
repository: {
|
|
28
|
+
type: "git",
|
|
29
|
+
url: "git+https://github.com/hasna/recordings.git"
|
|
30
|
+
},
|
|
27
31
|
main: "dist/index.js",
|
|
28
32
|
types: "dist/index.d.ts",
|
|
29
33
|
bin: {
|
|
@@ -86,6 +90,51 @@ var require_package = __commonJS((exports, module) => {
|
|
|
86
90
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
87
91
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
88
92
|
|
|
93
|
+
// src/mcp/http.ts
|
|
94
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
95
|
+
var DEFAULT_MCP_HTTP_PORT = 8829;
|
|
96
|
+
var MCP_HTTP_HOST = "127.0.0.1";
|
|
97
|
+
function isHttpMode(args) {
|
|
98
|
+
return args.includes("--http") || process.env.MCP_HTTP === "1";
|
|
99
|
+
}
|
|
100
|
+
function resolveMcpHttpPort(args) {
|
|
101
|
+
const portIdx = args.indexOf("--port");
|
|
102
|
+
if (portIdx >= 0 && args[portIdx + 1]) {
|
|
103
|
+
return Number(args[portIdx + 1]);
|
|
104
|
+
}
|
|
105
|
+
const envPort = process.env.MCP_HTTP_PORT;
|
|
106
|
+
if (envPort)
|
|
107
|
+
return Number(envPort);
|
|
108
|
+
return DEFAULT_MCP_HTTP_PORT;
|
|
109
|
+
}
|
|
110
|
+
async function handleMcpRequest(req, buildServer) {
|
|
111
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
112
|
+
sessionIdGenerator: undefined
|
|
113
|
+
});
|
|
114
|
+
const server = buildServer();
|
|
115
|
+
await server.connect(transport);
|
|
116
|
+
return transport.handleRequest(req);
|
|
117
|
+
}
|
|
118
|
+
function startMcpHttpServer(options) {
|
|
119
|
+
const { name, port, buildServer } = options;
|
|
120
|
+
const server = Bun.serve({
|
|
121
|
+
hostname: MCP_HTTP_HOST,
|
|
122
|
+
port,
|
|
123
|
+
async fetch(req) {
|
|
124
|
+
const url = new URL(req.url);
|
|
125
|
+
if (url.pathname === "/health" && req.method === "GET") {
|
|
126
|
+
return Response.json({ status: "ok", name });
|
|
127
|
+
}
|
|
128
|
+
if (url.pathname === "/mcp") {
|
|
129
|
+
return handleMcpRequest(req, buildServer);
|
|
130
|
+
}
|
|
131
|
+
return new Response("Not Found", { status: 404 });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
console.error(`${name}-mcp HTTP listening on http://${MCP_HTTP_HOST}:${port}/mcp`);
|
|
135
|
+
return server;
|
|
136
|
+
}
|
|
137
|
+
|
|
89
138
|
// node_modules/@hasna/cloud/dist/index.js
|
|
90
139
|
import { createRequire } from "module";
|
|
91
140
|
import { Database } from "bun:sqlite";
|
|
@@ -14526,15 +14575,40 @@ function runMigrations(db) {
|
|
|
14526
14575
|
const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
14527
14576
|
const currentLevel = result?.max_id ?? -1;
|
|
14528
14577
|
for (let i = currentLevel + 1;i < MIGRATIONS.length; i++) {
|
|
14529
|
-
db.run(MIGRATIONS[i]);
|
|
14530
14578
|
try {
|
|
14579
|
+
db.run(MIGRATIONS[i]);
|
|
14531
14580
|
db.query("INSERT INTO _migrations (id) VALUES (?)").run(i);
|
|
14532
14581
|
} catch (e) {
|
|
14582
|
+
if (isBenignMigrationError(e)) {
|
|
14583
|
+
db.query("INSERT OR IGNORE INTO _migrations (id) VALUES (?)").run(i);
|
|
14584
|
+
continue;
|
|
14585
|
+
}
|
|
14533
14586
|
if (!(e instanceof Error && e.message.includes("UNIQUE constraint failed"))) {
|
|
14534
14587
|
throw e;
|
|
14535
14588
|
}
|
|
14536
14589
|
}
|
|
14537
14590
|
}
|
|
14591
|
+
repairSchemaDrift(db);
|
|
14592
|
+
}
|
|
14593
|
+
function isBenignMigrationError(error) {
|
|
14594
|
+
if (!(error instanceof Error))
|
|
14595
|
+
return false;
|
|
14596
|
+
return error.message.includes("duplicate column name");
|
|
14597
|
+
}
|
|
14598
|
+
function repairSchemaDrift(db) {
|
|
14599
|
+
ensureColumn(db, "recordings", "goal", "TEXT");
|
|
14600
|
+
ensureColumn(db, "recordings", "role", "TEXT");
|
|
14601
|
+
ensureColumn(db, "recordings", "task_list_id", "TEXT");
|
|
14602
|
+
ensureColumn(db, "recordings", "machine_id", "TEXT");
|
|
14603
|
+
ensureColumn(db, "recordings", "metadata", "TEXT DEFAULT '{}'");
|
|
14604
|
+
ensureColumn(db, "agents", "active_project_id", "TEXT REFERENCES projects(id) ON DELETE SET NULL");
|
|
14605
|
+
}
|
|
14606
|
+
function ensureColumn(db, table, column, definition) {
|
|
14607
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
14608
|
+
if (rows.some((row) => row.name === column)) {
|
|
14609
|
+
return;
|
|
14610
|
+
}
|
|
14611
|
+
db.run(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
14538
14612
|
}
|
|
14539
14613
|
function getAdapter() {
|
|
14540
14614
|
if (!_adapter) {
|
|
@@ -14954,388 +15028,404 @@ async function processText(rawText, config, systemPrompt) {
|
|
|
14954
15028
|
}
|
|
14955
15029
|
|
|
14956
15030
|
// src/version.ts
|
|
14957
|
-
var VERSION = "0.1.
|
|
15031
|
+
var VERSION = "0.1.21";
|
|
14958
15032
|
|
|
14959
15033
|
// src/mcp/index.ts
|
|
14960
15034
|
var config = loadConfig();
|
|
14961
15035
|
ensureDataDir(config);
|
|
14962
15036
|
getDatabase(config.db_path);
|
|
14963
|
-
|
|
14964
|
-
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
}
|
|
14971
|
-
|
|
14972
|
-
|
|
14973
|
-
|
|
14974
|
-
}
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
|
|
14978
|
-
}
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
|
|
14983
|
-
|
|
14984
|
-
|
|
14985
|
-
|
|
14986
|
-
|
|
14987
|
-
|
|
14988
|
-
|
|
14989
|
-
|
|
14990
|
-
|
|
14991
|
-
|
|
14992
|
-
|
|
14993
|
-
|
|
14994
|
-
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14999
|
-
|
|
15000
|
-
|
|
15037
|
+
function buildServer() {
|
|
15038
|
+
const server = new McpServer({
|
|
15039
|
+
name: "recordings",
|
|
15040
|
+
version: VERSION
|
|
15041
|
+
});
|
|
15042
|
+
const registerTool = server.tool.bind(server);
|
|
15043
|
+
function text(content) {
|
|
15044
|
+
return { content: [{ type: "text", text: content }] };
|
|
15045
|
+
}
|
|
15046
|
+
function errorResult(e) {
|
|
15047
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
15048
|
+
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
15049
|
+
}
|
|
15050
|
+
function compact(r) {
|
|
15051
|
+
const t = (r.processed_text || r.raw_text).slice(0, 80);
|
|
15052
|
+
return `${r.id.slice(0, 8)} | ${r.processing_mode} | ${r.created_at.slice(0, 16)} | ${t}${t.length >= 80 ? "..." : ""}`;
|
|
15053
|
+
}
|
|
15054
|
+
function full(r) {
|
|
15055
|
+
const lines = [`ID: ${r.id}`, `Mode: ${r.processing_mode}`, `Model: ${r.model_used}`];
|
|
15056
|
+
if (r.enhancement_model)
|
|
15057
|
+
lines.push(`Enhanced by: ${r.enhancement_model}`);
|
|
15058
|
+
if (r.duration_ms)
|
|
15059
|
+
lines.push(`Duration: ${(r.duration_ms / 1000).toFixed(1)}s`);
|
|
15060
|
+
if (r.language)
|
|
15061
|
+
lines.push(`Language: ${r.language}`);
|
|
15062
|
+
if (r.tags.length > 0)
|
|
15063
|
+
lines.push(`Tags: ${r.tags.join(", ")}`);
|
|
15064
|
+
if (r.agent_id)
|
|
15065
|
+
lines.push(`Agent: ${r.agent_id}`);
|
|
15066
|
+
if (r.project_id)
|
|
15067
|
+
lines.push(`Project: ${r.project_id}`);
|
|
15068
|
+
if (r.session_id)
|
|
15069
|
+
lines.push(`Session: ${r.session_id}`);
|
|
15070
|
+
lines.push(`Created: ${r.created_at}`);
|
|
15071
|
+
lines.push(`Text: ${r.raw_text}`);
|
|
15072
|
+
if (r.processed_text && r.processed_text !== r.raw_text) {
|
|
15073
|
+
lines.push(`Enhanced: ${r.processed_text}`);
|
|
15074
|
+
}
|
|
15075
|
+
return lines.join(`
|
|
15001
15076
|
`);
|
|
15002
|
-
}
|
|
15003
|
-
async function saveRecordingMemento(args) {
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15011
|
-
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
|
|
15017
|
-
|
|
15018
|
-
|
|
15019
|
-
|
|
15020
|
-
|
|
15021
|
-
|
|
15022
|
-
|
|
15023
|
-
|
|
15024
|
-
|
|
15025
|
-
|
|
15026
|
-
}
|
|
15027
|
-
|
|
15028
|
-
|
|
15077
|
+
}
|
|
15078
|
+
async function saveRecordingMemento(args) {
|
|
15079
|
+
try {
|
|
15080
|
+
const proc = Bun.spawn([
|
|
15081
|
+
"mementos",
|
|
15082
|
+
"save",
|
|
15083
|
+
"--scope",
|
|
15084
|
+
"shared",
|
|
15085
|
+
"--category",
|
|
15086
|
+
"history",
|
|
15087
|
+
"--importance",
|
|
15088
|
+
"5",
|
|
15089
|
+
"--tags",
|
|
15090
|
+
"recording,transcription",
|
|
15091
|
+
"--summary",
|
|
15092
|
+
args.summary,
|
|
15093
|
+
args.key,
|
|
15094
|
+
args.value
|
|
15095
|
+
], {
|
|
15096
|
+
stdout: "ignore",
|
|
15097
|
+
stderr: "ignore"
|
|
15098
|
+
});
|
|
15099
|
+
await proc.exited;
|
|
15100
|
+
} catch {}
|
|
15101
|
+
}
|
|
15102
|
+
const toolDocs = {
|
|
15103
|
+
transcribe_audio: `Transcribe audio file. Auto-enhances if needed.
|
|
15029
15104
|
Params: audio_path (string, required): path to wav/mp3/m4a/webm | language (string): ISO code e.g. en/es/fr | no_enhance (bool): skip AI enhancement | tags (string[]): tags | agent_id (string) | project_id (string) | session_id (string)`,
|
|
15030
|
-
|
|
15105
|
+
save_recording: `Save text as recording. Auto-enhances if needed.
|
|
15031
15106
|
Params: text (string, required): text to save | enhance (bool): force enhancement | tags (string[]) | agent_id (string) | project_id (string) | session_id (string) | metadata (object)`,
|
|
15032
|
-
|
|
15107
|
+
get_recording: `Get recording by ID or prefix.
|
|
15033
15108
|
Params: id (string, required): recording ID or prefix`,
|
|
15034
|
-
|
|
15109
|
+
list_recordings: `List recordings, compact by default, most recent first.
|
|
15035
15110
|
Params: limit (number, default 10) | offset (number) | processing_mode ('raw'|'enhanced') | tags (string[]) | search (string): text search | since/until (ISO date) | agent_id | project_id | session_id | full (bool): verbose output`,
|
|
15036
|
-
|
|
15111
|
+
search_recordings: `Search recordings by text content.
|
|
15037
15112
|
Params: query (string, required) | limit (number, default 10) | agent_id | project_id | full (bool): verbose output`,
|
|
15038
|
-
|
|
15113
|
+
delete_recording: `Delete recording by ID.
|
|
15039
15114
|
Params: id (string, required)`,
|
|
15040
|
-
|
|
15115
|
+
recording_stats: `Recording count, mode breakdown, duration.
|
|
15041
15116
|
Params: none`,
|
|
15042
|
-
|
|
15117
|
+
detect_enhancement: `Check if text needs AI enhancement.
|
|
15043
15118
|
Params: text (string, required)`,
|
|
15044
|
-
|
|
15119
|
+
register_agent: `Register agent (idempotent). Auto-updates last_seen_at on re-register.
|
|
15045
15120
|
Params: name (string, required) | description (string) | role (string)`,
|
|
15046
|
-
|
|
15121
|
+
list_agents: `List registered agents.
|
|
15047
15122
|
Params: none`,
|
|
15048
|
-
|
|
15123
|
+
get_agent: `Get agent by ID or name.
|
|
15049
15124
|
Params: id (string, required)`,
|
|
15050
|
-
|
|
15125
|
+
heartbeat: `Update last_seen_at to signal agent is active.
|
|
15051
15126
|
Params: agent_id (string, required): agent ID or name`,
|
|
15052
|
-
|
|
15127
|
+
set_focus: `Set active project context for this agent session.
|
|
15053
15128
|
Params: agent_id (string, required) | project_id (string, nullable): project ID or null to clear`,
|
|
15054
|
-
|
|
15129
|
+
register_project: `Register project (idempotent).
|
|
15055
15130
|
Params: name (string, required) | path (string, required): absolute path | description (string)`,
|
|
15056
|
-
|
|
15131
|
+
list_projects: `List registered projects.
|
|
15057
15132
|
Params: none`
|
|
15058
|
-
};
|
|
15059
|
-
registerTool("describe_tool", "Get full param docs for any tool.", { name: exports_external2.string() }, async (args) => {
|
|
15060
|
-
|
|
15061
|
-
|
|
15062
|
-
});
|
|
15063
|
-
registerTool("transcribe_audio", "Transcribe audio file. Auto-enhances if needed.", {
|
|
15064
|
-
|
|
15065
|
-
|
|
15066
|
-
|
|
15067
|
-
|
|
15068
|
-
|
|
15069
|
-
|
|
15070
|
-
|
|
15071
|
-
}, async (args) => {
|
|
15072
|
-
|
|
15073
|
-
|
|
15074
|
-
|
|
15075
|
-
|
|
15076
|
-
|
|
15077
|
-
|
|
15078
|
-
|
|
15079
|
-
|
|
15080
|
-
|
|
15081
|
-
|
|
15082
|
-
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15089
|
-
|
|
15090
|
-
|
|
15091
|
-
|
|
15092
|
-
|
|
15093
|
-
|
|
15094
|
-
|
|
15095
|
-
|
|
15096
|
-
|
|
15097
|
-
|
|
15098
|
-
|
|
15099
|
-
|
|
15100
|
-
|
|
15101
|
-
|
|
15102
|
-
|
|
15103
|
-
|
|
15104
|
-
|
|
15105
|
-
|
|
15106
|
-
|
|
15133
|
+
};
|
|
15134
|
+
registerTool("describe_tool", "Get full param docs for any tool.", { name: exports_external2.string() }, async (args) => {
|
|
15135
|
+
const doc = toolDocs[args.name];
|
|
15136
|
+
return doc ? text(doc) : text(`Unknown tool: ${args.name}. Available: ${Object.keys(toolDocs).join(", ")}`);
|
|
15137
|
+
});
|
|
15138
|
+
registerTool("transcribe_audio", "Transcribe audio file. Auto-enhances if needed.", {
|
|
15139
|
+
audio_path: exports_external2.string(),
|
|
15140
|
+
language: exports_external2.string().optional(),
|
|
15141
|
+
no_enhance: exports_external2.boolean().optional(),
|
|
15142
|
+
tags: exports_external2.array(exports_external2.string()).optional(),
|
|
15143
|
+
agent_id: exports_external2.string().optional(),
|
|
15144
|
+
project_id: exports_external2.string().optional(),
|
|
15145
|
+
session_id: exports_external2.string().optional()
|
|
15146
|
+
}, async (args) => {
|
|
15147
|
+
try {
|
|
15148
|
+
const cfg = { ...config };
|
|
15149
|
+
if (args.language)
|
|
15150
|
+
cfg.language = args.language;
|
|
15151
|
+
if (args.no_enhance)
|
|
15152
|
+
cfg.auto_enhance = false;
|
|
15153
|
+
const transcription = await transcribeAudio(args.audio_path, cfg);
|
|
15154
|
+
const processed = await processText(transcription.text, cfg);
|
|
15155
|
+
const recording = createRecording({
|
|
15156
|
+
audio_path: args.audio_path,
|
|
15157
|
+
raw_text: transcription.text,
|
|
15158
|
+
processed_text: processed.mode === "enhanced" ? processed.text : undefined,
|
|
15159
|
+
processing_mode: processed.mode,
|
|
15160
|
+
model_used: transcription.model,
|
|
15161
|
+
enhancement_model: processed.enhancement_model || undefined,
|
|
15162
|
+
duration_ms: transcription.duration_ms,
|
|
15163
|
+
language: transcription.language || undefined,
|
|
15164
|
+
tags: args.tags,
|
|
15165
|
+
agent_id: args.agent_id,
|
|
15166
|
+
project_id: args.project_id,
|
|
15167
|
+
session_id: args.session_id
|
|
15168
|
+
});
|
|
15169
|
+
if (args.agent_id) {
|
|
15170
|
+
await saveRecordingMemento({
|
|
15171
|
+
key: `recording-${recording.id}`,
|
|
15172
|
+
value: JSON.stringify({
|
|
15173
|
+
recording_id: recording.id,
|
|
15174
|
+
text: processed.mode === "enhanced" ? processed.text : transcription.text,
|
|
15175
|
+
agent_id: args.agent_id,
|
|
15176
|
+
project_id: args.project_id,
|
|
15177
|
+
session_id: args.session_id,
|
|
15178
|
+
created_at: recording.created_at
|
|
15179
|
+
}),
|
|
15180
|
+
summary: `Recording ${recording.id.slice(0, 8)} for ${args.agent_id}`
|
|
15181
|
+
});
|
|
15182
|
+
}
|
|
15183
|
+
const output = processed.mode === "enhanced" ? processed.text : transcription.text;
|
|
15184
|
+
return text(`${recording.id.slice(0, 8)} | ${processed.mode} | ${output}`);
|
|
15185
|
+
} catch (e) {
|
|
15186
|
+
return errorResult(e);
|
|
15107
15187
|
}
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
|
|
15123
|
-
|
|
15124
|
-
|
|
15125
|
-
|
|
15126
|
-
|
|
15127
|
-
|
|
15128
|
-
|
|
15129
|
-
|
|
15130
|
-
|
|
15131
|
-
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
15137
|
-
|
|
15138
|
-
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
15143
|
-
|
|
15144
|
-
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15148
|
-
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
|
|
15152
|
-
}
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
});
|
|
15159
|
-
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
|
|
15163
|
-
|
|
15164
|
-
|
|
15165
|
-
|
|
15166
|
-
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
|
|
15186
|
-
|
|
15187
|
-
|
|
15188
|
-
|
|
15189
|
-
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15195
|
-
const recordings = listRecordings(filter);
|
|
15196
|
-
if (recordings.length === 0)
|
|
15197
|
-
return text("No recordings found.");
|
|
15198
|
-
const fmt = args.full ? full : compact;
|
|
15199
|
-
const sep = args.full ? `
|
|
15188
|
+
});
|
|
15189
|
+
registerTool("save_recording", "Save text as recording. Auto-enhances if needed.", {
|
|
15190
|
+
text: exports_external2.string(),
|
|
15191
|
+
enhance: exports_external2.boolean().optional(),
|
|
15192
|
+
tags: exports_external2.array(exports_external2.string()).optional(),
|
|
15193
|
+
agent_id: exports_external2.string().optional(),
|
|
15194
|
+
project_id: exports_external2.string().optional(),
|
|
15195
|
+
session_id: exports_external2.string().optional(),
|
|
15196
|
+
goal: exports_external2.string().optional().describe("Goal or purpose of this recording session (e.g. 'code review for PR #123')"),
|
|
15197
|
+
role: exports_external2.string().optional().describe("Agent role for this session (e.g. 'dev agent for connectdev')"),
|
|
15198
|
+
task_list_id: exports_external2.string().optional().describe("Task list ID to bind this recording to"),
|
|
15199
|
+
metadata: exports_external2.record(exports_external2.unknown()).optional()
|
|
15200
|
+
}, async (args) => {
|
|
15201
|
+
try {
|
|
15202
|
+
let processedText;
|
|
15203
|
+
let mode = "raw";
|
|
15204
|
+
let enhModel;
|
|
15205
|
+
if (args.enhance !== false) {
|
|
15206
|
+
const processed = await processText(args.text, config);
|
|
15207
|
+
if (processed.mode === "enhanced") {
|
|
15208
|
+
processedText = processed.text;
|
|
15209
|
+
mode = "enhanced";
|
|
15210
|
+
enhModel = processed.enhancement_model || undefined;
|
|
15211
|
+
}
|
|
15212
|
+
}
|
|
15213
|
+
const recording = createRecording({
|
|
15214
|
+
raw_text: args.text,
|
|
15215
|
+
processed_text: processedText,
|
|
15216
|
+
processing_mode: mode,
|
|
15217
|
+
model_used: "direct-input",
|
|
15218
|
+
enhancement_model: enhModel,
|
|
15219
|
+
tags: args.tags,
|
|
15220
|
+
agent_id: args.agent_id,
|
|
15221
|
+
project_id: args.project_id,
|
|
15222
|
+
session_id: args.session_id,
|
|
15223
|
+
goal: args.goal,
|
|
15224
|
+
role: args.role,
|
|
15225
|
+
task_list_id: args.task_list_id,
|
|
15226
|
+
metadata: args.metadata
|
|
15227
|
+
});
|
|
15228
|
+
const output = processedText || args.text;
|
|
15229
|
+
return text(`${recording.id.slice(0, 8)} | ${mode} | ${output}`);
|
|
15230
|
+
} catch (e) {
|
|
15231
|
+
return errorResult(e);
|
|
15232
|
+
}
|
|
15233
|
+
});
|
|
15234
|
+
registerTool("get_recording", "Get recording by ID or prefix.", { id: exports_external2.string() }, async (args) => {
|
|
15235
|
+
try {
|
|
15236
|
+
const r = getRecording(args.id);
|
|
15237
|
+
if (!r)
|
|
15238
|
+
return text(`Not found: ${args.id}`);
|
|
15239
|
+
return text(full(r));
|
|
15240
|
+
} catch (e) {
|
|
15241
|
+
return errorResult(e);
|
|
15242
|
+
}
|
|
15243
|
+
});
|
|
15244
|
+
registerTool("list_recordings", "List recordings. Compact default, recent first.", {
|
|
15245
|
+
limit: exports_external2.number().optional(),
|
|
15246
|
+
offset: exports_external2.number().optional(),
|
|
15247
|
+
processing_mode: exports_external2.enum(["raw", "enhanced"]).optional(),
|
|
15248
|
+
tags: exports_external2.array(exports_external2.string()).optional(),
|
|
15249
|
+
search: exports_external2.string().optional(),
|
|
15250
|
+
since: exports_external2.string().optional(),
|
|
15251
|
+
until: exports_external2.string().optional(),
|
|
15252
|
+
agent_id: exports_external2.string().optional(),
|
|
15253
|
+
project_id: exports_external2.string().optional(),
|
|
15254
|
+
session_id: exports_external2.string().optional(),
|
|
15255
|
+
full: exports_external2.boolean().optional()
|
|
15256
|
+
}, async (args) => {
|
|
15257
|
+
try {
|
|
15258
|
+
const filter = {
|
|
15259
|
+
limit: args.limit || 10,
|
|
15260
|
+
offset: args.offset,
|
|
15261
|
+
processing_mode: args.processing_mode,
|
|
15262
|
+
tags: args.tags,
|
|
15263
|
+
search: args.search,
|
|
15264
|
+
since: args.since,
|
|
15265
|
+
until: args.until,
|
|
15266
|
+
agent_id: args.agent_id,
|
|
15267
|
+
project_id: args.project_id,
|
|
15268
|
+
session_id: args.session_id
|
|
15269
|
+
};
|
|
15270
|
+
const recordings = listRecordings(filter);
|
|
15271
|
+
if (recordings.length === 0)
|
|
15272
|
+
return text("No recordings found.");
|
|
15273
|
+
const fmt = args.full ? full : compact;
|
|
15274
|
+
const sep = args.full ? `
|
|
15200
15275
|
---
|
|
15201
15276
|
` : `
|
|
15202
15277
|
`;
|
|
15203
|
-
|
|
15204
|
-
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
});
|
|
15208
|
-
registerTool("search_recordings", "Search recordings by text.", {
|
|
15209
|
-
|
|
15210
|
-
|
|
15211
|
-
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
}, async (args) => {
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
|
|
15219
|
-
|
|
15220
|
-
|
|
15221
|
-
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15278
|
+
return text(`${recordings.length} recording(s):${sep}${recordings.map(fmt).join(sep)}`);
|
|
15279
|
+
} catch (e) {
|
|
15280
|
+
return errorResult(e);
|
|
15281
|
+
}
|
|
15282
|
+
});
|
|
15283
|
+
registerTool("search_recordings", "Search recordings by text.", {
|
|
15284
|
+
query: exports_external2.string(),
|
|
15285
|
+
limit: exports_external2.number().optional(),
|
|
15286
|
+
agent_id: exports_external2.string().optional(),
|
|
15287
|
+
project_id: exports_external2.string().optional(),
|
|
15288
|
+
full: exports_external2.boolean().optional()
|
|
15289
|
+
}, async (args) => {
|
|
15290
|
+
try {
|
|
15291
|
+
const results = searchRecordings(args.query, {
|
|
15292
|
+
limit: args.limit || 10,
|
|
15293
|
+
agent_id: args.agent_id,
|
|
15294
|
+
project_id: args.project_id
|
|
15295
|
+
});
|
|
15296
|
+
if (results.length === 0)
|
|
15297
|
+
return text("No results.");
|
|
15298
|
+
const fmt = args.full ? full : compact;
|
|
15299
|
+
const sep = args.full ? `
|
|
15225
15300
|
---
|
|
15226
15301
|
` : `
|
|
15227
15302
|
`;
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
});
|
|
15233
|
-
registerTool("delete_recording", "Delete recording by ID.", { id: exports_external2.string() }, async (args) => {
|
|
15234
|
-
|
|
15235
|
-
|
|
15236
|
-
|
|
15237
|
-
|
|
15238
|
-
|
|
15239
|
-
});
|
|
15240
|
-
registerTool("recording_stats", "Recording stats: count, modes, duration.", {}, async () => {
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15303
|
+
return text(`${results.length} result(s):${sep}${results.map(fmt).join(sep)}`);
|
|
15304
|
+
} catch (e) {
|
|
15305
|
+
return errorResult(e);
|
|
15306
|
+
}
|
|
15307
|
+
});
|
|
15308
|
+
registerTool("delete_recording", "Delete recording by ID.", { id: exports_external2.string() }, async (args) => {
|
|
15309
|
+
try {
|
|
15310
|
+
return text(deleteRecording(args.id) ? `Deleted ${args.id}` : `Not found: ${args.id}`);
|
|
15311
|
+
} catch (e) {
|
|
15312
|
+
return errorResult(e);
|
|
15313
|
+
}
|
|
15314
|
+
});
|
|
15315
|
+
registerTool("recording_stats", "Recording stats: count, modes, duration.", {}, async () => {
|
|
15316
|
+
try {
|
|
15317
|
+
const s = getRecordingStats();
|
|
15318
|
+
let out = `Total: ${s.total} | Raw: ${s.raw} | Enhanced: ${s.enhanced} | Duration: ${(s.total_duration_ms / 1000).toFixed(1)}s`;
|
|
15319
|
+
if (Object.keys(s.by_model).length > 0) {
|
|
15320
|
+
out += `
|
|
15246
15321
|
` + Object.entries(s.by_model).map(([m, c]) => `${m}: ${c}`).join(", ");
|
|
15322
|
+
}
|
|
15323
|
+
return text(out);
|
|
15324
|
+
} catch (e) {
|
|
15325
|
+
return errorResult(e);
|
|
15247
15326
|
}
|
|
15248
|
-
|
|
15249
|
-
}
|
|
15250
|
-
|
|
15251
|
-
|
|
15252
|
-
});
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
15256
|
-
|
|
15257
|
-
}
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
});
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
}
|
|
15266
|
-
|
|
15267
|
-
|
|
15268
|
-
|
|
15269
|
-
|
|
15270
|
-
|
|
15271
|
-
const agents = listAgents();
|
|
15272
|
-
if (agents.length === 0)
|
|
15273
|
-
return text("None.");
|
|
15274
|
-
return text(agents.map((a) => `${a.id} | ${a.name} | ${a.role}`).join(`
|
|
15327
|
+
});
|
|
15328
|
+
registerTool("detect_enhancement", "Check if text needs AI enhancement.", { text: exports_external2.string() }, async (args) => {
|
|
15329
|
+
try {
|
|
15330
|
+
const r = needsEnhancement(args.text, config);
|
|
15331
|
+
return text(`${r.needs ? "Yes" : "No"}: ${r.reason}`);
|
|
15332
|
+
} catch (e) {
|
|
15333
|
+
return errorResult(e);
|
|
15334
|
+
}
|
|
15335
|
+
});
|
|
15336
|
+
registerTool("register_agent", "Register agent (idempotent).", { name: exports_external2.string(), description: exports_external2.string().optional(), role: exports_external2.string().optional() }, async (args) => {
|
|
15337
|
+
try {
|
|
15338
|
+
const a = registerAgent(args.name, args.description, args.role);
|
|
15339
|
+
return text(`${a.id} | ${a.name} | ${a.role}`);
|
|
15340
|
+
} catch (e) {
|
|
15341
|
+
return errorResult(e);
|
|
15342
|
+
}
|
|
15343
|
+
});
|
|
15344
|
+
registerTool("list_agents", "List registered agents.", {}, async () => {
|
|
15345
|
+
try {
|
|
15346
|
+
const agents = listAgents();
|
|
15347
|
+
if (agents.length === 0)
|
|
15348
|
+
return text("None.");
|
|
15349
|
+
return text(agents.map((a) => `${a.id} | ${a.name} | ${a.role}`).join(`
|
|
15275
15350
|
`));
|
|
15276
|
-
|
|
15277
|
-
|
|
15278
|
-
|
|
15279
|
-
});
|
|
15280
|
-
registerTool("get_agent", "Get agent by ID or name.", { id: exports_external2.string() }, async (args) => {
|
|
15281
|
-
|
|
15282
|
-
|
|
15283
|
-
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
|
-
|
|
15287
|
-
|
|
15288
|
-
|
|
15289
|
-
});
|
|
15290
|
-
registerTool("register_project", "Register project (idempotent).", { name: exports_external2.string(), path: exports_external2.string(), description: exports_external2.string().optional() }, async (args) => {
|
|
15291
|
-
|
|
15292
|
-
|
|
15293
|
-
|
|
15294
|
-
|
|
15295
|
-
|
|
15296
|
-
|
|
15297
|
-
});
|
|
15298
|
-
registerTool("list_projects", "List registered projects.", {}, async () => {
|
|
15299
|
-
|
|
15300
|
-
|
|
15301
|
-
|
|
15302
|
-
|
|
15303
|
-
|
|
15351
|
+
} catch (e) {
|
|
15352
|
+
return errorResult(e);
|
|
15353
|
+
}
|
|
15354
|
+
});
|
|
15355
|
+
registerTool("get_agent", "Get agent by ID or name.", { id: exports_external2.string() }, async (args) => {
|
|
15356
|
+
try {
|
|
15357
|
+
const a = getAgent(args.id);
|
|
15358
|
+
if (!a)
|
|
15359
|
+
return text(`Not found: ${args.id}`);
|
|
15360
|
+
return text(`${a.id} | ${a.name} | ${a.role} | ${a.last_seen_at}`);
|
|
15361
|
+
} catch (e) {
|
|
15362
|
+
return errorResult(e);
|
|
15363
|
+
}
|
|
15364
|
+
});
|
|
15365
|
+
registerTool("register_project", "Register project (idempotent).", { name: exports_external2.string(), path: exports_external2.string(), description: exports_external2.string().optional() }, async (args) => {
|
|
15366
|
+
try {
|
|
15367
|
+
const p = registerProject(args.name, args.path, args.description);
|
|
15368
|
+
return text(`${p.id.slice(0, 8)} | ${p.name} | ${p.path}`);
|
|
15369
|
+
} catch (e) {
|
|
15370
|
+
return errorResult(e);
|
|
15371
|
+
}
|
|
15372
|
+
});
|
|
15373
|
+
registerTool("list_projects", "List registered projects.", {}, async () => {
|
|
15374
|
+
try {
|
|
15375
|
+
const projects = listProjects();
|
|
15376
|
+
if (projects.length === 0)
|
|
15377
|
+
return text("None.");
|
|
15378
|
+
return text(projects.map((p) => `${p.id.slice(0, 8)} | ${p.name} | ${p.path}`).join(`
|
|
15304
15379
|
`));
|
|
15305
|
-
|
|
15306
|
-
|
|
15307
|
-
|
|
15308
|
-
});
|
|
15309
|
-
registerTool("heartbeat", "Update last_seen_at to signal agent is active. Call periodically during long tasks.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async (args) => {
|
|
15310
|
-
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
|
|
15318
|
-
});
|
|
15319
|
-
registerTool("set_focus", "Set active project context for this agent session.", { agent_id: exports_external2.string().describe("Agent ID or name"), project_id: exports_external2.string().nullable().optional().describe("Project ID to focus on, or null to clear") }, async (args) => {
|
|
15320
|
-
|
|
15321
|
-
|
|
15322
|
-
|
|
15323
|
-
|
|
15324
|
-
|
|
15325
|
-
|
|
15326
|
-
|
|
15380
|
+
} catch (e) {
|
|
15381
|
+
return errorResult(e);
|
|
15382
|
+
}
|
|
15383
|
+
});
|
|
15384
|
+
registerTool("heartbeat", "Update last_seen_at to signal agent is active. Call periodically during long tasks.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async (args) => {
|
|
15385
|
+
try {
|
|
15386
|
+
const agent = heartbeatAgent(args.agent_id);
|
|
15387
|
+
if (!agent)
|
|
15388
|
+
return text(`Agent not found: ${args.agent_id}`);
|
|
15389
|
+
return text(`${agent.id} | ${agent.name} | last_seen: ${agent.last_seen_at}`);
|
|
15390
|
+
} catch (e) {
|
|
15391
|
+
return errorResult(e);
|
|
15392
|
+
}
|
|
15393
|
+
});
|
|
15394
|
+
registerTool("set_focus", "Set active project context for this agent session.", { agent_id: exports_external2.string().describe("Agent ID or name"), project_id: exports_external2.string().nullable().optional().describe("Project ID to focus on, or null to clear") }, async (args) => {
|
|
15395
|
+
try {
|
|
15396
|
+
const agent = setAgentFocus(args.agent_id, args.project_id ?? null);
|
|
15397
|
+
if (!agent)
|
|
15398
|
+
return text(`Agent not found: ${args.agent_id}`);
|
|
15399
|
+
return text(args.project_id ? `Focus set: ${args.project_id}` : "Focus cleared");
|
|
15400
|
+
} catch (e) {
|
|
15401
|
+
return errorResult(e);
|
|
15402
|
+
}
|
|
15403
|
+
});
|
|
15404
|
+
registerTool("send_feedback", "Send feedback about this service", {
|
|
15405
|
+
message: exports_external2.string().describe("Feedback message"),
|
|
15406
|
+
email: exports_external2.string().optional().describe("Contact email (optional)"),
|
|
15407
|
+
category: exports_external2.enum(["bug", "feature", "general"]).optional().describe("Feedback category")
|
|
15408
|
+
}, async (params) => {
|
|
15409
|
+
const adapter = getAdapter();
|
|
15410
|
+
const pkg = require_package();
|
|
15411
|
+
adapter.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", params.message, params.email || null, params.category || "general", pkg.version);
|
|
15412
|
+
return text("Feedback saved. Thank you!");
|
|
15413
|
+
});
|
|
15414
|
+
registerCloudTools(server, "recordings");
|
|
15415
|
+
return server;
|
|
15416
|
+
}
|
|
15417
|
+
async function main() {
|
|
15418
|
+
const args = process.argv.slice(2);
|
|
15419
|
+
if (isHttpMode(args)) {
|
|
15420
|
+
startMcpHttpServer({ name: "recordings", port: resolveMcpHttpPort(args), buildServer });
|
|
15421
|
+
return;
|
|
15327
15422
|
}
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15333
|
-
}
|
|
15334
|
-
|
|
15335
|
-
|
|
15336
|
-
|
|
15337
|
-
return text("Feedback saved. Thank you!");
|
|
15338
|
-
});
|
|
15339
|
-
var transport = new StdioServerTransport;
|
|
15340
|
-
registerCloudTools(server, "recordings");
|
|
15341
|
-
await server.connect(transport);
|
|
15423
|
+
const transport = new StdioServerTransport;
|
|
15424
|
+
await buildServer().connect(transport);
|
|
15425
|
+
}
|
|
15426
|
+
if (import.meta.main) {
|
|
15427
|
+
await main();
|
|
15428
|
+
}
|
|
15429
|
+
export {
|
|
15430
|
+
buildServer
|
|
15431
|
+
};
|