recappi 0.1.74 → 0.1.75
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/dist/index.js +591 -52
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3001,6 +3001,9 @@ async function runDashboard(deps) {
|
|
|
3001
3001
|
transcribeRecordingArtifact: deps.transcribeRecordingArtifact,
|
|
3002
3002
|
onRetranscribe: deps.retranscribeRecording,
|
|
3003
3003
|
onResummarize: deps.resummarizeRecording,
|
|
3004
|
+
onSyncRecordingText: deps.syncRecordingText,
|
|
3005
|
+
onSyncRecordingAudio: deps.syncRecordingAudio,
|
|
3006
|
+
onExportRecording: deps.exportRecording,
|
|
3004
3007
|
initialView: deps.initialView ?? "overview",
|
|
3005
3008
|
openUrl,
|
|
3006
3009
|
copyText
|
|
@@ -3028,7 +3031,7 @@ var init_tui = __esm({
|
|
|
3028
3031
|
|
|
3029
3032
|
// src/cli.ts
|
|
3030
3033
|
import { Command, CommanderError, InvalidArgumentError } from "commander/esm.mjs";
|
|
3031
|
-
import
|
|
3034
|
+
import os6 from "os";
|
|
3032
3035
|
|
|
3033
3036
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
3034
3037
|
var external_exports = {};
|
|
@@ -3796,10 +3799,10 @@ function mergeDefs(...defs) {
|
|
|
3796
3799
|
function cloneDef(schema) {
|
|
3797
3800
|
return mergeDefs(schema._zod.def);
|
|
3798
3801
|
}
|
|
3799
|
-
function getElementAtPath(obj,
|
|
3800
|
-
if (!
|
|
3802
|
+
function getElementAtPath(obj, path7) {
|
|
3803
|
+
if (!path7)
|
|
3801
3804
|
return obj;
|
|
3802
|
-
return
|
|
3805
|
+
return path7.reduce((acc, key) => acc?.[key], obj);
|
|
3803
3806
|
}
|
|
3804
3807
|
function promiseAllObject(promisesObj) {
|
|
3805
3808
|
const keys = Object.keys(promisesObj);
|
|
@@ -4208,11 +4211,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
4208
4211
|
}
|
|
4209
4212
|
return false;
|
|
4210
4213
|
}
|
|
4211
|
-
function prefixIssues(
|
|
4214
|
+
function prefixIssues(path7, issues) {
|
|
4212
4215
|
return issues.map((iss) => {
|
|
4213
4216
|
var _a3;
|
|
4214
4217
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
4215
|
-
iss.path.unshift(
|
|
4218
|
+
iss.path.unshift(path7);
|
|
4216
4219
|
return iss;
|
|
4217
4220
|
});
|
|
4218
4221
|
}
|
|
@@ -4359,16 +4362,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
4359
4362
|
}
|
|
4360
4363
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
4361
4364
|
const fieldErrors = { _errors: [] };
|
|
4362
|
-
const processError = (error52,
|
|
4365
|
+
const processError = (error52, path7 = []) => {
|
|
4363
4366
|
for (const issue2 of error52.issues) {
|
|
4364
4367
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
4365
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4368
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
4366
4369
|
} else if (issue2.code === "invalid_key") {
|
|
4367
|
-
processError({ issues: issue2.issues }, [...
|
|
4370
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
4368
4371
|
} else if (issue2.code === "invalid_element") {
|
|
4369
|
-
processError({ issues: issue2.issues }, [...
|
|
4372
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
4370
4373
|
} else {
|
|
4371
|
-
const fullpath = [...
|
|
4374
|
+
const fullpath = [...path7, ...issue2.path];
|
|
4372
4375
|
if (fullpath.length === 0) {
|
|
4373
4376
|
fieldErrors._errors.push(mapper(issue2));
|
|
4374
4377
|
} else {
|
|
@@ -4395,17 +4398,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
4395
4398
|
}
|
|
4396
4399
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
4397
4400
|
const result = { errors: [] };
|
|
4398
|
-
const processError = (error52,
|
|
4401
|
+
const processError = (error52, path7 = []) => {
|
|
4399
4402
|
var _a3, _b;
|
|
4400
4403
|
for (const issue2 of error52.issues) {
|
|
4401
4404
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
4402
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4405
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
4403
4406
|
} else if (issue2.code === "invalid_key") {
|
|
4404
|
-
processError({ issues: issue2.issues }, [...
|
|
4407
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
4405
4408
|
} else if (issue2.code === "invalid_element") {
|
|
4406
|
-
processError({ issues: issue2.issues }, [...
|
|
4409
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
4407
4410
|
} else {
|
|
4408
|
-
const fullpath = [...
|
|
4411
|
+
const fullpath = [...path7, ...issue2.path];
|
|
4409
4412
|
if (fullpath.length === 0) {
|
|
4410
4413
|
result.errors.push(mapper(issue2));
|
|
4411
4414
|
continue;
|
|
@@ -4437,8 +4440,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
4437
4440
|
}
|
|
4438
4441
|
function toDotPath(_path) {
|
|
4439
4442
|
const segs = [];
|
|
4440
|
-
const
|
|
4441
|
-
for (const seg of
|
|
4443
|
+
const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
4444
|
+
for (const seg of path7) {
|
|
4442
4445
|
if (typeof seg === "number")
|
|
4443
4446
|
segs.push(`[${seg}]`);
|
|
4444
4447
|
else if (typeof seg === "symbol")
|
|
@@ -17130,13 +17133,13 @@ function resolveRef(ref, ctx) {
|
|
|
17130
17133
|
if (!ref.startsWith("#")) {
|
|
17131
17134
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
17132
17135
|
}
|
|
17133
|
-
const
|
|
17134
|
-
if (
|
|
17136
|
+
const path7 = ref.slice(1).split("/").filter(Boolean);
|
|
17137
|
+
if (path7.length === 0) {
|
|
17135
17138
|
return ctx.rootSchema;
|
|
17136
17139
|
}
|
|
17137
17140
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
17138
|
-
if (
|
|
17139
|
-
const key =
|
|
17141
|
+
if (path7[0] === defsKey) {
|
|
17142
|
+
const key = path7[1];
|
|
17140
17143
|
if (!key || !ctx.defs[key]) {
|
|
17141
17144
|
throw new Error(`Reference not found: ${ref}`);
|
|
17142
17145
|
}
|
|
@@ -18139,6 +18142,31 @@ var transcriptDataSchema = external_exports.object({
|
|
|
18139
18142
|
segments: external_exports.array(transcriptSegmentSchema),
|
|
18140
18143
|
summary: transcriptSummarySchema
|
|
18141
18144
|
});
|
|
18145
|
+
var recordingExportDataSchema = external_exports.object({
|
|
18146
|
+
origin: external_exports.string(),
|
|
18147
|
+
recordingId: external_exports.string(),
|
|
18148
|
+
exportDir: external_exports.string(),
|
|
18149
|
+
textPath: external_exports.string(),
|
|
18150
|
+
manifestPath: external_exports.string(),
|
|
18151
|
+
remoteManifestPath: external_exports.string(),
|
|
18152
|
+
sessionMetadataPath: external_exports.string(),
|
|
18153
|
+
recordingJsonPath: external_exports.string(),
|
|
18154
|
+
subscriptionPath: external_exports.string(),
|
|
18155
|
+
subscriptionJsonPath: external_exports.string(),
|
|
18156
|
+
audioPath: external_exports.string(),
|
|
18157
|
+
transcriptId: external_exports.string().nullable().optional(),
|
|
18158
|
+
transcriptPath: external_exports.string().optional(),
|
|
18159
|
+
transcriptJsonPath: external_exports.string().optional(),
|
|
18160
|
+
summaryPath: external_exports.string().optional(),
|
|
18161
|
+
summaryJsonPath: external_exports.string().optional(),
|
|
18162
|
+
actionItemsPath: external_exports.string().optional(),
|
|
18163
|
+
summaryStatus: summaryStatusSchema.optional(),
|
|
18164
|
+
audio: external_exports.object({
|
|
18165
|
+
contentType: external_exports.string().optional(),
|
|
18166
|
+
contentLength: external_exports.number().int().nonnegative().optional(),
|
|
18167
|
+
reused: external_exports.boolean().optional()
|
|
18168
|
+
}).optional()
|
|
18169
|
+
});
|
|
18142
18170
|
var doctorCheckStatusSchema = external_exports.enum(["ok", "warn", "error"]);
|
|
18143
18171
|
var doctorCheckSchema = external_exports.object({
|
|
18144
18172
|
name: external_exports.string(),
|
|
@@ -19401,7 +19429,10 @@ var RecappiApiClient = class {
|
|
|
19401
19429
|
const contentLength = numberHeader(response.headers.get("content-length"));
|
|
19402
19430
|
const dir = opts.directory ?? await fs3.mkdtemp(path4.join(os4.tmpdir(), "recappi-cli-audio-"));
|
|
19403
19431
|
if (opts.directory) await fs3.mkdir(dir, { recursive: true });
|
|
19404
|
-
const filePath = path4.join(
|
|
19432
|
+
const filePath = path4.join(
|
|
19433
|
+
dir,
|
|
19434
|
+
recordingAudioFileName(recordingId, opts.title, contentType, opts.filenameStem)
|
|
19435
|
+
);
|
|
19405
19436
|
try {
|
|
19406
19437
|
await pipeline(
|
|
19407
19438
|
Readable.fromWeb(response.body),
|
|
@@ -19700,12 +19731,12 @@ var RecappiApiClient = class {
|
|
|
19700
19731
|
} : {}
|
|
19701
19732
|
};
|
|
19702
19733
|
}
|
|
19703
|
-
async getJson(
|
|
19704
|
-
const response = await this.request("GET",
|
|
19734
|
+
async getJson(path7) {
|
|
19735
|
+
const response = await this.request("GET", path7);
|
|
19705
19736
|
return await parseJson(response);
|
|
19706
19737
|
}
|
|
19707
|
-
async postJson(
|
|
19708
|
-
const response = await this.request("POST",
|
|
19738
|
+
async postJson(path7, body) {
|
|
19739
|
+
const response = await this.request("POST", path7, JSON.stringify(body), {
|
|
19709
19740
|
headers: { "content-type": "application/json" }
|
|
19710
19741
|
});
|
|
19711
19742
|
return await parseJson(response);
|
|
@@ -19799,7 +19830,10 @@ function audioExtensionForContentType(contentType) {
|
|
|
19799
19830
|
return "wav";
|
|
19800
19831
|
}
|
|
19801
19832
|
}
|
|
19802
|
-
function recordingAudioFileName(recordingId, title, contentType) {
|
|
19833
|
+
function recordingAudioFileName(recordingId, title, contentType, filenameStem) {
|
|
19834
|
+
if (filenameStem && filenameStem.trim()) {
|
|
19835
|
+
return `${truncateFileStem(safeFileStem(filenameStem), 96)}.${audioExtensionForContentType(contentType)}`;
|
|
19836
|
+
}
|
|
19803
19837
|
const idStem = truncateFileStem(safeFileStem(recordingId), 48);
|
|
19804
19838
|
const titleStem = title ? truncateFileStem(safeFileStem(title), 80) : "";
|
|
19805
19839
|
const stem = titleStem ? `${titleStem}-${idStem}` : idStem;
|
|
@@ -20093,7 +20127,7 @@ import { promises as fs4 } from "fs";
|
|
|
20093
20127
|
import path5 from "path";
|
|
20094
20128
|
function createRecordingAudioRuntime(client, deps = {}) {
|
|
20095
20129
|
const downloadRecordingAudioFile = async (recordingId, opts) => {
|
|
20096
|
-
const cached2 = await findReusableDownload(recordingId, deps);
|
|
20130
|
+
const cached2 = opts?.directory ? null : await findReusableDownload(recordingId, deps);
|
|
20097
20131
|
if (cached2) return cached2;
|
|
20098
20132
|
const directory = opts?.directory ?? (deps.account ? defaultDownloadDirectory(deps) : void 0);
|
|
20099
20133
|
const download = await client.downloadRecordingAudio(recordingId, {
|
|
@@ -20249,6 +20283,7 @@ var COMMON_TASKS = [
|
|
|
20249
20283
|
{ label: "Transcribe a local file", command: "recappi upload <file> --transcribe --wait" },
|
|
20250
20284
|
{ label: "Re-transcribe a recording", command: "recappi recordings retranscribe <recordingId> --wait" },
|
|
20251
20285
|
{ label: "Re-summarize a recording", command: "recappi recordings resummarize <recordingId>" },
|
|
20286
|
+
{ label: "Export recording bundle", command: "recappi recordings export <recordingId> --dir ./bundle" },
|
|
20252
20287
|
{ label: "List / find recordings", command: "recappi recordings list" },
|
|
20253
20288
|
{ label: "Read a transcript", command: "recappi transcript get <transcriptId>" },
|
|
20254
20289
|
{ label: "Download / open audio", command: "recappi audio <recordingId> --open" },
|
|
@@ -20355,7 +20390,24 @@ var COMMAND_METADATA = {
|
|
|
20355
20390
|
"recordings get": {
|
|
20356
20391
|
capabilities: ["Fetch one recording's metadata and status by id"],
|
|
20357
20392
|
examples: [{ description: "Fetch one recording", command: "recappi recordings get <recordingId>" }],
|
|
20358
|
-
relatedCommands: ["recordings list", "transcript get", "audio"]
|
|
20393
|
+
relatedCommands: ["recordings list", "recordings export", "transcript get", "audio"]
|
|
20394
|
+
},
|
|
20395
|
+
"recordings export": {
|
|
20396
|
+
capabilities: [
|
|
20397
|
+
"Write a plain-text Markdown handoff file for agents",
|
|
20398
|
+
"Export raw audio, transcript, summary, action items, subscription/account data, and JSON sidecars"
|
|
20399
|
+
],
|
|
20400
|
+
examples: [
|
|
20401
|
+
{
|
|
20402
|
+
description: "Export a recording bundle into a chosen directory",
|
|
20403
|
+
command: "recappi recordings export <recordingId> --dir ./recappi-export"
|
|
20404
|
+
},
|
|
20405
|
+
{
|
|
20406
|
+
description: "Export and return machine-readable paths",
|
|
20407
|
+
command: "recappi recordings export <recordingId> --json --compact"
|
|
20408
|
+
}
|
|
20409
|
+
],
|
|
20410
|
+
relatedCommands: ["recordings get", "transcript get", "audio", "account status"]
|
|
20359
20411
|
},
|
|
20360
20412
|
"recordings list": {
|
|
20361
20413
|
capabilities: ["List recent recordings", "Search recordings and transcripts", "Find a recordingId"],
|
|
@@ -20363,7 +20415,7 @@ var COMMAND_METADATA = {
|
|
|
20363
20415
|
{ description: "List recent recordings", command: "recappi recordings list" },
|
|
20364
20416
|
{ description: "Search recordings and transcripts", command: "recappi recordings list --search <query>" }
|
|
20365
20417
|
],
|
|
20366
|
-
relatedCommands: ["recordings get", "audio", "transcript get"]
|
|
20418
|
+
relatedCommands: ["recordings get", "recordings export", "audio", "transcript get"]
|
|
20367
20419
|
},
|
|
20368
20420
|
"recordings retranscribe": {
|
|
20369
20421
|
capabilities: ["Re-transcribe an existing recording", "Re-transcribe with new language/prompt/scene/model"],
|
|
@@ -20440,6 +20492,408 @@ ${lines.join("\n")}
|
|
|
20440
20492
|
` : "";
|
|
20441
20493
|
}
|
|
20442
20494
|
|
|
20495
|
+
// src/export.ts
|
|
20496
|
+
import { promises as fs5 } from "fs";
|
|
20497
|
+
import os5 from "os";
|
|
20498
|
+
import path6 from "path";
|
|
20499
|
+
async function syncRecordingText(opts) {
|
|
20500
|
+
const context = await loadRecordingBundleContext(opts);
|
|
20501
|
+
const sessionDir = await resolveRecordingSessionDir(context.recording, opts);
|
|
20502
|
+
return writeRecordingTextFiles(context, sessionDir, {
|
|
20503
|
+
now: opts.now
|
|
20504
|
+
});
|
|
20505
|
+
}
|
|
20506
|
+
async function syncRecordingAudio(opts) {
|
|
20507
|
+
const context = await loadRecordingBundleContext(opts);
|
|
20508
|
+
const sessionDir = await resolveRecordingSessionDir(context.recording, opts);
|
|
20509
|
+
const audio = await downloadRecordingAudioToDir(context.recording, opts.recordingAudio, sessionDir);
|
|
20510
|
+
const text = await writeRecordingTextFiles(context, sessionDir, {
|
|
20511
|
+
now: opts.now,
|
|
20512
|
+
uploadFilename: path6.basename(audio.localPath)
|
|
20513
|
+
});
|
|
20514
|
+
return { ...text, audioPath: audio.localPath, audio: audioMetadata(audio) };
|
|
20515
|
+
}
|
|
20516
|
+
async function exportRecording(opts) {
|
|
20517
|
+
const context = await loadRecordingBundleContext(opts);
|
|
20518
|
+
const exportDir = await resolveRecordingSessionDir(context.recording, opts);
|
|
20519
|
+
const audio = await downloadRecordingAudioToDir(context.recording, opts.recordingAudio, exportDir);
|
|
20520
|
+
const textFiles = await writeRecordingTextFiles(context, exportDir, {
|
|
20521
|
+
now: opts.now,
|
|
20522
|
+
uploadFilename: path6.basename(audio.localPath)
|
|
20523
|
+
});
|
|
20524
|
+
const { recording, subscription, transcript } = context;
|
|
20525
|
+
const subscriptionPath = path6.join(exportDir, "subscription.md");
|
|
20526
|
+
const subscriptionJsonPath = path6.join(exportDir, "subscription.json");
|
|
20527
|
+
await fs5.writeFile(subscriptionPath, renderSubscriptionMarkdown(subscription), "utf8");
|
|
20528
|
+
await writeJson(subscriptionJsonPath, subscription);
|
|
20529
|
+
const textPath = path6.join(exportDir, "handoff.md");
|
|
20530
|
+
const manifestPath = path6.join(exportDir, "manifest.json");
|
|
20531
|
+
const data = recordingExportDataSchema.parse({
|
|
20532
|
+
origin: recording.origin,
|
|
20533
|
+
recordingId: recording.recordingId,
|
|
20534
|
+
exportDir,
|
|
20535
|
+
textPath,
|
|
20536
|
+
manifestPath,
|
|
20537
|
+
remoteManifestPath: textFiles.remoteManifestPath,
|
|
20538
|
+
sessionMetadataPath: textFiles.sessionMetadataPath,
|
|
20539
|
+
recordingJsonPath: textFiles.recordingJsonPath,
|
|
20540
|
+
subscriptionPath,
|
|
20541
|
+
subscriptionJsonPath,
|
|
20542
|
+
audioPath: audio.localPath,
|
|
20543
|
+
...textFiles.transcriptId !== void 0 ? { transcriptId: textFiles.transcriptId } : {},
|
|
20544
|
+
...textFiles.transcriptPath ? { transcriptPath: textFiles.transcriptPath } : {},
|
|
20545
|
+
...textFiles.transcriptJsonPath ? { transcriptJsonPath: textFiles.transcriptJsonPath } : {},
|
|
20546
|
+
...textFiles.summaryPath ? { summaryPath: textFiles.summaryPath } : {},
|
|
20547
|
+
...textFiles.summaryJsonPath ? { summaryJsonPath: textFiles.summaryJsonPath } : {},
|
|
20548
|
+
...textFiles.actionItemsPath ? { actionItemsPath: textFiles.actionItemsPath } : {},
|
|
20549
|
+
...textFiles.summaryStatus ? { summaryStatus: textFiles.summaryStatus } : {},
|
|
20550
|
+
audio: audioMetadata(audio)
|
|
20551
|
+
});
|
|
20552
|
+
await fs5.writeFile(
|
|
20553
|
+
textPath,
|
|
20554
|
+
renderHandoffMarkdown(recording, subscription, audio, data, transcript),
|
|
20555
|
+
"utf8"
|
|
20556
|
+
);
|
|
20557
|
+
await writeJson(manifestPath, {
|
|
20558
|
+
exportedAt: (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
|
|
20559
|
+
command: "recordings export",
|
|
20560
|
+
data
|
|
20561
|
+
});
|
|
20562
|
+
return data;
|
|
20563
|
+
}
|
|
20564
|
+
async function loadRecordingBundleContext(opts) {
|
|
20565
|
+
const recording = await opts.client.getRecording(opts.recordingId);
|
|
20566
|
+
const subscription = await opts.client.accountStatus();
|
|
20567
|
+
const transcript = recording.activeTranscriptId ? await opts.client.getTranscript(recording.activeTranscriptId) : void 0;
|
|
20568
|
+
return {
|
|
20569
|
+
recording,
|
|
20570
|
+
subscription,
|
|
20571
|
+
...transcript ? { transcript } : {},
|
|
20572
|
+
transcriptId: transcript?.transcriptId ?? recording.activeTranscriptId ?? void 0
|
|
20573
|
+
};
|
|
20574
|
+
}
|
|
20575
|
+
async function downloadRecordingAudioToDir(recording, recordingAudio, directory) {
|
|
20576
|
+
return recordingAudio.downloadRecordingAudioFile(recording.recordingId, {
|
|
20577
|
+
directory,
|
|
20578
|
+
filenameStem: "recording",
|
|
20579
|
+
title: recording.title ?? recording.summaryTitle ?? recording.recordingId
|
|
20580
|
+
});
|
|
20581
|
+
}
|
|
20582
|
+
async function writeRecordingTextFiles(context, sessionDir, opts) {
|
|
20583
|
+
const { recording, transcript, subscription } = context;
|
|
20584
|
+
await fs5.mkdir(sessionDir, { recursive: true });
|
|
20585
|
+
const recordingJsonPath = path6.join(sessionDir, "recording.json");
|
|
20586
|
+
const sessionMetadataPath = path6.join(sessionDir, "session-metadata.json");
|
|
20587
|
+
const remoteManifestPath = path6.join(sessionDir, "remote-session.json");
|
|
20588
|
+
const existingManifest = await readJsonRecord(remoteManifestPath);
|
|
20589
|
+
const uploadFilename = opts.uploadFilename ?? (typeof existingManifest?.uploadFilename === "string" ? existingManifest.uploadFilename : void 0);
|
|
20590
|
+
await writeJson(recordingJsonPath, recording);
|
|
20591
|
+
await writeJson(sessionMetadataPath, renderSessionMetadata(recording));
|
|
20592
|
+
await writeJson(
|
|
20593
|
+
remoteManifestPath,
|
|
20594
|
+
renderRemoteSessionManifest(recording, uploadFilename, transcript, subscription, opts.now)
|
|
20595
|
+
);
|
|
20596
|
+
let transcriptPath;
|
|
20597
|
+
let transcriptJsonPath;
|
|
20598
|
+
let summaryPath;
|
|
20599
|
+
let summaryJsonPath;
|
|
20600
|
+
let actionItemsPath;
|
|
20601
|
+
let summaryStatus;
|
|
20602
|
+
if (transcript) {
|
|
20603
|
+
transcriptPath = path6.join(sessionDir, "transcript.md");
|
|
20604
|
+
transcriptJsonPath = path6.join(sessionDir, "transcript.json");
|
|
20605
|
+
summaryPath = path6.join(sessionDir, "summary.md");
|
|
20606
|
+
summaryJsonPath = path6.join(sessionDir, "summary.json");
|
|
20607
|
+
actionItemsPath = path6.join(sessionDir, "action-items.md");
|
|
20608
|
+
summaryStatus = transcript.summary.status;
|
|
20609
|
+
await fs5.writeFile(transcriptPath, renderTranscriptMarkdown(transcript), "utf8");
|
|
20610
|
+
await writeJson(transcriptJsonPath, transcript);
|
|
20611
|
+
await writeJson(summaryJsonPath, transcript.summary);
|
|
20612
|
+
await fs5.writeFile(summaryPath, renderSummaryMarkdown(recording, transcript), "utf8");
|
|
20613
|
+
await writeOptionalText(actionItemsPath, renderActionItemsMarkdown(transcript.summary));
|
|
20614
|
+
}
|
|
20615
|
+
return {
|
|
20616
|
+
recordingId: recording.recordingId,
|
|
20617
|
+
sessionDir,
|
|
20618
|
+
remoteManifestPath,
|
|
20619
|
+
sessionMetadataPath,
|
|
20620
|
+
recordingJsonPath,
|
|
20621
|
+
...context.transcriptId !== void 0 ? { transcriptId: context.transcriptId } : {},
|
|
20622
|
+
...transcriptPath ? { transcriptPath } : {},
|
|
20623
|
+
...transcriptJsonPath ? { transcriptJsonPath } : {},
|
|
20624
|
+
...summaryPath ? { summaryPath } : {},
|
|
20625
|
+
...summaryJsonPath ? { summaryJsonPath } : {},
|
|
20626
|
+
...actionItemsPath ? { actionItemsPath } : {},
|
|
20627
|
+
...summaryStatus ? { summaryStatus } : {}
|
|
20628
|
+
};
|
|
20629
|
+
}
|
|
20630
|
+
async function resolveRecordingSessionDir(recording, opts) {
|
|
20631
|
+
if (opts.directory) return path6.resolve(opts.directory);
|
|
20632
|
+
const existing = await findExistingRecordingSessionDir(recording.recordingId, opts.homeDir, opts.env);
|
|
20633
|
+
if (existing) return existing;
|
|
20634
|
+
return createRecordingSessionDir(recording, opts.homeDir, opts.env);
|
|
20635
|
+
}
|
|
20636
|
+
async function findExistingRecordingSessionDir(recordingId, homeDir, env) {
|
|
20637
|
+
const base = recordingSessionBaseDirectory(homeDir, env);
|
|
20638
|
+
let entries;
|
|
20639
|
+
try {
|
|
20640
|
+
entries = await fs5.readdir(base, { withFileTypes: true });
|
|
20641
|
+
} catch {
|
|
20642
|
+
return void 0;
|
|
20643
|
+
}
|
|
20644
|
+
for (const entry of entries) {
|
|
20645
|
+
if (!entry.isDirectory()) continue;
|
|
20646
|
+
const dir = path6.join(base, entry.name);
|
|
20647
|
+
const manifest = await readJsonRecord(path6.join(dir, "remote-session.json"));
|
|
20648
|
+
if (manifest?.recordingId === recordingId) return dir;
|
|
20649
|
+
}
|
|
20650
|
+
return void 0;
|
|
20651
|
+
}
|
|
20652
|
+
async function createRecordingSessionDir(recording, homeDir, env) {
|
|
20653
|
+
const base = recordingSessionBaseDirectory(homeDir, env);
|
|
20654
|
+
await fs5.mkdir(base, { recursive: true });
|
|
20655
|
+
const stem = formatSessionDirectoryDate(new Date(recording.createdAt));
|
|
20656
|
+
let candidate = path6.join(base, stem);
|
|
20657
|
+
let suffix = 2;
|
|
20658
|
+
while (await pathExists(candidate)) {
|
|
20659
|
+
candidate = path6.join(base, `${stem}-cloud-${suffix}`);
|
|
20660
|
+
suffix += 1;
|
|
20661
|
+
}
|
|
20662
|
+
await fs5.mkdir(candidate, { recursive: true });
|
|
20663
|
+
return candidate;
|
|
20664
|
+
}
|
|
20665
|
+
function recordingSessionBaseDirectory(homeDir = os5.homedir(), env = process.env) {
|
|
20666
|
+
const explicit = env.RECAPPI_LOCAL_SESSIONS_DIR?.trim();
|
|
20667
|
+
if (explicit) return explicit;
|
|
20668
|
+
return path6.join(homeDir, "Documents", "Recappi Mini");
|
|
20669
|
+
}
|
|
20670
|
+
async function readJsonRecord(filePath) {
|
|
20671
|
+
try {
|
|
20672
|
+
const parsed = JSON.parse(await fs5.readFile(filePath, "utf8"));
|
|
20673
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : void 0;
|
|
20674
|
+
} catch {
|
|
20675
|
+
return void 0;
|
|
20676
|
+
}
|
|
20677
|
+
}
|
|
20678
|
+
async function pathExists(filePath) {
|
|
20679
|
+
try {
|
|
20680
|
+
await fs5.access(filePath);
|
|
20681
|
+
return true;
|
|
20682
|
+
} catch {
|
|
20683
|
+
return false;
|
|
20684
|
+
}
|
|
20685
|
+
}
|
|
20686
|
+
function formatSessionDirectoryDate(date5) {
|
|
20687
|
+
const year = date5.getFullYear();
|
|
20688
|
+
const month = String(date5.getMonth() + 1).padStart(2, "0");
|
|
20689
|
+
const day = String(date5.getDate()).padStart(2, "0");
|
|
20690
|
+
const hour = String(date5.getHours()).padStart(2, "0");
|
|
20691
|
+
const minute = String(date5.getMinutes()).padStart(2, "0");
|
|
20692
|
+
const second = String(date5.getSeconds()).padStart(2, "0");
|
|
20693
|
+
return `${year}-${month}-${day}_${hour}${minute}${second}`;
|
|
20694
|
+
}
|
|
20695
|
+
async function writeJson(filePath, value) {
|
|
20696
|
+
await fs5.writeFile(filePath, `${JSON.stringify(value, null, 2)}
|
|
20697
|
+
`, "utf8");
|
|
20698
|
+
}
|
|
20699
|
+
async function writeOptionalText(filePath, value) {
|
|
20700
|
+
if (value && value.trim()) {
|
|
20701
|
+
await fs5.writeFile(filePath, value, "utf8");
|
|
20702
|
+
return;
|
|
20703
|
+
}
|
|
20704
|
+
await fs5.rm(filePath, { force: true });
|
|
20705
|
+
}
|
|
20706
|
+
function renderHandoffMarkdown(recording, subscription, audio, data, transcript) {
|
|
20707
|
+
const lines = [];
|
|
20708
|
+
lines.push(`# ${recording.title ?? recording.summaryTitle ?? "Recappi Recording"}`);
|
|
20709
|
+
lines.push("");
|
|
20710
|
+
lines.push("## Files", "");
|
|
20711
|
+
lines.push(`- Audio: ${audio.localPath}`);
|
|
20712
|
+
lines.push(`- Subscription: ${data.subscriptionPath}`);
|
|
20713
|
+
if (data.summaryPath) lines.push(`- Summary: ${data.summaryPath}`);
|
|
20714
|
+
if (data.transcriptPath) lines.push(`- Transcript: ${data.transcriptPath}`);
|
|
20715
|
+
if (data.actionItemsPath) lines.push(`- Action items: ${data.actionItemsPath}`);
|
|
20716
|
+
lines.push(`- Remote session manifest: ${data.remoteManifestPath}`);
|
|
20717
|
+
lines.push(`- Session metadata: ${data.sessionMetadataPath}`);
|
|
20718
|
+
lines.push(`- Manifest: ${data.manifestPath}`);
|
|
20719
|
+
lines.push("");
|
|
20720
|
+
lines.push("## Recording", "");
|
|
20721
|
+
lines.push(`- recordingId: ${recording.recordingId}`);
|
|
20722
|
+
lines.push(`- status: ${recording.status}`);
|
|
20723
|
+
if (recording.durationMs !== void 0 && recording.durationMs !== null) {
|
|
20724
|
+
lines.push(`- duration: ${formatTimestamp(recording.durationMs)}`);
|
|
20725
|
+
}
|
|
20726
|
+
if (recording.sizeBytes !== void 0 && recording.sizeBytes !== null) {
|
|
20727
|
+
lines.push(`- sizeBytes: ${recording.sizeBytes}`);
|
|
20728
|
+
}
|
|
20729
|
+
if (transcript) {
|
|
20730
|
+
lines.push(`- transcriptId: ${transcript.transcriptId}`);
|
|
20731
|
+
lines.push(`- summaryStatus: ${transcript.summary.status}`);
|
|
20732
|
+
} else {
|
|
20733
|
+
lines.push("- transcript: not available");
|
|
20734
|
+
}
|
|
20735
|
+
lines.push("");
|
|
20736
|
+
lines.push("## Subscription", "");
|
|
20737
|
+
appendSubscriptionLines(lines, subscription);
|
|
20738
|
+
lines.push("");
|
|
20739
|
+
if (transcript) {
|
|
20740
|
+
lines.push(renderSummaryMarkdown(recording, transcript).trimEnd());
|
|
20741
|
+
lines.push("");
|
|
20742
|
+
lines.push(renderTranscriptMarkdown(transcript).trimEnd());
|
|
20743
|
+
lines.push("");
|
|
20744
|
+
}
|
|
20745
|
+
return `${lines.join("\n").trimEnd()}
|
|
20746
|
+
`;
|
|
20747
|
+
}
|
|
20748
|
+
function renderSubscriptionMarkdown(subscription) {
|
|
20749
|
+
const lines = ["# Subscription", ""];
|
|
20750
|
+
appendSubscriptionLines(lines, subscription);
|
|
20751
|
+
return `${lines.join("\n").trimEnd()}
|
|
20752
|
+
`;
|
|
20753
|
+
}
|
|
20754
|
+
function renderSessionMetadata(recording) {
|
|
20755
|
+
const sourceTitle = recording.title ?? recording.summaryTitle ?? "Recappi Cloud";
|
|
20756
|
+
return stripUndefined({
|
|
20757
|
+
summaryTitle: recording.summaryTitle ?? recording.title ?? void 0,
|
|
20758
|
+
sourceTitle,
|
|
20759
|
+
sourceAppName: void 0,
|
|
20760
|
+
sourceBundleID: void 0,
|
|
20761
|
+
startedAt: new Date(recording.createdAt).toISOString(),
|
|
20762
|
+
sceneTemplate: void 0,
|
|
20763
|
+
extraPrompt: void 0,
|
|
20764
|
+
includesMicrophoneAudio: void 0
|
|
20765
|
+
});
|
|
20766
|
+
}
|
|
20767
|
+
function renderRemoteSessionManifest(recording, uploadFilename, transcript, subscription, now) {
|
|
20768
|
+
return stripUndefined({
|
|
20769
|
+
recordingId: recording.recordingId,
|
|
20770
|
+
jobId: transcript?.jobId,
|
|
20771
|
+
transcriptId: transcript?.transcriptId ?? recording.activeTranscriptId ?? void 0,
|
|
20772
|
+
stage: transcript ? "done" : "synced",
|
|
20773
|
+
errorMessage: void 0,
|
|
20774
|
+
uploadFilename,
|
|
20775
|
+
provider: transcript?.provider,
|
|
20776
|
+
model: transcript?.model,
|
|
20777
|
+
updatedAt: (now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
|
|
20778
|
+
accountUserId: subscription.userId,
|
|
20779
|
+
accountBackendOrigin: subscription.origin
|
|
20780
|
+
});
|
|
20781
|
+
}
|
|
20782
|
+
function stripUndefined(value) {
|
|
20783
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
20784
|
+
}
|
|
20785
|
+
function appendSubscriptionLines(lines, subscription) {
|
|
20786
|
+
lines.push(`- origin: ${subscription.origin}`);
|
|
20787
|
+
lines.push(`- loggedIn: ${subscription.loggedIn}`);
|
|
20788
|
+
if (subscription.email) lines.push(`- email: ${subscription.email}`);
|
|
20789
|
+
if (subscription.userId) lines.push(`- userId: ${subscription.userId}`);
|
|
20790
|
+
if (subscription.billing) {
|
|
20791
|
+
lines.push(`- plan: ${subscription.billing.tier}`);
|
|
20792
|
+
lines.push(`- minutesUsed: ${subscription.billing.minutesUsed}`);
|
|
20793
|
+
lines.push(`- minutesCap: ${subscription.billing.minutesCap ?? "unlimited"}`);
|
|
20794
|
+
lines.push(`- batchMinutesUsed: ${subscription.billing.batchMinutesUsed}`);
|
|
20795
|
+
lines.push(`- realtimeMinutesUsed: ${subscription.billing.realtimeMinutesUsed}`);
|
|
20796
|
+
lines.push(`- storageBytes: ${subscription.billing.storageBytes}`);
|
|
20797
|
+
lines.push(`- storageCapBytes: ${subscription.billing.storageCapBytes ?? "unlimited"}`);
|
|
20798
|
+
lines.push(`- periodStart: ${subscription.billing.periodStart}`);
|
|
20799
|
+
lines.push(`- periodEnd: ${subscription.billing.periodEnd}`);
|
|
20800
|
+
}
|
|
20801
|
+
}
|
|
20802
|
+
function renderTranscriptMarkdown(transcript) {
|
|
20803
|
+
return `# Transcript
|
|
20804
|
+
|
|
20805
|
+
${renderTranscriptLines(transcript)}
|
|
20806
|
+
`;
|
|
20807
|
+
}
|
|
20808
|
+
function renderTranscriptLines(transcript) {
|
|
20809
|
+
const lines = transcript.segments.map((segment) => {
|
|
20810
|
+
const speaker = segment.speaker ? `${segment.speaker}: ` : "";
|
|
20811
|
+
return `[${formatTimestamp(segment.startMs)}] ${speaker}${segment.text}`;
|
|
20812
|
+
});
|
|
20813
|
+
if (lines.length > 0) return lines.join("\n");
|
|
20814
|
+
return transcript.text;
|
|
20815
|
+
}
|
|
20816
|
+
function renderSummaryMarkdown(recording, transcript) {
|
|
20817
|
+
const summary = transcript.summary;
|
|
20818
|
+
const lines = [];
|
|
20819
|
+
lines.push(`# ${summary.title ?? recording.title ?? recording.summaryTitle ?? "Summary"}`);
|
|
20820
|
+
lines.push("");
|
|
20821
|
+
lines.push(`- recordingId: ${recording.recordingId}`);
|
|
20822
|
+
lines.push(`- transcriptId: ${transcript.transcriptId}`);
|
|
20823
|
+
lines.push(`- summaryStatus: ${summary.status}`);
|
|
20824
|
+
if (summary.error) lines.push(`- error: ${summary.error}`);
|
|
20825
|
+
lines.push("");
|
|
20826
|
+
if (summary.tldr) {
|
|
20827
|
+
lines.push("## TL;DR", "");
|
|
20828
|
+
lines.push(summary.tldr, "");
|
|
20829
|
+
}
|
|
20830
|
+
appendStringList(lines, "Key Points", summary.keyPoints);
|
|
20831
|
+
appendStringList(lines, "Topics", summary.topics);
|
|
20832
|
+
appendStringList(lines, "Decisions", summary.decisions);
|
|
20833
|
+
appendActionItems(lines, summary.actionItems);
|
|
20834
|
+
appendTimeline(lines, summary.timeline);
|
|
20835
|
+
appendQuotes(lines, summary.quotes);
|
|
20836
|
+
if (lines[lines.length - 1] !== "") lines.push("");
|
|
20837
|
+
return lines.join("\n");
|
|
20838
|
+
}
|
|
20839
|
+
function renderActionItemsMarkdown(summary) {
|
|
20840
|
+
if (!summary.actionItems || summary.actionItems.length === 0) return null;
|
|
20841
|
+
const lines = ["# Action Items", ""];
|
|
20842
|
+
for (const item of summary.actionItems) {
|
|
20843
|
+
lines.push(`- ${item.who ? `${item.who} - ` : ""}${item.what}`);
|
|
20844
|
+
}
|
|
20845
|
+
lines.push("");
|
|
20846
|
+
return lines.join("\n");
|
|
20847
|
+
}
|
|
20848
|
+
function appendStringList(lines, title, values) {
|
|
20849
|
+
if (!values || values.length === 0) return;
|
|
20850
|
+
lines.push(`## ${title}`, "");
|
|
20851
|
+
for (const value of values) lines.push(`- ${value}`);
|
|
20852
|
+
lines.push("");
|
|
20853
|
+
}
|
|
20854
|
+
function appendActionItems(lines, values) {
|
|
20855
|
+
if (!values || values.length === 0) return;
|
|
20856
|
+
lines.push("## Action Items", "");
|
|
20857
|
+
for (const item of values) {
|
|
20858
|
+
lines.push(`- ${item.who ? `${item.who}: ` : ""}${item.what}`);
|
|
20859
|
+
}
|
|
20860
|
+
lines.push("");
|
|
20861
|
+
}
|
|
20862
|
+
function appendTimeline(lines, values) {
|
|
20863
|
+
if (!values || values.length === 0) return;
|
|
20864
|
+
lines.push("## Timeline", "");
|
|
20865
|
+
for (const item of values) {
|
|
20866
|
+
lines.push(
|
|
20867
|
+
`- ${formatTimestamp(item.startMs)}-${formatTimestamp(item.endMs)}: ${item.title} - ${item.summary}`
|
|
20868
|
+
);
|
|
20869
|
+
}
|
|
20870
|
+
lines.push("");
|
|
20871
|
+
}
|
|
20872
|
+
function appendQuotes(lines, values) {
|
|
20873
|
+
if (!values || values.length === 0) return;
|
|
20874
|
+
lines.push("## Quotes", "");
|
|
20875
|
+
for (const item of values) {
|
|
20876
|
+
lines.push(`- ${item.speaker ? `${item.speaker}: ` : ""}"${item.text}"`);
|
|
20877
|
+
}
|
|
20878
|
+
lines.push("");
|
|
20879
|
+
}
|
|
20880
|
+
function audioMetadata(audio) {
|
|
20881
|
+
return {
|
|
20882
|
+
...audio.contentType ? { contentType: audio.contentType } : {},
|
|
20883
|
+
...audio.contentLength !== void 0 ? { contentLength: audio.contentLength } : {},
|
|
20884
|
+
reused: audio.reused
|
|
20885
|
+
};
|
|
20886
|
+
}
|
|
20887
|
+
function formatTimestamp(ms) {
|
|
20888
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
20889
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
20890
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
20891
|
+
const seconds = totalSeconds % 60;
|
|
20892
|
+
const mm = String(minutes).padStart(2, "0");
|
|
20893
|
+
const ss = String(seconds).padStart(2, "0");
|
|
20894
|
+
return hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;
|
|
20895
|
+
}
|
|
20896
|
+
|
|
20443
20897
|
// src/progressStepper.ts
|
|
20444
20898
|
var STEP_DEFS = [
|
|
20445
20899
|
{ key: "check", label: "Check" },
|
|
@@ -20934,6 +21388,28 @@ Next:
|
|
|
20934
21388
|
}
|
|
20935
21389
|
return;
|
|
20936
21390
|
}
|
|
21391
|
+
if (command === "recordings export" && isRecord4(data)) {
|
|
21392
|
+
opts.stdout("Recording export ready\n");
|
|
21393
|
+
printStringField(opts, data, "recordingId");
|
|
21394
|
+
printStringField(opts, data, "exportDir");
|
|
21395
|
+
printStringField(opts, data, "textPath");
|
|
21396
|
+
printStringField(opts, data, "audioPath");
|
|
21397
|
+
printStringField(opts, data, "transcriptPath");
|
|
21398
|
+
printStringField(opts, data, "summaryPath");
|
|
21399
|
+
printStringField(opts, data, "actionItemsPath");
|
|
21400
|
+
printStringField(opts, data, "subscriptionPath");
|
|
21401
|
+
printStringField(opts, data, "transcriptJsonPath");
|
|
21402
|
+
printStringField(opts, data, "summaryJsonPath");
|
|
21403
|
+
printStringField(opts, data, "subscriptionJsonPath");
|
|
21404
|
+
printStringField(opts, data, "recordingJsonPath");
|
|
21405
|
+
printStringField(opts, data, "remoteManifestPath");
|
|
21406
|
+
printStringField(opts, data, "sessionMetadataPath");
|
|
21407
|
+
printStringField(opts, data, "manifestPath");
|
|
21408
|
+
if (typeof data.transcriptPath !== "string") {
|
|
21409
|
+
opts.stdout(" transcript: no active transcript\n");
|
|
21410
|
+
}
|
|
21411
|
+
return;
|
|
21412
|
+
}
|
|
20937
21413
|
if (command === "recordings retranscribe" && isRecord4(data)) {
|
|
20938
21414
|
opts.stdout("Transcription started\n");
|
|
20939
21415
|
if (typeof data.recordingId === "string") opts.stdout(` recordingId: ${data.recordingId}
|
|
@@ -21311,6 +21787,10 @@ function recordingTitle(item) {
|
|
|
21311
21787
|
function numberText(value) {
|
|
21312
21788
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : "0";
|
|
21313
21789
|
}
|
|
21790
|
+
function printStringField(opts, data, field) {
|
|
21791
|
+
if (typeof data[field] === "string") opts.stdout(` ${field}: ${data[field]}
|
|
21792
|
+
`);
|
|
21793
|
+
}
|
|
21314
21794
|
function formatDurationMs(ms) {
|
|
21315
21795
|
return formatClock(ms / 1e3);
|
|
21316
21796
|
}
|
|
@@ -21427,6 +21907,7 @@ var COMMAND_DATA_SCHEMAS = {
|
|
|
21427
21907
|
upload: uploadBatchDataSchema,
|
|
21428
21908
|
record: recordCommandDataSchema,
|
|
21429
21909
|
"recordings get": recordingDataSchema,
|
|
21910
|
+
"recordings export": recordingExportDataSchema,
|
|
21430
21911
|
"recordings list": recordingListDataSchema,
|
|
21431
21912
|
"recordings retranscribe": recordingTranscribeDataSchema,
|
|
21432
21913
|
"recordings resummarize": recordingSummarizeDataSchema,
|
|
@@ -21465,11 +21946,11 @@ function buildSchemaDocument(program) {
|
|
|
21465
21946
|
event: toJsonSchema(operationEventSchema)
|
|
21466
21947
|
};
|
|
21467
21948
|
}
|
|
21468
|
-
function walkCommands(command,
|
|
21949
|
+
function walkCommands(command, path7, out) {
|
|
21469
21950
|
for (const sub of subcommandsOf(command)) {
|
|
21470
21951
|
const name = sub.name();
|
|
21471
21952
|
if (name === "help") continue;
|
|
21472
|
-
const fullPath = [...
|
|
21953
|
+
const fullPath = [...path7, name];
|
|
21473
21954
|
const children = subcommandsOf(sub).filter((child) => child.name() !== "help");
|
|
21474
21955
|
if (children.length === 0) {
|
|
21475
21956
|
out.push(leafCommandDoc(sub, fullPath.join(" ")));
|
|
@@ -21871,8 +22352,8 @@ function requestLaunchServicesSidecarShutdown(input) {
|
|
|
21871
22352
|
} catch {
|
|
21872
22353
|
}
|
|
21873
22354
|
}
|
|
21874
|
-
function createFifo(
|
|
21875
|
-
const result = spawnSync("mkfifo", [
|
|
22355
|
+
function createFifo(path7) {
|
|
22356
|
+
const result = spawnSync("mkfifo", [path7], { encoding: "utf8" });
|
|
21876
22357
|
if (result.status !== 0) {
|
|
21877
22358
|
throw cliError("record.helper_unavailable", "Recappi recording helper could not start.", {
|
|
21878
22359
|
hint: result.stderr || "Could not create the local recorder pipes. Try again."
|
|
@@ -22496,9 +22977,9 @@ function resolveSidecarCommand(opts) {
|
|
|
22496
22977
|
hint: `No bundled helper is registered for ${platform}. Set ${SIDECAR_COMMAND_ENV} to a compatible helper when one is available.`
|
|
22497
22978
|
});
|
|
22498
22979
|
}
|
|
22499
|
-
function ensureBundledHelperExecutable(
|
|
22500
|
-
if (process.platform === "darwin" &&
|
|
22501
|
-
const stableApp = ensureStableDarwinHelperApp(
|
|
22980
|
+
function ensureBundledHelperExecutable(path7, opts = {}) {
|
|
22981
|
+
if (process.platform === "darwin" && path7.endsWith(".app")) {
|
|
22982
|
+
const stableApp = ensureStableDarwinHelperApp(path7, opts);
|
|
22502
22983
|
const executable = darwinAppExecutablePath(stableApp);
|
|
22503
22984
|
if (!existsSync(executable)) {
|
|
22504
22985
|
throw cliError("record.helper_unavailable", "Recappi recording helper is not available.", {
|
|
@@ -22508,19 +22989,19 @@ function ensureBundledHelperExecutable(path6, opts = {}) {
|
|
|
22508
22989
|
ensureExecutableMode(executable);
|
|
22509
22990
|
return stableApp;
|
|
22510
22991
|
}
|
|
22511
|
-
if (process.platform === "win32") return
|
|
22512
|
-
ensureExecutableMode(
|
|
22513
|
-
return
|
|
22992
|
+
if (process.platform === "win32") return path7;
|
|
22993
|
+
ensureExecutableMode(path7);
|
|
22994
|
+
return path7;
|
|
22514
22995
|
}
|
|
22515
|
-
function ensureExecutableMode(
|
|
22516
|
-
const mode = statSync(
|
|
22996
|
+
function ensureExecutableMode(path7) {
|
|
22997
|
+
const mode = statSync(path7).mode;
|
|
22517
22998
|
if ((mode & 73) !== 0) return;
|
|
22518
22999
|
try {
|
|
22519
|
-
chmodSync(
|
|
23000
|
+
chmodSync(path7, mode | 493);
|
|
22520
23001
|
} catch (error51) {
|
|
22521
23002
|
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
22522
23003
|
throw cliError("record.helper_unavailable", "Recappi recording helper is not executable.", {
|
|
22523
|
-
hint: `Could not make bundled helper executable at ${
|
|
23004
|
+
hint: `Could not make bundled helper executable at ${path7}: ${message}. Reinstall recappi, or set ${SIDECAR_COMMAND_ENV} to a compatible helper.`
|
|
22524
23005
|
});
|
|
22525
23006
|
}
|
|
22526
23007
|
}
|
|
@@ -22565,14 +23046,14 @@ function helperSourceSignature(sourceApp) {
|
|
|
22565
23046
|
codeSignature: existsSync(signaturePath) ? fileDigest(signaturePath) : null
|
|
22566
23047
|
});
|
|
22567
23048
|
}
|
|
22568
|
-
function fileDigest(
|
|
23049
|
+
function fileDigest(path7) {
|
|
22569
23050
|
const hash2 = createHash("sha256");
|
|
22570
|
-
hash2.update(readFileSync2(
|
|
23051
|
+
hash2.update(readFileSync2(path7));
|
|
22571
23052
|
return hash2.digest("hex");
|
|
22572
23053
|
}
|
|
22573
|
-
function readTextIfExists(
|
|
23054
|
+
function readTextIfExists(path7) {
|
|
22574
23055
|
try {
|
|
22575
|
-
return readFileSync2(
|
|
23056
|
+
return readFileSync2(path7, "utf8");
|
|
22576
23057
|
} catch {
|
|
22577
23058
|
return null;
|
|
22578
23059
|
}
|
|
@@ -22935,6 +23416,26 @@ async function runCli(deps = {}) {
|
|
|
22935
23416
|
},
|
|
22936
23417
|
retranscribeRecording: (recordingId, options = {}) => client.transcribeRecording({ recordingId, ...options }),
|
|
22937
23418
|
resummarizeRecording: (recordingId) => client.summarizeRecording({ recordingId }),
|
|
23419
|
+
syncRecordingText: (recordingId) => syncRecordingText({
|
|
23420
|
+
recordingId,
|
|
23421
|
+
client,
|
|
23422
|
+
env: deps.env,
|
|
23423
|
+
homeDir: deps.homeDir
|
|
23424
|
+
}),
|
|
23425
|
+
syncRecordingAudio: (recordingId) => syncRecordingAudio({
|
|
23426
|
+
recordingId,
|
|
23427
|
+
client,
|
|
23428
|
+
recordingAudio,
|
|
23429
|
+
env: deps.env,
|
|
23430
|
+
homeDir: deps.homeDir
|
|
23431
|
+
}),
|
|
23432
|
+
exportRecording: (recordingId) => exportRecording({
|
|
23433
|
+
recordingId,
|
|
23434
|
+
client,
|
|
23435
|
+
recordingAudio,
|
|
23436
|
+
env: deps.env,
|
|
23437
|
+
homeDir: deps.homeDir
|
|
23438
|
+
}),
|
|
22938
23439
|
initialView: parsed.initialView
|
|
22939
23440
|
});
|
|
22940
23441
|
return 0;
|
|
@@ -22965,7 +23466,7 @@ async function runCli(deps = {}) {
|
|
|
22965
23466
|
return 0;
|
|
22966
23467
|
}
|
|
22967
23468
|
if (parsed.kind === "auth-logout") {
|
|
22968
|
-
const cleared = await clearAuthConfig(deps.homeDir ??
|
|
23469
|
+
const cleared = await clearAuthConfig(deps.homeDir ?? os6.homedir());
|
|
22969
23470
|
renderSuccess("auth logout", { loggedIn: false, origin: auth.origin, cleared }, render3);
|
|
22970
23471
|
return 0;
|
|
22971
23472
|
}
|
|
@@ -22976,7 +23477,7 @@ async function runCli(deps = {}) {
|
|
|
22976
23477
|
hint: keychain.hint ?? "Run recappi auth login instead."
|
|
22977
23478
|
});
|
|
22978
23479
|
}
|
|
22979
|
-
await saveAuthConfig(deps.homeDir ??
|
|
23480
|
+
await saveAuthConfig(deps.homeDir ?? os6.homedir(), {
|
|
22980
23481
|
origin: auth.origin,
|
|
22981
23482
|
token: keychain.token
|
|
22982
23483
|
});
|
|
@@ -23125,6 +23626,29 @@ async function runCli(deps = {}) {
|
|
|
23125
23626
|
renderSuccess("recordings get", data, render3);
|
|
23126
23627
|
return 0;
|
|
23127
23628
|
}
|
|
23629
|
+
if (parsed.kind === "recordings-export") {
|
|
23630
|
+
const status = await client.authStatus();
|
|
23631
|
+
if (!status.loggedIn || !status.userId) {
|
|
23632
|
+
throw cliError("auth.not_logged_in", "Sign in before exporting a recording bundle.", {
|
|
23633
|
+
hint: "Run recappi auth login, or import the Recappi Mini session with recappi auth import-macos."
|
|
23634
|
+
});
|
|
23635
|
+
}
|
|
23636
|
+
const recordingAudio = createRecordingAudioRuntime(client, {
|
|
23637
|
+
account: { backendOrigin: auth.origin, userId: status.userId },
|
|
23638
|
+
env: deps.env,
|
|
23639
|
+
homeDir: deps.homeDir
|
|
23640
|
+
});
|
|
23641
|
+
const data = await exportRecording({
|
|
23642
|
+
recordingId: parsed.recordingId,
|
|
23643
|
+
...parsed.directory ? { directory: parsed.directory } : {},
|
|
23644
|
+
client,
|
|
23645
|
+
recordingAudio,
|
|
23646
|
+
env: deps.env,
|
|
23647
|
+
homeDir: deps.homeDir
|
|
23648
|
+
});
|
|
23649
|
+
renderSuccess("recordings export", data, render3);
|
|
23650
|
+
return 0;
|
|
23651
|
+
}
|
|
23128
23652
|
if (parsed.kind === "recordings-retranscribe") {
|
|
23129
23653
|
const eventMode = parsed.options.mode === "jsonl" ? "jsonl" : "human";
|
|
23130
23654
|
const data = await client.transcribeRecording({
|
|
@@ -23480,6 +24004,20 @@ Agent mode:
|
|
|
23480
24004
|
});
|
|
23481
24005
|
}
|
|
23482
24006
|
);
|
|
24007
|
+
const recordingsExport = recordings.command("export <recordingId>").description("Export audio, transcript, summary, and subscription/account files for agents").option("--dir <dir>", "directory for exported files", parseStringOption("--dir")).addHelpText("after", commandMetadataHelpText("recordings export"));
|
|
24008
|
+
addCommonOptions(recordingsExport);
|
|
24009
|
+
recordingsExport.action(
|
|
24010
|
+
(recordingId, _options, command) => {
|
|
24011
|
+
const opts = command.opts();
|
|
24012
|
+
onSelect({
|
|
24013
|
+
kind: "recordings-export",
|
|
24014
|
+
options: collectGlobalOptions(command),
|
|
24015
|
+
commandName: "recordings export",
|
|
24016
|
+
recordingId,
|
|
24017
|
+
...typeof opts.dir === "string" ? { directory: opts.dir } : {}
|
|
24018
|
+
});
|
|
24019
|
+
}
|
|
24020
|
+
);
|
|
23483
24021
|
const recordingsRetranscribe = recordings.command("retranscribe <recordingId>").description("Start a fresh transcription job for an existing recording").option("--language <lang>", "transcription language hint", parseStringOption("--language")).option("--provider <name>", "transcription provider", parseStringOption("--provider")).option("--model <name>", "transcription model", parseStringOption("--model")).option("--prompt <text>", "custom transcription prompt/context", parseStringOption("--prompt")).option("--scene <id>", "transcription scene preset", parseStringOption("--scene")).option("--wait", "wait for the transcription job to reach a terminal state").addHelpText("after", commandMetadataHelpText("recordings retranscribe"));
|
|
23484
24022
|
addCommonOptions(recordingsRetranscribe);
|
|
23485
24023
|
recordingsRetranscribe.action(
|
|
@@ -23654,7 +24192,8 @@ var VALUE_OPTIONS = /* @__PURE__ */ new Set([
|
|
|
23654
24192
|
"--status",
|
|
23655
24193
|
"--limit",
|
|
23656
24194
|
"--cursor",
|
|
23657
|
-
"--search"
|
|
24195
|
+
"--search",
|
|
24196
|
+
"--dir"
|
|
23658
24197
|
]);
|
|
23659
24198
|
function hasCommandToken(argv) {
|
|
23660
24199
|
return commandTokens(argv).length > 0;
|