recappi 0.1.60 → 0.1.62
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 +142 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -17868,12 +17868,19 @@ var recordingTranscribeDataSchema = external_exports.object({
|
|
|
17868
17868
|
});
|
|
17869
17869
|
var jobDataSchema = external_exports.object({
|
|
17870
17870
|
jobId: external_exports.string(),
|
|
17871
|
+
origin: external_exports.string().optional(),
|
|
17871
17872
|
recordingId: external_exports.string().optional(),
|
|
17872
17873
|
transcriptId: external_exports.string().nullable().optional(),
|
|
17873
17874
|
status: transcriptionJobStatusSchema,
|
|
17874
17875
|
provider: external_exports.string().optional(),
|
|
17875
17876
|
model: external_exports.string().optional(),
|
|
17876
|
-
language: external_exports.string().nullable().optional()
|
|
17877
|
+
language: external_exports.string().nullable().optional(),
|
|
17878
|
+
progressPercent: external_exports.number().min(0).max(100).nullable().optional(),
|
|
17879
|
+
processedDurationMs: external_exports.number().int().nonnegative().nullable().optional(),
|
|
17880
|
+
recording: external_exports.object({
|
|
17881
|
+
title: external_exports.string().nullable().optional(),
|
|
17882
|
+
durationMs: external_exports.number().int().nonnegative().nullable().optional()
|
|
17883
|
+
}).optional()
|
|
17877
17884
|
});
|
|
17878
17885
|
var jobStatusFilterSchema = external_exports.enum([
|
|
17879
17886
|
"active",
|
|
@@ -18033,6 +18040,7 @@ var operationEventTypeSchema = external_exports.enum(["started", "progress", "re
|
|
|
18033
18040
|
var operationEventSchema = external_exports.object({
|
|
18034
18041
|
type: operationEventTypeSchema,
|
|
18035
18042
|
command: external_exports.string(),
|
|
18043
|
+
origin: external_exports.string().optional(),
|
|
18036
18044
|
filePath: external_exports.string().optional(),
|
|
18037
18045
|
recordingId: external_exports.string().optional(),
|
|
18038
18046
|
jobId: external_exports.string().optional(),
|
|
@@ -18580,7 +18588,12 @@ function isNodeErrorCode(error51, code) {
|
|
|
18580
18588
|
}
|
|
18581
18589
|
async function readDurationMs(filePath, contentType) {
|
|
18582
18590
|
if (contentType === "audio/wav") {
|
|
18583
|
-
|
|
18591
|
+
let handle;
|
|
18592
|
+
try {
|
|
18593
|
+
handle = await fs2.open(filePath, "r");
|
|
18594
|
+
} catch (error51) {
|
|
18595
|
+
throwReadablePathError(filePath, error51);
|
|
18596
|
+
}
|
|
18584
18597
|
try {
|
|
18585
18598
|
const buffer = Buffer.alloc(4096);
|
|
18586
18599
|
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
@@ -18595,7 +18608,10 @@ async function readDurationMs(filePath, contentType) {
|
|
|
18595
18608
|
if (typeof metadata.format.duration === "number" && Number.isFinite(metadata.format.duration)) {
|
|
18596
18609
|
return Math.max(1, Math.round(metadata.format.duration * 1e3));
|
|
18597
18610
|
}
|
|
18598
|
-
} catch {
|
|
18611
|
+
} catch (error51) {
|
|
18612
|
+
if (isPermissionDenied(error51)) {
|
|
18613
|
+
throwPermissionDenied(filePath);
|
|
18614
|
+
}
|
|
18599
18615
|
throw cliError(
|
|
18600
18616
|
"input.duration_unavailable",
|
|
18601
18617
|
`Could not read duration for non-WAV file: ${filePath}`,
|
|
@@ -18612,6 +18628,28 @@ async function readDurationMs(filePath, contentType) {
|
|
|
18612
18628
|
}
|
|
18613
18629
|
);
|
|
18614
18630
|
}
|
|
18631
|
+
function throwReadablePathError(filePath, error51) {
|
|
18632
|
+
if (isPermissionDenied(error51)) {
|
|
18633
|
+
throwPermissionDenied(filePath);
|
|
18634
|
+
}
|
|
18635
|
+
throw error51;
|
|
18636
|
+
}
|
|
18637
|
+
function throwPermissionDenied(filePath) {
|
|
18638
|
+
throw cliError("input.permission_denied", `Permission denied reading path: ${filePath}`, {
|
|
18639
|
+
hint: "Grant this terminal/agent access to the file, or copy the audio to a readable location."
|
|
18640
|
+
});
|
|
18641
|
+
}
|
|
18642
|
+
function isPermissionDenied(error51) {
|
|
18643
|
+
if (isNodeErrorCode(error51, "EACCES") || isNodeErrorCode(error51, "EPERM")) return true;
|
|
18644
|
+
if (typeof error51 === "object" && error51 !== null && "cause" in error51) {
|
|
18645
|
+
const cause = error51.cause;
|
|
18646
|
+
if (cause && cause !== error51 && isPermissionDenied(cause)) return true;
|
|
18647
|
+
}
|
|
18648
|
+
if (error51 instanceof Error) {
|
|
18649
|
+
return /(?:EACCES|EPERM|permission denied|operation not permitted)/i.test(error51.message);
|
|
18650
|
+
}
|
|
18651
|
+
return false;
|
|
18652
|
+
}
|
|
18615
18653
|
|
|
18616
18654
|
// src/store.ts
|
|
18617
18655
|
import { mkdirSync } from "fs";
|
|
@@ -19277,9 +19315,12 @@ var RecappiApiClient = class {
|
|
|
19277
19315
|
opts.onEvent?.({
|
|
19278
19316
|
type: "progress",
|
|
19279
19317
|
command: "jobs wait",
|
|
19318
|
+
origin: this.auth.origin,
|
|
19280
19319
|
jobId,
|
|
19320
|
+
...job.recordingId ? { recordingId: job.recordingId } : {},
|
|
19281
19321
|
status: job.status,
|
|
19282
|
-
...job.transcriptId ? { transcriptId: job.transcriptId } : {}
|
|
19322
|
+
...job.transcriptId ? { transcriptId: job.transcriptId } : {},
|
|
19323
|
+
...typeof job.progressPercent === "number" ? { percent: job.progressPercent } : {}
|
|
19283
19324
|
});
|
|
19284
19325
|
if (job.status === "succeeded") return job;
|
|
19285
19326
|
if (job.status === "failed") {
|
|
@@ -19317,6 +19358,15 @@ var RecappiApiClient = class {
|
|
|
19317
19358
|
message: "Finishing upload"
|
|
19318
19359
|
});
|
|
19319
19360
|
await this.postJson(`/api/recordings/${init.id}/complete`, { parts });
|
|
19361
|
+
opts.onEvent?.({
|
|
19362
|
+
type: "progress",
|
|
19363
|
+
command: "upload",
|
|
19364
|
+
origin: this.auth.origin,
|
|
19365
|
+
filePath: relative,
|
|
19366
|
+
recordingId: init.id,
|
|
19367
|
+
status: "uploaded",
|
|
19368
|
+
message: `Uploaded \xB7 ${recordingCloudUrl(this.auth.origin, init.id)}`
|
|
19369
|
+
});
|
|
19320
19370
|
let jobId;
|
|
19321
19371
|
let transcriptId;
|
|
19322
19372
|
let status = "ready";
|
|
@@ -19324,6 +19374,7 @@ var RecappiApiClient = class {
|
|
|
19324
19374
|
opts.onEvent?.({
|
|
19325
19375
|
type: "progress",
|
|
19326
19376
|
command: "upload",
|
|
19377
|
+
origin: this.auth.origin,
|
|
19327
19378
|
filePath: relative,
|
|
19328
19379
|
recordingId: init.id,
|
|
19329
19380
|
status: "starting_transcription",
|
|
@@ -19341,6 +19392,17 @@ var RecappiApiClient = class {
|
|
|
19341
19392
|
jobId = transcribe.jobId;
|
|
19342
19393
|
status = transcribe.status;
|
|
19343
19394
|
if (transcribe.transcriptId) transcriptId = transcribe.transcriptId;
|
|
19395
|
+
opts.onEvent?.({
|
|
19396
|
+
type: "progress",
|
|
19397
|
+
command: "upload",
|
|
19398
|
+
origin: this.auth.origin,
|
|
19399
|
+
filePath: relative,
|
|
19400
|
+
recordingId: init.id,
|
|
19401
|
+
jobId,
|
|
19402
|
+
status,
|
|
19403
|
+
...transcriptId ? { transcriptId } : {},
|
|
19404
|
+
message: status === "succeeded" ? "Transcription already ready" : "Transcription queued"
|
|
19405
|
+
});
|
|
19344
19406
|
if (opts.wait) {
|
|
19345
19407
|
const waited = await this.waitForJob(jobId, {
|
|
19346
19408
|
onEvent: (event) => opts.onEvent?.({
|
|
@@ -19411,14 +19473,27 @@ var RecappiApiClient = class {
|
|
|
19411
19473
|
if (typeof parsed.id !== "string" || typeof parsed.status !== "string") {
|
|
19412
19474
|
throw cliError("cloud.invalid_response", "Job response was missing id or status.");
|
|
19413
19475
|
}
|
|
19476
|
+
const recording = isRecord2(parsed.recording) ? parsed.recording : void 0;
|
|
19477
|
+
const processedDurationMs = typeof parsed.processedDurationMs === "number" || parsed.processedDurationMs === null ? parsed.processedDurationMs : void 0;
|
|
19478
|
+
const recordingDurationMs = recording && (typeof recording.durationMs === "number" || recording.durationMs === null) ? recording.durationMs : void 0;
|
|
19479
|
+
const progressPercent = jobProgressPercent(parsed, processedDurationMs, recordingDurationMs);
|
|
19414
19480
|
return {
|
|
19415
19481
|
jobId: parsed.id,
|
|
19482
|
+
origin: this.auth.origin,
|
|
19416
19483
|
...typeof parsed.recordingId === "string" ? { recordingId: parsed.recordingId } : {},
|
|
19417
19484
|
status: parsed.status,
|
|
19418
19485
|
...typeof parsed.transcriptId === "string" || parsed.transcriptId === null ? { transcriptId: parsed.transcriptId } : {},
|
|
19419
19486
|
...typeof parsed.provider === "string" ? { provider: parsed.provider } : {},
|
|
19420
19487
|
...typeof parsed.model === "string" ? { model: parsed.model } : {},
|
|
19421
|
-
...typeof parsed.language === "string" || parsed.language === null ? { language: parsed.language } : {}
|
|
19488
|
+
...typeof parsed.language === "string" || parsed.language === null ? { language: parsed.language } : {},
|
|
19489
|
+
...progressPercent !== void 0 ? { progressPercent } : {},
|
|
19490
|
+
...processedDurationMs !== void 0 ? { processedDurationMs } : {},
|
|
19491
|
+
...recording ? {
|
|
19492
|
+
recording: {
|
|
19493
|
+
...typeof recording.title === "string" || recording.title === null ? { title: recording.title } : {},
|
|
19494
|
+
...recordingDurationMs !== void 0 ? { durationMs: recordingDurationMs } : {}
|
|
19495
|
+
}
|
|
19496
|
+
} : {}
|
|
19422
19497
|
};
|
|
19423
19498
|
}
|
|
19424
19499
|
async getJson(path6) {
|
|
@@ -19737,6 +19812,21 @@ function stringValue2(value) {
|
|
|
19737
19812
|
function numberValue2(value) {
|
|
19738
19813
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
19739
19814
|
}
|
|
19815
|
+
function recordingCloudUrl(origin, recordingId) {
|
|
19816
|
+
return `${origin.replace(/\/+$/, "")}/recordings/${encodeURIComponent(recordingId)}`;
|
|
19817
|
+
}
|
|
19818
|
+
function jobProgressPercent(row, processedDurationMs, recordingDurationMs) {
|
|
19819
|
+
const chunkProgress = isRecord2(row.chunkProgress) ? row.chunkProgress : void 0;
|
|
19820
|
+
const chunkPercent = numberValue2(chunkProgress?.percent);
|
|
19821
|
+
if (chunkPercent !== void 0) return clampPercent(chunkPercent);
|
|
19822
|
+
if (typeof processedDurationMs === "number" && typeof recordingDurationMs === "number" && recordingDurationMs > 0) {
|
|
19823
|
+
return clampPercent(processedDurationMs / recordingDurationMs * 100);
|
|
19824
|
+
}
|
|
19825
|
+
return void 0;
|
|
19826
|
+
}
|
|
19827
|
+
function clampPercent(value) {
|
|
19828
|
+
return Math.max(0, Math.min(100, Math.round(value)));
|
|
19829
|
+
}
|
|
19740
19830
|
function nullableCap(value) {
|
|
19741
19831
|
if (value === null) return null;
|
|
19742
19832
|
const number4 = numberValue2(value);
|
|
@@ -20176,8 +20266,11 @@ function renderFailure(command, error51, opts, data) {
|
|
|
20176
20266
|
for (const item of data.failures) {
|
|
20177
20267
|
const label = humanFileLabel(item.filePath) ?? item.filePath;
|
|
20178
20268
|
opts.stderr(` ${label}: ${item.error.message} (${item.error.code})
|
|
20269
|
+
`);
|
|
20270
|
+
if (item.error.hint) opts.stderr(` ${item.error.hint}
|
|
20179
20271
|
`);
|
|
20180
20272
|
}
|
|
20273
|
+
return;
|
|
20181
20274
|
}
|
|
20182
20275
|
if (error51.hint) opts.stderr(`${error51.hint}
|
|
20183
20276
|
`);
|
|
@@ -20191,7 +20284,10 @@ function renderEvent(event, opts) {
|
|
|
20191
20284
|
}
|
|
20192
20285
|
if ((event.type === "started" || event.type === "progress") && opts.mode === "human") {
|
|
20193
20286
|
const line = formatHumanProgress(event, opts);
|
|
20194
|
-
if (line)
|
|
20287
|
+
if (line) {
|
|
20288
|
+
if (isPersistentProgressEvent(event)) writePersistentHumanProgress(line, opts);
|
|
20289
|
+
else writeHumanProgress(line, opts);
|
|
20290
|
+
}
|
|
20195
20291
|
}
|
|
20196
20292
|
}
|
|
20197
20293
|
function renderEnvelope(envelope, opts) {
|
|
@@ -20355,10 +20451,12 @@ Next:
|
|
|
20355
20451
|
}
|
|
20356
20452
|
if (command === "upload" && isUploadBatch(data)) {
|
|
20357
20453
|
if (data.successes.length > 0) {
|
|
20358
|
-
opts.stdout(data.successes
|
|
20454
|
+
opts.stdout(uploadSuccessHeading(data.successes));
|
|
20359
20455
|
}
|
|
20360
20456
|
for (const item of data.successes) {
|
|
20361
20457
|
opts.stdout(` recordingId: ${item.recordingId}
|
|
20458
|
+
`);
|
|
20459
|
+
opts.stdout(` recordingUrl: ${uploadRecordingUrl(item)}
|
|
20362
20460
|
`);
|
|
20363
20461
|
if (item.jobId) opts.stdout(` jobId: ${item.jobId}
|
|
20364
20462
|
`);
|
|
@@ -20450,7 +20548,11 @@ Next:
|
|
|
20450
20548
|
}
|
|
20451
20549
|
if ((command === "jobs wait" || command === "upload") && isRecord4(data)) {
|
|
20452
20550
|
if (typeof data.transcriptId === "string") {
|
|
20453
|
-
opts.stdout("
|
|
20551
|
+
opts.stdout("Transcript ready\n");
|
|
20552
|
+
if (typeof data.recordingId === "string" && typeof data.origin === "string") {
|
|
20553
|
+
opts.stdout(` recordingUrl: ${jobRecordingUrl(data.origin, data.recordingId, data.jobId)}
|
|
20554
|
+
`);
|
|
20555
|
+
}
|
|
20454
20556
|
opts.stdout(` transcriptId: ${data.transcriptId}
|
|
20455
20557
|
`);
|
|
20456
20558
|
opts.stdout(`
|
|
@@ -20529,17 +20631,20 @@ function formatUploadProgress(event, opts, scope) {
|
|
|
20529
20631
|
return `Uploading${event.filePath ? ` ${humanFileLabel(event.filePath)}` : ""}: ${event.percent}%`;
|
|
20530
20632
|
}
|
|
20531
20633
|
if (event.status === "finishing_upload") return "Finalizing upload";
|
|
20634
|
+
if (event.status === "uploaded") {
|
|
20635
|
+
return event.message ?? uploadedLine(event.origin, event.recordingId);
|
|
20636
|
+
}
|
|
20532
20637
|
if (event.status === "starting_transcription") return "Starting transcription";
|
|
20533
20638
|
return formatJobProgress(event);
|
|
20534
20639
|
}
|
|
20535
20640
|
function formatJobProgress(event) {
|
|
20536
20641
|
switch (event.status) {
|
|
20537
20642
|
case "queued":
|
|
20538
|
-
return "Waiting for transcription";
|
|
20643
|
+
return event.jobId ? `Waiting for transcription (job ${event.jobId})` : "Waiting for transcription";
|
|
20539
20644
|
case "running":
|
|
20540
|
-
return "Transcribing";
|
|
20645
|
+
return typeof event.percent === "number" ? `Transcribing: ${event.percent}%` : "Transcribing\u2026";
|
|
20541
20646
|
case "succeeded":
|
|
20542
|
-
return "
|
|
20647
|
+
return "\u2713 Transcript ready";
|
|
20543
20648
|
case "failed":
|
|
20544
20649
|
return "Transcription failed";
|
|
20545
20650
|
default:
|
|
@@ -20557,6 +20662,11 @@ function writeHumanProgress(line, opts) {
|
|
|
20557
20662
|
opts.stderr(`\r${line}${padding}`);
|
|
20558
20663
|
state.activeLineLength = line.length;
|
|
20559
20664
|
}
|
|
20665
|
+
function writePersistentHumanProgress(line, opts) {
|
|
20666
|
+
finishHumanProgress(opts);
|
|
20667
|
+
opts.stderr(`${line}
|
|
20668
|
+
`);
|
|
20669
|
+
}
|
|
20560
20670
|
function finishHumanProgress(opts) {
|
|
20561
20671
|
const state = opts.progress;
|
|
20562
20672
|
if (!state?.interactive || state.activeLineLength === 0) return;
|
|
@@ -20566,11 +20676,32 @@ function finishHumanProgress(opts) {
|
|
|
20566
20676
|
function progressScope(event) {
|
|
20567
20677
|
return [event.command, event.filePath, event.recordingId, event.jobId].filter(Boolean).join(":");
|
|
20568
20678
|
}
|
|
20679
|
+
function isPersistentProgressEvent(event) {
|
|
20680
|
+
return event.command === "upload" && event.status === "uploaded";
|
|
20681
|
+
}
|
|
20569
20682
|
function humanFileLabel(filePath) {
|
|
20570
20683
|
if (!filePath) return void 0;
|
|
20571
20684
|
const normalized = filePath.replaceAll("\\", "/");
|
|
20572
20685
|
return normalized.split("/").filter(Boolean).at(-1) ?? filePath;
|
|
20573
20686
|
}
|
|
20687
|
+
function uploadRecordingUrl(item) {
|
|
20688
|
+
return jobRecordingUrl(item.origin, item.recordingId, item.jobId);
|
|
20689
|
+
}
|
|
20690
|
+
function uploadSuccessHeading(successes) {
|
|
20691
|
+
const transcriptReady = successes.length > 0 && successes.every((item) => Boolean(item.transcriptId));
|
|
20692
|
+
if (transcriptReady) return successes.length === 1 ? "\u2713 Transcript ready\n" : "\u2713 Transcripts ready\n";
|
|
20693
|
+
return successes.length === 1 ? "Upload complete\n" : "Uploads complete\n";
|
|
20694
|
+
}
|
|
20695
|
+
function jobRecordingUrl(origin, recordingId, jobId) {
|
|
20696
|
+
const base = recordingCloudUrl2(origin, recordingId);
|
|
20697
|
+
return typeof jobId === "string" ? `${base}?job=${encodeURIComponent(jobId)}` : base;
|
|
20698
|
+
}
|
|
20699
|
+
function uploadedLine(origin, recordingId) {
|
|
20700
|
+
return origin && recordingId ? `Uploaded \xB7 ${recordingCloudUrl2(origin, recordingId)}` : "Uploaded";
|
|
20701
|
+
}
|
|
20702
|
+
function recordingCloudUrl2(origin, recordingId) {
|
|
20703
|
+
return `${origin.replace(/\/+$/, "")}/recordings/${encodeURIComponent(recordingId)}`;
|
|
20704
|
+
}
|
|
20574
20705
|
function renderTranscriptHuman(data, opts) {
|
|
20575
20706
|
const segments = Array.isArray(data.segments) ? data.segments : [];
|
|
20576
20707
|
let printedBody = false;
|