recappi 0.1.57 → 0.1.59

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 CHANGED
@@ -18532,45 +18532,24 @@ function contentTypeForPath(filePath) {
18532
18532
  const fromExtension = EXT_TO_CONTENT_TYPE[path2.extname(filePath).toLowerCase()];
18533
18533
  return fromExtension ? normalizeAudioType(fromExtension) : null;
18534
18534
  }
18535
- async function collectAudioFiles(inputPath) {
18535
+ async function planAudioFile(filePath, titleOverride) {
18536
18536
  let stat;
18537
18537
  try {
18538
- stat = await fs2.stat(inputPath);
18538
+ stat = await fs2.stat(filePath);
18539
18539
  } catch {
18540
- throw cliError("input.not_found", `Path not found: ${inputPath}`);
18541
- }
18542
- if (stat.isFile()) return [inputPath];
18543
- if (!stat.isDirectory()) {
18544
- throw cliError("input.not_file", `Path is not a file or directory: ${inputPath}`);
18540
+ throw cliError("input.not_found", `Path not found: ${filePath}`);
18545
18541
  }
18546
- const found = [];
18547
- await walk(inputPath, found);
18548
- found.sort((a, b) => a.localeCompare(b));
18549
- return found;
18550
- }
18551
- async function walk(dir, found) {
18552
- const entries = await fs2.readdir(dir, { withFileTypes: true });
18553
- entries.sort((a, b) => a.name.localeCompare(b.name));
18554
- for (const entry of entries) {
18555
- const fullPath = path2.join(dir, entry.name);
18556
- if (entry.isDirectory()) {
18557
- await walk(fullPath, found);
18558
- } else if (entry.isFile() && contentTypeForPath(fullPath)) {
18559
- found.push(fullPath);
18560
- }
18542
+ if (!stat.isFile()) {
18543
+ throw cliError("input.not_file", `Path is not a file: ${filePath}`, {
18544
+ hint: "Pass one or more audio files explicitly. For a folder, expand a shell glob such as recappi upload ./recordings/*.m4a."
18545
+ });
18561
18546
  }
18562
- }
18563
- async function planAudioFile(filePath, titleOverride) {
18564
18547
  const contentType = contentTypeForPath(filePath);
18565
18548
  if (!contentType) {
18566
18549
  throw cliError("input.unsupported_audio", `Unsupported audio file: ${filePath}`, {
18567
18550
  hint: "Supported extensions: wav, mp3, aiff, aac, m4a, ogg, flac."
18568
18551
  });
18569
18552
  }
18570
- const stat = await fs2.stat(filePath);
18571
- if (!stat.isFile()) {
18572
- throw cliError("input.not_file", `Path is not a file: ${filePath}`);
18573
- }
18574
18553
  const title = titleOverride ?? path2.basename(filePath, path2.extname(filePath));
18575
18554
  const durationMs = await readDurationMs(filePath, contentType);
18576
18555
  return {
@@ -19245,13 +19224,13 @@ var RecappiApiClient = class {
19245
19224
  });
19246
19225
  }
19247
19226
  async uploadPathBatch(opts) {
19248
- const files = await collectAudioFiles(opts.inputPath);
19227
+ const files = opts.inputPaths;
19249
19228
  if (files.length === 0) {
19250
19229
  throw cliError(
19251
- "input.unsupported_audio",
19252
- `No supported audio files found: ${opts.inputPath}`,
19230
+ "usage.invalid_argument",
19231
+ "Missing upload file path.",
19253
19232
  {
19254
- hint: "Supported extensions: wav, mp3, aiff, aac, m4a, ogg, flac."
19233
+ hint: "Pass one or more audio files, e.g. recappi upload talk.m4a notes.wav."
19255
19234
  }
19256
19235
  );
19257
19236
  }
@@ -19984,18 +19963,22 @@ var COMMAND_METADATA = {
19984
19963
  },
19985
19964
  upload: {
19986
19965
  capabilities: [
19987
- "Upload a local audio file or directory",
19966
+ "Upload one or more local audio files",
19988
19967
  "Transcribe uploaded audio",
19989
19968
  "Wait for transcription to finish"
19990
19969
  ],
19991
19970
  examples: [
19992
19971
  {
19993
- description: "Upload a local audio file and wait for transcription",
19972
+ description: "Upload a file and wait for transcription",
19994
19973
  command: "recappi upload talk.m4a --transcribe --wait"
19995
19974
  },
19996
19975
  {
19997
- description: "Upload and transcribe a directory of audio files",
19998
- command: "recappi upload ./recordings --transcribe"
19976
+ description: "Upload several files at once",
19977
+ command: "recappi upload intro.m4a talk.wav qa.m4a --transcribe"
19978
+ },
19979
+ {
19980
+ description: "Upload a whole folder via shell glob",
19981
+ command: "recappi upload ./recordings/*.m4a --transcribe"
19999
19982
  },
20000
19983
  {
20001
19984
  description: "Transcribe with title and language hints",
@@ -20170,6 +20153,14 @@ function renderFailure(command, error51, opts, data) {
20170
20153
  finishHumanProgress(opts);
20171
20154
  opts.stderr(`recappi: ${error51.message}
20172
20155
  `);
20156
+ if (command === "upload" && isUploadBatch(data) && data.failures.length > 0) {
20157
+ opts.stderr("Failures:\n");
20158
+ for (const item of data.failures) {
20159
+ const label = humanFileLabel(item.filePath) ?? item.filePath;
20160
+ opts.stderr(` ${label}: ${item.error.message} (${item.error.code})
20161
+ `);
20162
+ }
20163
+ }
20173
20164
  if (error51.hint) opts.stderr(`${error51.hint}
20174
20165
  `);
20175
20166
  }
@@ -20813,6 +20804,7 @@ function argumentDocs(command) {
20813
20804
  return args.map((arg) => ({
20814
20805
  name: arg.name(),
20815
20806
  required: arg.required === true,
20807
+ ...arg.variadic === true ? { variadic: true } : {},
20816
20808
  ...arg.description ? { description: arg.description } : {}
20817
20809
  }));
20818
20810
  }
@@ -22059,7 +22051,7 @@ async function uploadRecordedSessionAfterStop(client, data, opts = {}) {
22059
22051
  if (!artifact.audioPath) return data;
22060
22052
  try {
22061
22053
  const upload = await client.uploadPathBatch({
22062
- inputPath: artifact.audioPath,
22054
+ inputPaths: [artifact.audioPath],
22063
22055
  transcribe: true,
22064
22056
  wait: false,
22065
22057
  ...opts.title ? { title: opts.title } : {},
@@ -22197,7 +22189,7 @@ async function runCli(deps = {}) {
22197
22189
  throw cliError("input.not_found", "No local audio file is available to transcribe.");
22198
22190
  }
22199
22191
  const data = await client.uploadPathBatch({
22200
- inputPath: artifact.audioPath,
22192
+ inputPaths: [artifact.audioPath],
22201
22193
  transcribe: true,
22202
22194
  wait: false,
22203
22195
  onEvent
@@ -22276,7 +22268,7 @@ async function runCli(deps = {}) {
22276
22268
  }
22277
22269
  if (parsed.kind === "upload") {
22278
22270
  const data = await client.uploadPathBatch({
22279
- inputPath: parsed.path,
22271
+ inputPaths: parsed.paths,
22280
22272
  title: parsed.title,
22281
22273
  transcribe: parsed.transcribe,
22282
22274
  wait: parsed.wait,
@@ -22571,11 +22563,6 @@ function buildProgram({ onHelpOutput, onSelect }) {
22571
22563
  `
22572
22564
  ${commonTasksHelpText()}
22573
22565
 
22574
- Transcribe:
22575
- Local audio file recappi upload <file> --transcribe --wait
22576
- Existing cloud recording recappi recordings retranscribe <recordingId> --wait
22577
- (the "transcript" command only fetches an existing transcript by id.)
22578
-
22579
22566
  Agent mode:
22580
22567
  Non-TTY stdout defaults to JSON. Progress and human diagnostics go to stderr.
22581
22568
  Use --json for a single envelope or --jsonl for a terminal event stream.
@@ -22643,14 +22630,14 @@ Agent mode:
22643
22630
  commandName: "account status"
22644
22631
  });
22645
22632
  });
22646
- const upload = program.command("upload <file-or-dir>").description("Upload a local audio file or directory (optionally transcribe)").option("--title <title>", "recording title", parseStringOption("--title")).option("--transcribe", "start transcription after upload").option("--wait", "wait for the transcription job to reach a terminal state").option("--language <lang>", "transcription language hint", parseStringOption("--language")).option("--provider <name>", "transcription provider", parseStringOption("--provider")).option("--prompt <text>", "transcription prompt/context", parseStringOption("--prompt")).option("--force", "force upload if a conflict is retryable").addHelpText("after", commandMetadataHelpText("upload"));
22633
+ const upload = program.command("upload <files...>").description("Upload one or more local audio files (optionally transcribe)").option("--title <title>", "recording title", parseStringOption("--title")).option("--transcribe", "start transcription after upload").option("--wait", "wait for the transcription job to reach a terminal state").option("--language <lang>", "transcription language hint", parseStringOption("--language")).option("--provider <name>", "transcription provider", parseStringOption("--provider")).option("--prompt <text>", "transcription prompt/context", parseStringOption("--prompt")).option("--force", "force upload if a conflict is retryable").addHelpText("after", commandMetadataHelpText("upload"));
22647
22634
  addCommonOptions(upload);
22648
- upload.action((inputPath, opts, command) => {
22635
+ upload.action((inputPaths, opts, command) => {
22649
22636
  onSelect({
22650
22637
  kind: "upload",
22651
22638
  options: collectGlobalOptions(command),
22652
22639
  commandName: "upload",
22653
- path: inputPath,
22640
+ paths: inputPaths,
22654
22641
  ...typeof opts.title === "string" ? { title: opts.title } : {},
22655
22642
  ...opts.transcribe === true ? { transcribe: true } : {},
22656
22643
  ...opts.wait === true ? { wait: true, transcribe: true } : {},