recappi 0.1.69 → 0.1.71
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 +116 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -150,6 +150,12 @@ function transcribeFraction(item) {
|
|
|
150
150
|
if (!total || total <= 0 || done == null) return null;
|
|
151
151
|
return Math.max(0, Math.min(1, done / total));
|
|
152
152
|
}
|
|
153
|
+
function isJobStalled(item, nowMs) {
|
|
154
|
+
return item.status === "running" && typeof item.claimExpiresAt === "number" && item.claimExpiresAt < nowMs;
|
|
155
|
+
}
|
|
156
|
+
function effectiveJobStatus(item, nowMs) {
|
|
157
|
+
return isJobStalled(item, nowMs) ? "stalled" : item.status;
|
|
158
|
+
}
|
|
153
159
|
function statusStyle(status) {
|
|
154
160
|
switch (status) {
|
|
155
161
|
case "running":
|
|
@@ -160,6 +166,8 @@ function statusStyle(status) {
|
|
|
160
166
|
return { label: "Ready", color: "green" };
|
|
161
167
|
case "failed":
|
|
162
168
|
return { label: "Failed", color: "red" };
|
|
169
|
+
case "stalled":
|
|
170
|
+
return { label: "Stalled", color: "yellow" };
|
|
163
171
|
default:
|
|
164
172
|
return { label: status, color: "gray" };
|
|
165
173
|
}
|
|
@@ -191,11 +199,14 @@ function statusGlyph(status, spinnerFrame) {
|
|
|
191
199
|
return "\u2713";
|
|
192
200
|
case "failed":
|
|
193
201
|
return "\u2717";
|
|
202
|
+
case "stalled":
|
|
203
|
+
return "!";
|
|
194
204
|
default:
|
|
195
205
|
return "\u2022";
|
|
196
206
|
}
|
|
197
207
|
}
|
|
198
|
-
function jobDetail(item) {
|
|
208
|
+
function jobDetail(item, nowMs) {
|
|
209
|
+
if (nowMs != null && isJobStalled(item, nowMs)) return "stalled \u2014 worker lost \xB7 T retry";
|
|
199
210
|
if (item.status === "running") {
|
|
200
211
|
const fraction = transcribeFraction(item);
|
|
201
212
|
if (fraction != null) {
|
|
@@ -735,17 +746,19 @@ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
|
735
746
|
function JobRow({
|
|
736
747
|
item,
|
|
737
748
|
selected,
|
|
738
|
-
spinnerFrame
|
|
749
|
+
spinnerFrame,
|
|
750
|
+
nowMs
|
|
739
751
|
}) {
|
|
740
|
-
const
|
|
741
|
-
const
|
|
752
|
+
const status = nowMs != null ? effectiveJobStatus(item, nowMs) : item.status;
|
|
753
|
+
const style = statusStyle(status);
|
|
754
|
+
const glyph = statusGlyph(status, spinnerFrame);
|
|
742
755
|
const title = item.recording?.title ?? item.recordingId;
|
|
743
756
|
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
744
757
|
/* @__PURE__ */ jsx7(Box5, { width: 3, children: /* @__PURE__ */ jsx7(Text5, { color: "cyan", children: selected ? "\u25B8" : "" }) }),
|
|
745
758
|
/* @__PURE__ */ jsx7(Box5, { width: 2, children: /* @__PURE__ */ jsx7(Text5, { color: style.color, children: glyph }) }),
|
|
746
759
|
/* @__PURE__ */ jsx7(Box5, { width: 13, children: /* @__PURE__ */ jsx7(Text5, { color: style.color, children: style.label }) }),
|
|
747
760
|
/* @__PURE__ */ jsx7(Box5, { width: 26, children: /* @__PURE__ */ jsx7(Text5, { bold: selected, wrap: "truncate-end", children: title }) }),
|
|
748
|
-
/* @__PURE__ */ jsx7(Text5, { dimColor: !selected, children: jobDetail(item) })
|
|
761
|
+
/* @__PURE__ */ jsx7(Text5, { dimColor: !selected, children: jobDetail(item, nowMs) })
|
|
749
762
|
] });
|
|
750
763
|
}
|
|
751
764
|
var init_JobRow = __esm({
|
|
@@ -761,7 +774,8 @@ import { jsx as jsx8 } from "react/jsx-runtime";
|
|
|
761
774
|
function JobsView({
|
|
762
775
|
items,
|
|
763
776
|
selectedIndex,
|
|
764
|
-
spinnerFrame
|
|
777
|
+
spinnerFrame,
|
|
778
|
+
nowMs
|
|
765
779
|
}) {
|
|
766
780
|
if (items.length === 0) {
|
|
767
781
|
return /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: "No transcription jobs yet \u2014 run: recappi upload <file> --transcribe" }) });
|
|
@@ -771,7 +785,8 @@ function JobsView({
|
|
|
771
785
|
{
|
|
772
786
|
item,
|
|
773
787
|
selected: index === selectedIndex,
|
|
774
|
-
spinnerFrame
|
|
788
|
+
spinnerFrame,
|
|
789
|
+
nowMs
|
|
775
790
|
},
|
|
776
791
|
item.jobId
|
|
777
792
|
)) });
|
|
@@ -794,6 +809,7 @@ function recordingTitle2(item) {
|
|
|
794
809
|
function recordingProcessingState(item, jobStatus, spinnerFrame) {
|
|
795
810
|
if (item.status === "uploading") return { glyph: "\u2191", color: "cyan" };
|
|
796
811
|
if (item.status === "failed" || jobStatus === "failed") return { glyph: "\u2717", color: "red" };
|
|
812
|
+
if (jobStatus === "stalled") return { glyph: "!", color: "yellow" };
|
|
797
813
|
if (jobStatus === "running") return { glyph: spinnerChar(spinnerFrame), color: "cyan" };
|
|
798
814
|
if (jobStatus === "queued") return { glyph: "\u25CB", color: "yellow" };
|
|
799
815
|
if (item.status === "aborted") return { glyph: "\u2022", color: "gray" };
|
|
@@ -1019,7 +1035,8 @@ function JobDetailView({
|
|
|
1019
1035
|
spinnerFrame,
|
|
1020
1036
|
nowMs
|
|
1021
1037
|
}) {
|
|
1022
|
-
const
|
|
1038
|
+
const status = effectiveJobStatus(item, nowMs);
|
|
1039
|
+
const style = statusStyle(status);
|
|
1023
1040
|
const links = resolveJobLinks(item, origin);
|
|
1024
1041
|
const title = item.recording?.title ?? item.recordingId;
|
|
1025
1042
|
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingX: 1, children: [
|
|
@@ -1067,10 +1084,10 @@ function JobDetailView({
|
|
|
1067
1084
|
/* @__PURE__ */ jsx13(
|
|
1068
1085
|
TimelineRow,
|
|
1069
1086
|
{
|
|
1070
|
-
label:
|
|
1087
|
+
label: status === "failed" ? "Failed" : status === "stalled" ? "Stalled \u2014 worker lost" : status === "running" ? "Transcribing" : "Finished",
|
|
1071
1088
|
done: item.finishedAt != null,
|
|
1072
1089
|
failed: item.status === "failed",
|
|
1073
|
-
running:
|
|
1090
|
+
running: status === "running",
|
|
1074
1091
|
at: item.finishedAt,
|
|
1075
1092
|
nowMs
|
|
1076
1093
|
}
|
|
@@ -2452,12 +2469,13 @@ function AppShell({
|
|
|
2452
2469
|
const id = setInterval(() => setSpinnerFrame((f) => f + 1), spinnerMs);
|
|
2453
2470
|
return () => clearInterval(id);
|
|
2454
2471
|
}, [hasRunning, loaded, spinnerMs]);
|
|
2455
|
-
const jobRank = (s) => s === "running" ? 4 : s === "queued" ? 3 : s === "failed" ? 2 : s === "succeeded" ? 1 : 0;
|
|
2472
|
+
const jobRank = (s) => s === "running" ? 5 : s === "stalled" ? 4 : s === "queued" ? 3 : s === "failed" ? 2 : s === "succeeded" ? 1 : 0;
|
|
2456
2473
|
const jobStatusByRecording = /* @__PURE__ */ new Map();
|
|
2457
2474
|
for (const job of jobs) {
|
|
2475
|
+
const status = effectiveJobStatus(job, now());
|
|
2458
2476
|
const prev = jobStatusByRecording.get(job.recordingId);
|
|
2459
|
-
if (!prev || jobRank(
|
|
2460
|
-
jobStatusByRecording.set(job.recordingId,
|
|
2477
|
+
if (!prev || jobRank(status) > jobRank(prev)) {
|
|
2478
|
+
jobStatusByRecording.set(job.recordingId, status);
|
|
2461
2479
|
}
|
|
2462
2480
|
}
|
|
2463
2481
|
useEffect4(() => {
|
|
@@ -2804,7 +2822,8 @@ function AppShell({
|
|
|
2804
2822
|
{
|
|
2805
2823
|
items: jobs.slice(win.start, win.end),
|
|
2806
2824
|
selectedIndex: selected - win.start,
|
|
2807
|
-
spinnerFrame
|
|
2825
|
+
spinnerFrame,
|
|
2826
|
+
nowMs: now()
|
|
2808
2827
|
}
|
|
2809
2828
|
);
|
|
2810
2829
|
}
|
|
@@ -2916,6 +2935,7 @@ async function runDashboard(deps) {
|
|
|
2916
2935
|
startRecordSetupPreview: deps.startRecordSetupPreview,
|
|
2917
2936
|
transcribeRecordingArtifact: deps.transcribeRecordingArtifact,
|
|
2918
2937
|
onRetranscribe: deps.retranscribeRecording,
|
|
2938
|
+
onResummarize: deps.resummarizeRecording,
|
|
2919
2939
|
initialView: deps.initialView ?? "overview",
|
|
2920
2940
|
openUrl,
|
|
2921
2941
|
copyText
|
|
@@ -18009,6 +18029,12 @@ var summaryStatusSchema = external_exports.enum([
|
|
|
18009
18029
|
"failed",
|
|
18010
18030
|
"skipped"
|
|
18011
18031
|
]);
|
|
18032
|
+
var recordingSummarizeDataSchema = external_exports.object({
|
|
18033
|
+
origin: external_exports.string(),
|
|
18034
|
+
recordingId: external_exports.string(),
|
|
18035
|
+
transcriptId: external_exports.string(),
|
|
18036
|
+
summaryStatus: summaryStatusSchema
|
|
18037
|
+
});
|
|
18012
18038
|
var summaryActionItemSchema = external_exports.object({
|
|
18013
18039
|
who: external_exports.string().optional(),
|
|
18014
18040
|
what: external_exports.string()
|
|
@@ -19282,6 +19308,22 @@ var RecappiApiClient = class {
|
|
|
19282
19308
|
}
|
|
19283
19309
|
return result;
|
|
19284
19310
|
}
|
|
19311
|
+
async summarizeRecording(opts) {
|
|
19312
|
+
const hasPrompt = Boolean(opts.prompt?.trim());
|
|
19313
|
+
const parsed = await this.postJson(
|
|
19314
|
+
`/api/recordings/${encodeURIComponent(opts.recordingId)}/summarize`,
|
|
19315
|
+
{
|
|
19316
|
+
...hasPrompt ? { prompt: opts.prompt } : {},
|
|
19317
|
+
...opts.model ? { model: opts.model } : {}
|
|
19318
|
+
}
|
|
19319
|
+
);
|
|
19320
|
+
return recordingSummarizeDataSchema.parse({
|
|
19321
|
+
origin: this.auth.origin,
|
|
19322
|
+
recordingId: opts.recordingId,
|
|
19323
|
+
transcriptId: parsed.transcriptId,
|
|
19324
|
+
summaryStatus: parsed.summaryStatus
|
|
19325
|
+
});
|
|
19326
|
+
}
|
|
19285
19327
|
async downloadRecordingAudio(recordingId, opts = {}) {
|
|
19286
19328
|
const response = await this.request(
|
|
19287
19329
|
"GET",
|
|
@@ -20141,6 +20183,7 @@ var COMMON_TASKS = [
|
|
|
20141
20183
|
{ label: "Record audio + live captions", command: "recappi record --live" },
|
|
20142
20184
|
{ label: "Transcribe a local file", command: "recappi upload <file> --transcribe --wait" },
|
|
20143
20185
|
{ label: "Re-transcribe a recording", command: "recappi recordings retranscribe <recordingId> --wait" },
|
|
20186
|
+
{ label: "Re-summarize a recording", command: "recappi recordings resummarize <recordingId>" },
|
|
20144
20187
|
{ label: "List / find recordings", command: "recappi recordings list" },
|
|
20145
20188
|
{ label: "Read a transcript", command: "recappi transcript get <transcriptId>" },
|
|
20146
20189
|
{ label: "Download / open audio", command: "recappi audio <recordingId> --open" },
|
|
@@ -20271,6 +20314,20 @@ var COMMAND_METADATA = {
|
|
|
20271
20314
|
],
|
|
20272
20315
|
relatedCommands: ["jobs wait", "transcript get"]
|
|
20273
20316
|
},
|
|
20317
|
+
"recordings resummarize": {
|
|
20318
|
+
capabilities: ["Retry or regenerate the summary for an existing recording", "Re-summarize with a custom prompt/model"],
|
|
20319
|
+
examples: [
|
|
20320
|
+
{
|
|
20321
|
+
description: "Retry summary generation for the active transcript",
|
|
20322
|
+
command: "recappi recordings resummarize <recordingId>"
|
|
20323
|
+
},
|
|
20324
|
+
{
|
|
20325
|
+
description: "Re-summarize with custom context",
|
|
20326
|
+
command: 'recappi recordings resummarize <recordingId> --prompt "focus on action items"'
|
|
20327
|
+
}
|
|
20328
|
+
],
|
|
20329
|
+
relatedCommands: ["recordings get", "transcript get"]
|
|
20330
|
+
},
|
|
20274
20331
|
"transcript get": {
|
|
20275
20332
|
capabilities: ["Fetch a finished transcript by id"],
|
|
20276
20333
|
examples: [{ description: "Fetch an existing transcript", command: "recappi transcript get <transcriptId>" }],
|
|
@@ -20831,6 +20888,24 @@ Next:
|
|
|
20831
20888
|
opts.stdout(`
|
|
20832
20889
|
Next:
|
|
20833
20890
|
recappi jobs wait ${data.jobId}
|
|
20891
|
+
`);
|
|
20892
|
+
}
|
|
20893
|
+
return;
|
|
20894
|
+
}
|
|
20895
|
+
if (command === "recordings resummarize" && isRecord4(data)) {
|
|
20896
|
+
opts.stdout("Summary queued\n");
|
|
20897
|
+
if (typeof data.recordingId === "string") opts.stdout(` recordingId: ${data.recordingId}
|
|
20898
|
+
`);
|
|
20899
|
+
if (typeof data.transcriptId === "string") opts.stdout(` transcriptId: ${data.transcriptId}
|
|
20900
|
+
`);
|
|
20901
|
+
if (typeof data.summaryStatus === "string") {
|
|
20902
|
+
opts.stdout(` summaryStatus: ${data.summaryStatus}
|
|
20903
|
+
`);
|
|
20904
|
+
}
|
|
20905
|
+
if (typeof data.transcriptId === "string") {
|
|
20906
|
+
opts.stdout(`
|
|
20907
|
+
Next:
|
|
20908
|
+
recappi transcript get ${data.transcriptId}
|
|
20834
20909
|
`);
|
|
20835
20910
|
}
|
|
20836
20911
|
return;
|
|
@@ -21289,6 +21364,7 @@ var COMMAND_DATA_SCHEMAS = {
|
|
|
21289
21364
|
"recordings get": recordingDataSchema,
|
|
21290
21365
|
"recordings list": recordingListDataSchema,
|
|
21291
21366
|
"recordings retranscribe": recordingTranscribeDataSchema,
|
|
21367
|
+
"recordings resummarize": recordingSummarizeDataSchema,
|
|
21292
21368
|
"jobs list": jobListDataSchema,
|
|
21293
21369
|
"jobs wait": jobDataSchema,
|
|
21294
21370
|
"transcript get": transcriptDataSchema
|
|
@@ -22793,6 +22869,7 @@ async function runCli(deps = {}) {
|
|
|
22793
22869
|
return success2;
|
|
22794
22870
|
},
|
|
22795
22871
|
retranscribeRecording: (recordingId, options = {}) => client.transcribeRecording({ recordingId, ...options }),
|
|
22872
|
+
resummarizeRecording: (recordingId) => client.summarizeRecording({ recordingId }),
|
|
22796
22873
|
initialView: parsed.initialView
|
|
22797
22874
|
});
|
|
22798
22875
|
return 0;
|
|
@@ -23001,6 +23078,15 @@ async function runCli(deps = {}) {
|
|
|
23001
23078
|
renderSuccess("recordings retranscribe", data, render3);
|
|
23002
23079
|
return 0;
|
|
23003
23080
|
}
|
|
23081
|
+
if (parsed.kind === "recordings-resummarize") {
|
|
23082
|
+
const data = await client.summarizeRecording({
|
|
23083
|
+
recordingId: parsed.recordingId,
|
|
23084
|
+
prompt: parsed.prompt,
|
|
23085
|
+
model: parsed.model
|
|
23086
|
+
});
|
|
23087
|
+
renderSuccess("recordings resummarize", data, render3);
|
|
23088
|
+
return 0;
|
|
23089
|
+
}
|
|
23004
23090
|
if (parsed.kind === "dashboard-stats") {
|
|
23005
23091
|
const data = await client.dashboardStats();
|
|
23006
23092
|
renderSuccess("dashboard stats", data, render3);
|
|
@@ -23302,7 +23388,7 @@ Agent mode:
|
|
|
23302
23388
|
commandName: "dashboard stats"
|
|
23303
23389
|
});
|
|
23304
23390
|
});
|
|
23305
|
-
const recordings = program.command("recordings").description("List, fetch, and re-
|
|
23391
|
+
const recordings = program.command("recordings").description("List, fetch, re-transcribe, and re-summarize recordings");
|
|
23306
23392
|
addCommonOptions(recordings);
|
|
23307
23393
|
const recordingsList = recordings.command("list").description("List recent recordings").option("--limit <n>", "number of recordings to show", parseLimitOption("--limit", 1, 100), 20).option("--cursor <cursor>", "pagination cursor", parseStringOption("--cursor")).option("--search <query>", "search recordings and transcripts", parseStringOption("--search")).addHelpText("after", commandMetadataHelpText("recordings list"));
|
|
23308
23394
|
addCommonOptions(recordingsList);
|
|
@@ -23348,6 +23434,21 @@ Agent mode:
|
|
|
23348
23434
|
});
|
|
23349
23435
|
}
|
|
23350
23436
|
);
|
|
23437
|
+
const recordingsResummarize = recordings.command("resummarize <recordingId>").description("Retry or regenerate the summary for an existing recording").option("--model <name>", "summary model", parseStringOption("--model")).option("--prompt <text>", "custom summary prompt/context", parseStringOption("--prompt")).addHelpText("after", commandMetadataHelpText("recordings resummarize"));
|
|
23438
|
+
addCommonOptions(recordingsResummarize);
|
|
23439
|
+
recordingsResummarize.action(
|
|
23440
|
+
(recordingId, _options, command) => {
|
|
23441
|
+
const opts = command.opts();
|
|
23442
|
+
onSelect({
|
|
23443
|
+
kind: "recordings-resummarize",
|
|
23444
|
+
options: collectGlobalOptions(command),
|
|
23445
|
+
commandName: "recordings resummarize",
|
|
23446
|
+
recordingId,
|
|
23447
|
+
...typeof opts.model === "string" ? { model: opts.model } : {},
|
|
23448
|
+
...typeof opts.prompt === "string" ? { prompt: opts.prompt } : {}
|
|
23449
|
+
});
|
|
23450
|
+
}
|
|
23451
|
+
);
|
|
23351
23452
|
const transcript = program.command("transcript").description("Read transcripts (create via upload --transcribe or recordings retranscribe)").addHelpText(
|
|
23352
23453
|
"after",
|
|
23353
23454
|
`
|