@hasna/recordings 0.1.23 → 0.1.24
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 +16 -7
- package/dist/cli/index.js +114 -64
- package/dist/cli/storage.d.ts +3 -0
- package/dist/cli/storage.d.ts.map +1 -0
- package/dist/db/pg-migrations.d.ts +1 -1
- package/dist/db/storage-config.d.ts +27 -0
- package/dist/db/storage-config.d.ts.map +1 -0
- package/dist/db/storage-sync.d.ts +36 -0
- package/dist/db/storage-sync.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +110 -50
- package/dist/lib/enhancer.d.ts.map +1 -1
- package/dist/lib/transcriber.d.ts +1 -0
- package/dist/lib/transcriber.d.ts.map +1 -1
- package/dist/mcp/http.d.ts +13 -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 +519 -408
- package/dist/mcp/storage-tools.d.ts +3 -0
- package/dist/mcp/storage-tools.d.ts.map +1 -0
- package/dist/storage.d.ts +7 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +5656 -0
- package/dist/version.d.ts +1 -1
- package/package.json +8 -3
- package/scripts/install_macos_app.sh +17 -0
- package/src/native/Recordings/App/RecordingsApp.swift +3 -2
- package/src/native/Recordings/RecordingsLib/MenuBarPopover.swift +101 -80
- package/src/native/Recordings/RecordingsLib/OpenAIAPIKeyStore.swift +26 -0
- package/src/native/Recordings/RecordingsLib/RealtimeTranscriptionClient.swift +13 -1
- package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +92 -31
- package/src/native/Recordings/RecordingsLib/SettingsView.swift +5 -1
- package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +12 -0
- package/src/native/Recordings/RecordingsTests/OpenAIAPIKeyStoreTests.swift +49 -0
- package/src/native/Recordings/RecordingsTests/PasteTargetTests.swift +54 -0
- package/src/native/Recordings/RecordingsTests/TranscriptResolutionTests.swift +58 -0
- package/dist/cli/cloud.d.ts +0 -3
- package/dist/cli/cloud.d.ts.map +0 -1
- package/dist/db/cloud-config.d.ts +0 -14
- package/dist/db/cloud-config.d.ts.map +0 -1
- package/dist/db/cloud-sync.d.ts +0 -29
- package/dist/db/cloud-sync.d.ts.map +0 -1
- package/dist/mcp/cloud-tools.d.ts +0 -3
- package/dist/mcp/cloud-tools.d.ts.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -5098,7 +5098,7 @@ var init_pg_migrate = __esm(() => {
|
|
|
5098
5098
|
var require_package = __commonJS((exports, module) => {
|
|
5099
5099
|
module.exports = {
|
|
5100
5100
|
name: "@hasna/recordings",
|
|
5101
|
-
version: "0.1.
|
|
5101
|
+
version: "0.1.24",
|
|
5102
5102
|
type: "module",
|
|
5103
5103
|
description: "Speech-to-text recording tool with MCP and CLI \u2014 records, transcribes, and optionally enhances text using AI",
|
|
5104
5104
|
repository: {
|
|
@@ -5115,13 +5115,18 @@ var require_package = __commonJS((exports, module) => {
|
|
|
5115
5115
|
".": {
|
|
5116
5116
|
import: "./dist/index.js",
|
|
5117
5117
|
types: "./dist/index.d.ts"
|
|
5118
|
+
},
|
|
5119
|
+
"./storage": {
|
|
5120
|
+
import: "./dist/storage.js",
|
|
5121
|
+
types: "./dist/storage.d.ts"
|
|
5118
5122
|
}
|
|
5119
5123
|
},
|
|
5120
5124
|
scripts: {
|
|
5121
|
-
|
|
5125
|
+
clean: "rm -rf dist",
|
|
5126
|
+
build: "bun run clean && bun run build:cli && bun run build:mcp && bun run build:lib && tsc --emitDeclarationOnly --outDir dist",
|
|
5122
5127
|
"build:cli": "bun build src/cli/index.ts --target=bun --outfile=dist/cli/index.js --external=commander --external=chalk --external=openai",
|
|
5123
5128
|
"build:mcp": "bun build src/mcp/index.ts --target=bun --outfile=dist/mcp/index.js --external=@modelcontextprotocol/sdk --external=openai",
|
|
5124
|
-
"build:lib": "bun build src/index.ts --target=bun --
|
|
5129
|
+
"build:lib": "bun build src/index.ts src/storage.ts --target=bun --outdir=dist --external=openai",
|
|
5125
5130
|
typecheck: "tsc --noEmit",
|
|
5126
5131
|
test: "bun test",
|
|
5127
5132
|
"test:coverage": "bun test --coverage",
|
|
@@ -5889,7 +5894,7 @@ async function transcribeAudio(audioPath, config, options = {}) {
|
|
|
5889
5894
|
};
|
|
5890
5895
|
} catch (error) {
|
|
5891
5896
|
const msg = error instanceof Error ? error.message : String(error);
|
|
5892
|
-
throw new TranscriptionError(`Transcription failed: ${msg}`);
|
|
5897
|
+
throw new TranscriptionError(`Transcription failed: ${describeTranscriptionFailure(msg)}`);
|
|
5893
5898
|
}
|
|
5894
5899
|
}
|
|
5895
5900
|
async function transcribeAudioStream(audioPath, config, options = {}) {
|
|
@@ -5925,11 +5930,20 @@ async function transcribeAudioStream(audioPath, config, options = {}) {
|
|
|
5925
5930
|
};
|
|
5926
5931
|
} catch (error) {
|
|
5927
5932
|
const msg = error instanceof Error ? error.message : String(error);
|
|
5928
|
-
throw new TranscriptionError(`Streaming transcription failed: ${msg}`);
|
|
5933
|
+
throw new TranscriptionError(`Streaming transcription failed: ${describeTranscriptionFailure(msg)}`);
|
|
5929
5934
|
} finally {
|
|
5930
5935
|
fileStream.destroy();
|
|
5931
5936
|
}
|
|
5932
5937
|
}
|
|
5938
|
+
function describeTranscriptionFailure(message) {
|
|
5939
|
+
if (/401|incorrect api key|invalid_api_key/i.test(message)) {
|
|
5940
|
+
return "OpenAI API key invalid or expired (401). Update it in ~/.hasna/recordings/config.json, the OPENAI_API_KEY env var, or the Recordings app Settings.";
|
|
5941
|
+
}
|
|
5942
|
+
if (/429|exceeded your current quota|insufficient_quota/i.test(message)) {
|
|
5943
|
+
return "OpenAI quota exceeded (429). Check the OpenAI account plan and billing.";
|
|
5944
|
+
}
|
|
5945
|
+
return message;
|
|
5946
|
+
}
|
|
5933
5947
|
function buildVerbatimPrompt(context) {
|
|
5934
5948
|
const base = "Transcribe the speaker's words verbatim. Output only words that were spoken. Do not summarize, paraphrase, rewrite, clean up grammar, add explanations, or infer missing words. Preserve names, acronyms, technical terms, punctuation, and casing when audible.";
|
|
5935
5949
|
const trimmed = context?.trim();
|
|
@@ -6047,7 +6061,7 @@ ${systemPrompt}` : basePrompt;
|
|
|
6047
6061
|
};
|
|
6048
6062
|
} catch (error) {
|
|
6049
6063
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6050
|
-
throw new EnhancementError(`Enhancement failed: ${msg}`);
|
|
6064
|
+
throw new EnhancementError(`Enhancement failed: ${describeTranscriptionFailure(msg)}`);
|
|
6051
6065
|
}
|
|
6052
6066
|
}
|
|
6053
6067
|
async function processText(rawText, config, systemPrompt) {
|
|
@@ -6067,69 +6081,94 @@ async function processText(rawText, config, systemPrompt) {
|
|
|
6067
6081
|
}
|
|
6068
6082
|
|
|
6069
6083
|
// src/version.ts
|
|
6070
|
-
var VERSION = "0.1.
|
|
6084
|
+
var VERSION = "0.1.24";
|
|
6071
6085
|
|
|
6072
|
-
// src/cli/
|
|
6086
|
+
// src/cli/storage.ts
|
|
6073
6087
|
import chalk from "chalk";
|
|
6074
6088
|
|
|
6075
|
-
// src/db/
|
|
6089
|
+
// src/db/storage-config.ts
|
|
6076
6090
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
6077
6091
|
import { homedir as homedir2 } from "os";
|
|
6078
6092
|
import { join as join3 } from "path";
|
|
6079
|
-
var
|
|
6080
|
-
|
|
6081
|
-
|
|
6093
|
+
var STORAGE_CONFIG_PATH = join3(homedir2(), ".hasna", "recordings", "storage", "config.json");
|
|
6094
|
+
var RECORDINGS_STORAGE_ENV = "HASNA_RECORDINGS_DATABASE_URL";
|
|
6095
|
+
var RECORDINGS_STORAGE_FALLBACK_ENV = "RECORDINGS_DATABASE_URL";
|
|
6096
|
+
var RECORDINGS_STORAGE_MODE_ENV = "HASNA_RECORDINGS_STORAGE_MODE";
|
|
6097
|
+
var RECORDINGS_STORAGE_MODE_FALLBACK_ENV = "RECORDINGS_STORAGE_MODE";
|
|
6098
|
+
var STORAGE_DATABASE_ENV = [RECORDINGS_STORAGE_ENV, RECORDINGS_STORAGE_FALLBACK_ENV];
|
|
6099
|
+
function readEnv(name) {
|
|
6100
|
+
const value = process.env[name]?.trim();
|
|
6101
|
+
return value || undefined;
|
|
6102
|
+
}
|
|
6103
|
+
function normalizeMode(value) {
|
|
6104
|
+
const normalized = value?.trim().toLowerCase();
|
|
6105
|
+
if (normalized === "local" || normalized === "hybrid" || normalized === "remote")
|
|
6106
|
+
return normalized;
|
|
6107
|
+
return;
|
|
6108
|
+
}
|
|
6109
|
+
function getStorageDatabaseEnvName() {
|
|
6110
|
+
for (const name of STORAGE_DATABASE_ENV) {
|
|
6111
|
+
if (readEnv(name))
|
|
6112
|
+
return name;
|
|
6113
|
+
}
|
|
6114
|
+
return null;
|
|
6115
|
+
}
|
|
6116
|
+
function getStorageDatabaseEnv() {
|
|
6117
|
+
const name = getStorageDatabaseEnvName();
|
|
6118
|
+
return name ? { name } : null;
|
|
6082
6119
|
}
|
|
6083
|
-
function
|
|
6084
|
-
|
|
6120
|
+
function getStorageDatabaseUrl() {
|
|
6121
|
+
const env = getStorageDatabaseEnv();
|
|
6122
|
+
return env ? readEnv(env.name) : undefined;
|
|
6085
6123
|
}
|
|
6086
|
-
function
|
|
6124
|
+
function getStorageConfig() {
|
|
6087
6125
|
const config = {
|
|
6088
6126
|
mode: "local",
|
|
6089
6127
|
rds: {
|
|
6090
6128
|
host: "",
|
|
6091
6129
|
port: 5432,
|
|
6092
6130
|
username: "",
|
|
6093
|
-
password_env: "
|
|
6131
|
+
password_env: "RECORDINGS_DATABASE_PASSWORD",
|
|
6094
6132
|
ssl: true
|
|
6095
6133
|
}
|
|
6096
6134
|
};
|
|
6097
|
-
if (existsSync3(
|
|
6135
|
+
if (existsSync3(STORAGE_CONFIG_PATH)) {
|
|
6098
6136
|
try {
|
|
6099
|
-
const raw = JSON.parse(readFileSync2(
|
|
6100
|
-
config.mode = raw.mode ?? config.mode;
|
|
6137
|
+
const raw = JSON.parse(readFileSync2(STORAGE_CONFIG_PATH, "utf-8"));
|
|
6138
|
+
config.mode = normalizeMode(raw.mode) ?? config.mode;
|
|
6101
6139
|
config.rds = { ...config.rds, ...raw.rds ?? {} };
|
|
6102
6140
|
} catch {}
|
|
6103
6141
|
}
|
|
6104
|
-
const modeOverride =
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6142
|
+
const modeOverride = readEnv(RECORDINGS_STORAGE_MODE_ENV) ?? readEnv(RECORDINGS_STORAGE_MODE_FALLBACK_ENV);
|
|
6143
|
+
const normalizedMode = normalizeMode(modeOverride);
|
|
6144
|
+
if (normalizedMode) {
|
|
6145
|
+
config.mode = normalizedMode;
|
|
6146
|
+
} else if (getStorageDatabaseUrl() && config.mode === "local") {
|
|
6108
6147
|
config.mode = "hybrid";
|
|
6109
6148
|
}
|
|
6110
6149
|
return config;
|
|
6111
6150
|
}
|
|
6112
|
-
function
|
|
6113
|
-
const direct =
|
|
6151
|
+
function getStorageConnectionString(dbName = "recordings") {
|
|
6152
|
+
const direct = getStorageDatabaseUrl();
|
|
6114
6153
|
if (direct)
|
|
6115
6154
|
return direct;
|
|
6116
|
-
const config =
|
|
6155
|
+
const config = getStorageConfig();
|
|
6117
6156
|
const { host, port, username, password_env, ssl } = config.rds;
|
|
6118
6157
|
if (!host || !username) {
|
|
6119
|
-
throw new Error("
|
|
6158
|
+
throw new Error("Storage database is not configured. Set HASNA_RECORDINGS_DATABASE_URL or configure ~/.hasna/recordings/storage/config.json.");
|
|
6120
6159
|
}
|
|
6121
6160
|
const password = process.env[password_env];
|
|
6122
6161
|
if (!password) {
|
|
6123
|
-
throw new Error(`
|
|
6162
|
+
throw new Error(`Storage database password is not set. Export ${password_env}.`);
|
|
6124
6163
|
}
|
|
6125
6164
|
const sslParam = ssl ? "?sslmode=require" : "";
|
|
6126
6165
|
return `postgres://${username}:${encodeURIComponent(password)}@${host}:${port}/${dbName}${sslParam}`;
|
|
6127
6166
|
}
|
|
6128
6167
|
|
|
6129
|
-
// src/db/
|
|
6168
|
+
// src/db/storage-sync.ts
|
|
6130
6169
|
init_remote_storage();
|
|
6131
6170
|
init_pg_migrations();
|
|
6132
|
-
var
|
|
6171
|
+
var STORAGE_TABLES = [
|
|
6133
6172
|
"projects",
|
|
6134
6173
|
"agents",
|
|
6135
6174
|
"recordings",
|
|
@@ -6188,21 +6227,26 @@ function upsertSqlite(db, table, rows) {
|
|
|
6188
6227
|
}
|
|
6189
6228
|
return written;
|
|
6190
6229
|
}
|
|
6191
|
-
async function
|
|
6192
|
-
return new PgAdapterAsync(
|
|
6230
|
+
async function getStoragePg() {
|
|
6231
|
+
return new PgAdapterAsync(getStorageConnectionString("recordings"));
|
|
6193
6232
|
}
|
|
6194
|
-
async function
|
|
6233
|
+
async function runStorageMigrations(remote) {
|
|
6195
6234
|
for (const migration of PG_MIGRATIONS) {
|
|
6196
6235
|
await remote.exec(migration);
|
|
6197
6236
|
}
|
|
6198
6237
|
}
|
|
6199
|
-
function
|
|
6200
|
-
const config =
|
|
6238
|
+
function getStorageStatus(db = getDatabase()) {
|
|
6239
|
+
const config = getStorageConfig();
|
|
6240
|
+
const activeEnv = getStorageDatabaseEnv();
|
|
6201
6241
|
return {
|
|
6242
|
+
configured: Boolean(activeEnv),
|
|
6202
6243
|
mode: config.mode,
|
|
6203
|
-
enabled: config.mode === "hybrid" || config.mode === "
|
|
6244
|
+
enabled: config.mode === "hybrid" || config.mode === "remote",
|
|
6245
|
+
env: STORAGE_DATABASE_ENV,
|
|
6246
|
+
activeEnv: activeEnv?.name ?? null,
|
|
6247
|
+
service: "recordings",
|
|
6204
6248
|
db_path: getDbPath(),
|
|
6205
|
-
tables:
|
|
6249
|
+
tables: STORAGE_TABLES.map((table) => {
|
|
6206
6250
|
try {
|
|
6207
6251
|
const row = db.query(`SELECT COUNT(*) as count FROM ${quoteId(table)}`).get();
|
|
6208
6252
|
return { table, rows: row.count };
|
|
@@ -6212,12 +6256,12 @@ function getCloudStatus(db = getDatabase()) {
|
|
|
6212
6256
|
})
|
|
6213
6257
|
};
|
|
6214
6258
|
}
|
|
6215
|
-
async function
|
|
6259
|
+
async function pushStorageChanges(tables = [...STORAGE_TABLES]) {
|
|
6216
6260
|
const db = getDatabase();
|
|
6217
|
-
const remote = await
|
|
6261
|
+
const remote = await getStoragePg();
|
|
6218
6262
|
const results = [];
|
|
6219
6263
|
try {
|
|
6220
|
-
await
|
|
6264
|
+
await runStorageMigrations(remote);
|
|
6221
6265
|
for (const table of tables) {
|
|
6222
6266
|
const result = { table, direction: "push", rows_read: 0, rows_written: 0, errors: [] };
|
|
6223
6267
|
try {
|
|
@@ -6234,12 +6278,12 @@ async function pushCloudChanges(tables = [...CLOUD_TABLES]) {
|
|
|
6234
6278
|
}
|
|
6235
6279
|
return results;
|
|
6236
6280
|
}
|
|
6237
|
-
async function
|
|
6281
|
+
async function pullStorageChanges(tables = [...STORAGE_TABLES]) {
|
|
6238
6282
|
const db = getDatabase();
|
|
6239
|
-
const remote = await
|
|
6283
|
+
const remote = await getStoragePg();
|
|
6240
6284
|
const results = [];
|
|
6241
6285
|
try {
|
|
6242
|
-
await
|
|
6286
|
+
await runStorageMigrations(remote);
|
|
6243
6287
|
for (const table of tables) {
|
|
6244
6288
|
const result = { table, direction: "pull", rows_read: 0, rows_written: 0, errors: [] };
|
|
6245
6289
|
try {
|
|
@@ -6256,20 +6300,26 @@ async function pullCloudChanges(tables = [...CLOUD_TABLES]) {
|
|
|
6256
6300
|
}
|
|
6257
6301
|
return results;
|
|
6258
6302
|
}
|
|
6259
|
-
async function
|
|
6303
|
+
async function syncStorageChanges(tables = [...STORAGE_TABLES]) {
|
|
6260
6304
|
return {
|
|
6261
|
-
push: await
|
|
6262
|
-
pull: await
|
|
6305
|
+
push: await pushStorageChanges(tables),
|
|
6306
|
+
pull: await pullStorageChanges(tables)
|
|
6263
6307
|
};
|
|
6264
6308
|
}
|
|
6265
|
-
function
|
|
6309
|
+
function parseStorageTables(raw) {
|
|
6266
6310
|
if (!raw)
|
|
6267
|
-
return [...
|
|
6311
|
+
return [...STORAGE_TABLES];
|
|
6268
6312
|
const requested = raw.split(",").map((table) => table.trim()).filter(Boolean);
|
|
6269
|
-
|
|
6313
|
+
if (requested.length === 0)
|
|
6314
|
+
return [...STORAGE_TABLES];
|
|
6315
|
+
const allowed = new Set(STORAGE_TABLES);
|
|
6316
|
+
const invalid = requested.filter((table) => !allowed.has(table));
|
|
6317
|
+
if (invalid.length > 0)
|
|
6318
|
+
throw new Error(`Unknown recordings sync table(s): ${invalid.join(", ")}`);
|
|
6319
|
+
return requested;
|
|
6270
6320
|
}
|
|
6271
6321
|
|
|
6272
|
-
// src/cli/
|
|
6322
|
+
// src/cli/storage.ts
|
|
6273
6323
|
function shouldJson(program, opts) {
|
|
6274
6324
|
return Boolean(opts.json || program.opts().json);
|
|
6275
6325
|
}
|
|
@@ -6285,10 +6335,10 @@ function printResults(results) {
|
|
|
6285
6335
|
}
|
|
6286
6336
|
}
|
|
6287
6337
|
}
|
|
6288
|
-
function
|
|
6289
|
-
const
|
|
6290
|
-
|
|
6291
|
-
const status =
|
|
6338
|
+
function registerStorageCommands(program) {
|
|
6339
|
+
const storage = program.command("storage").description("Manage recordings local/remote storage sync");
|
|
6340
|
+
storage.command("status").description("Show local database and storage sync status").option("--json", "Output JSON").action((opts) => {
|
|
6341
|
+
const status = getStorageStatus();
|
|
6292
6342
|
if (shouldJson(program, opts)) {
|
|
6293
6343
|
printJson(status);
|
|
6294
6344
|
return;
|
|
@@ -6300,9 +6350,9 @@ function registerCloudCommands(program) {
|
|
|
6300
6350
|
console.log(` ${table.table}: ${table.rows}`);
|
|
6301
6351
|
}
|
|
6302
6352
|
});
|
|
6303
|
-
|
|
6353
|
+
storage.command("push").description("Push local recordings data to PostgreSQL").option("--tables <tables>", "Comma-separated table names").option("--json", "Output JSON").action(async (opts) => {
|
|
6304
6354
|
try {
|
|
6305
|
-
const results = await
|
|
6355
|
+
const results = await pushStorageChanges(parseStorageTables(opts.tables));
|
|
6306
6356
|
if (shouldJson(program, opts))
|
|
6307
6357
|
printJson(results);
|
|
6308
6358
|
else
|
|
@@ -6315,9 +6365,9 @@ function registerCloudCommands(program) {
|
|
|
6315
6365
|
process.exitCode = 1;
|
|
6316
6366
|
}
|
|
6317
6367
|
});
|
|
6318
|
-
|
|
6368
|
+
storage.command("pull").description("Pull PostgreSQL recordings data into the local database").option("--tables <tables>", "Comma-separated table names").option("--json", "Output JSON").action(async (opts) => {
|
|
6319
6369
|
try {
|
|
6320
|
-
const results = await
|
|
6370
|
+
const results = await pullStorageChanges(parseStorageTables(opts.tables));
|
|
6321
6371
|
if (shouldJson(program, opts))
|
|
6322
6372
|
printJson(results);
|
|
6323
6373
|
else
|
|
@@ -6330,9 +6380,9 @@ function registerCloudCommands(program) {
|
|
|
6330
6380
|
process.exitCode = 1;
|
|
6331
6381
|
}
|
|
6332
6382
|
});
|
|
6333
|
-
|
|
6383
|
+
storage.command("sync").description("Push local changes, then pull remote changes").option("--tables <tables>", "Comma-separated table names").option("--json", "Output JSON").action(async (opts) => {
|
|
6334
6384
|
try {
|
|
6335
|
-
const result = await
|
|
6385
|
+
const result = await syncStorageChanges(parseStorageTables(opts.tables));
|
|
6336
6386
|
if (shouldJson(program, opts)) {
|
|
6337
6387
|
printJson(result);
|
|
6338
6388
|
return;
|
|
@@ -6349,10 +6399,10 @@ function registerCloudCommands(program) {
|
|
|
6349
6399
|
process.exitCode = 1;
|
|
6350
6400
|
}
|
|
6351
6401
|
});
|
|
6352
|
-
|
|
6402
|
+
storage.command("migrate").description("Apply PostgreSQL migrations").option("--connection-string <url>", "PostgreSQL connection string").option("--json", "Output JSON").action(async (opts) => {
|
|
6353
6403
|
try {
|
|
6354
6404
|
const { applyPgMigrations: applyPgMigrations2 } = await Promise.resolve().then(() => (init_pg_migrate(), exports_pg_migrate));
|
|
6355
|
-
const result = await applyPgMigrations2(opts.connectionString ||
|
|
6405
|
+
const result = await applyPgMigrations2(opts.connectionString || getStorageConnectionString("recordings"));
|
|
6356
6406
|
if (shouldJson(program, opts)) {
|
|
6357
6407
|
printJson(result);
|
|
6358
6408
|
return;
|
|
@@ -6374,7 +6424,7 @@ function registerCloudCommands(program) {
|
|
|
6374
6424
|
process.exitCode = 1;
|
|
6375
6425
|
}
|
|
6376
6426
|
});
|
|
6377
|
-
|
|
6427
|
+
storage.command("feedback <message>").description("Save feedback locally").option("--email <email>").option("--category <category>", "Feedback category", "general").option("--json", "Output JSON").action((message, opts) => {
|
|
6378
6428
|
try {
|
|
6379
6429
|
const adapter = getAdapter();
|
|
6380
6430
|
adapter.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", message, opts.email || null, opts.category || "general", "recordings");
|
|
@@ -6395,7 +6445,7 @@ function registerCloudCommands(program) {
|
|
|
6395
6445
|
// src/cli/index.ts
|
|
6396
6446
|
var program = new Command;
|
|
6397
6447
|
program.name("recordings").description("Speech-to-text recording tool \u2014 record, transcribe, and enhance with AI").version(VERSION).option("--json", "Output as JSON").option("--agent <name>", "Agent name or ID").option("--project <name>", "Project name or ID").option("--session <id>", "Session ID");
|
|
6398
|
-
|
|
6448
|
+
registerStorageCommands(program);
|
|
6399
6449
|
program.command("record").description("Record from microphone, transcribe, and optionally enhance").option("-d, --duration <seconds>", "Record for specific duration").option("--no-enhance", "Skip AI enhancement").option("-t, --tags <tags>", "Comma-separated tags").option("-l, --language <lang>", "Language code (e.g. en, es, fr)").action(async (opts) => {
|
|
6400
6450
|
const config = loadConfig();
|
|
6401
6451
|
ensureDataDir(config);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/cli/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8BzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkI9D"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type StorageMode = "local" | "hybrid" | "remote";
|
|
2
|
+
export interface StorageConfig {
|
|
3
|
+
mode: StorageMode;
|
|
4
|
+
rds: {
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
username: string;
|
|
8
|
+
password_env: string;
|
|
9
|
+
ssl: boolean;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface StorageEnv {
|
|
13
|
+
name: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const RECORDINGS_STORAGE_ENV = "HASNA_RECORDINGS_DATABASE_URL";
|
|
16
|
+
export declare const RECORDINGS_STORAGE_FALLBACK_ENV = "RECORDINGS_DATABASE_URL";
|
|
17
|
+
export declare const RECORDINGS_STORAGE_MODE_ENV = "HASNA_RECORDINGS_STORAGE_MODE";
|
|
18
|
+
export declare const RECORDINGS_STORAGE_MODE_FALLBACK_ENV = "RECORDINGS_STORAGE_MODE";
|
|
19
|
+
export declare const STORAGE_DATABASE_ENV: readonly ["HASNA_RECORDINGS_DATABASE_URL", "RECORDINGS_DATABASE_URL"];
|
|
20
|
+
export declare const STORAGE_MODE_ENV: readonly ["HASNA_RECORDINGS_STORAGE_MODE", "RECORDINGS_STORAGE_MODE"];
|
|
21
|
+
export declare function getStorageDatabaseEnvName(): (typeof STORAGE_DATABASE_ENV)[number] | null;
|
|
22
|
+
export declare function getStorageDatabaseEnv(): StorageEnv | null;
|
|
23
|
+
export declare function getStorageDatabaseUrl(): string | undefined;
|
|
24
|
+
export declare function getStorageConfig(): StorageConfig;
|
|
25
|
+
export declare function getStorageConnectionString(dbName?: string): string;
|
|
26
|
+
export declare const getConnectionString: typeof getStorageConnectionString;
|
|
27
|
+
//# sourceMappingURL=storage-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-config.d.ts","sourceRoot":"","sources":["../../src/db/storage-config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE;QACH,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,GAAG,EAAE,OAAO,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAGD,eAAO,MAAM,sBAAsB,kCAAkC,CAAC;AACtE,eAAO,MAAM,+BAA+B,4BAA4B,CAAC;AACzE,eAAO,MAAM,2BAA2B,kCAAkC,CAAC;AAC3E,eAAO,MAAM,oCAAoC,4BAA4B,CAAC;AAC9E,eAAO,MAAM,oBAAoB,uEAAqE,CAAC;AACvG,eAAO,MAAM,gBAAgB,uEAA+E,CAAC;AAa7G,wBAAgB,yBAAyB,IAAI,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAKxF;AAED,wBAAgB,qBAAqB,IAAI,UAAU,GAAG,IAAI,CAGzD;AAED,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,SAAS,CAG1D;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CA+BhD;AAED,wBAAgB,0BAA0B,CAAC,MAAM,SAAe,GAAG,MAAM,CAiBxE;AAED,eAAO,MAAM,mBAAmB,mCAA6B,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Database } from "bun:sqlite";
|
|
2
|
+
import { STORAGE_DATABASE_ENV, type StorageMode } from "./storage-config.js";
|
|
3
|
+
import { PgAdapterAsync } from "./remote-storage.js";
|
|
4
|
+
export interface SyncResult {
|
|
5
|
+
table: string;
|
|
6
|
+
direction: "push" | "pull";
|
|
7
|
+
rows_read: number;
|
|
8
|
+
rows_written: number;
|
|
9
|
+
errors: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface StorageStatus {
|
|
12
|
+
configured: boolean;
|
|
13
|
+
mode: StorageMode;
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
env: typeof STORAGE_DATABASE_ENV;
|
|
16
|
+
activeEnv: string | null;
|
|
17
|
+
service: "recordings";
|
|
18
|
+
db_path: string;
|
|
19
|
+
tables: Array<{
|
|
20
|
+
table: string;
|
|
21
|
+
rows: number;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export declare const STORAGE_TABLES: readonly ["projects", "agents", "recordings", "recording_tags", "feedback"];
|
|
25
|
+
export declare const RECORDINGS_STORAGE_TABLES: readonly ["projects", "agents", "recordings", "recording_tags", "feedback"];
|
|
26
|
+
export declare function getStoragePg(): Promise<PgAdapterAsync>;
|
|
27
|
+
export declare function runStorageMigrations(remote: PgAdapterAsync): Promise<void>;
|
|
28
|
+
export declare function getStorageStatus(db?: Database): StorageStatus;
|
|
29
|
+
export declare function pushStorageChanges(tables?: string[]): Promise<SyncResult[]>;
|
|
30
|
+
export declare function pullStorageChanges(tables?: string[]): Promise<SyncResult[]>;
|
|
31
|
+
export declare function syncStorageChanges(tables?: string[]): Promise<{
|
|
32
|
+
push: SyncResult[];
|
|
33
|
+
pull: SyncResult[];
|
|
34
|
+
}>;
|
|
35
|
+
export declare function parseStorageTables(raw?: string): string[];
|
|
36
|
+
//# sourceMappingURL=storage-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-sync.d.ts","sourceRoot":"","sources":["../../src/db/storage-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EACL,oBAAoB,EAIpB,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKrD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,oBAAoB,CAAC;IACjC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,eAAO,MAAM,cAAc,6EAMjB,CAAC;AAEX,eAAO,MAAM,yBAAyB,6EAAiB,CAAC;AA6ExD,wBAAsB,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC,CAE5D;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,GAAE,QAAwB,GAAG,aAAa,CAoB5E;AAED,wBAAsB,kBAAkB,CAAC,MAAM,GAAE,MAAM,EAAwB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAuBtG;AAED,wBAAsB,kBAAkB,CAAC,MAAM,GAAE,MAAM,EAAwB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAuBtG;AAED,wBAAsB,kBAAkB,CAAC,MAAM,GAAE,MAAM,EAAwB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAAC,IAAI,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CAKpI;AAED,wBAAgB,kBAAkB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAQzD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export type { Recording, CreateRecordingInput, RecordingFilter, ProcessingMode,
|
|
|
2
2
|
export { RecordingNotFoundError, RecordingError, TranscriptionError, EnhancementError, } from "./types/index.js";
|
|
3
3
|
export { getDatabase, closeDatabase, resetDatabase, getDbPath, shortUuid, } from "./db/database.js";
|
|
4
4
|
export { PgAdapterAsync } from "./db/remote-storage.js";
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
5
|
+
export { RECORDINGS_STORAGE_ENV, RECORDINGS_STORAGE_FALLBACK_ENV, RECORDINGS_STORAGE_MODE_ENV, RECORDINGS_STORAGE_MODE_FALLBACK_ENV, STORAGE_DATABASE_ENV, STORAGE_MODE_ENV, getStorageConfig, getStorageConnectionString, getConnectionString, getStorageDatabaseEnv, getStorageDatabaseEnvName, getStorageDatabaseUrl, type StorageConfig, type StorageEnv, type StorageMode, } from "./db/storage-config.js";
|
|
6
|
+
export { RECORDINGS_STORAGE_TABLES, STORAGE_TABLES, getStorageStatus, pushStorageChanges, pullStorageChanges, syncStorageChanges, parseStorageTables, type StorageStatus, type SyncResult, } from "./db/storage-sync.js";
|
|
7
7
|
export { applyPgMigrations, type PgMigrationResult } from "./db/pg-migrate.js";
|
|
8
8
|
export { createRecording, getRecording, listRecordings, deleteRecording, searchRecordings, getRecordingStats, } from "./db/recordings.js";
|
|
9
9
|
export { registerAgent, getAgent, listAgents } from "./db/agents.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,KAAK,EACL,OAAO,EACP,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,SAAS,EACT,SAAS,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,KAAK,EACL,OAAO,EACP,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,SAAS,EACT,SAAS,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,+BAA+B,EAC/B,2BAA2B,EAC3B,oCAAoC,EACpC,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG/E,OAAO,EACL,eAAe,EACf,YAAY,EACZ,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGrE,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,cAAc,GACf,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,cAAc,EACd,aAAa,EACb,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,cAAc,GACf,MAAM,mBAAmB,CAAC"}
|