recappi 0.1.25 → 0.1.26

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
@@ -1414,12 +1414,12 @@ function statusGlyph2(status) {
1414
1414
  function PermissionPreflightView({
1415
1415
  items
1416
1416
  }) {
1417
- const allGranted = items.length > 0 && items.every((item) => item.status === "granted");
1417
+ const allGranted = items.length > 0 && items.every((item) => item.status === "granted" && !item.requiresProcessRestart);
1418
1418
  return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 1, children: [
1419
1419
  /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "\u2039 Recording permissions" }),
1420
1420
  /* @__PURE__ */ jsx15(Box13, { marginTop: 1, flexDirection: "column", children: items.length === 0 ? /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Checking permissions\u2026" }) : items.map((item) => {
1421
1421
  const status = statusGlyph2(item.status);
1422
- const hint = item.status === "granted" ? void 0 : item.hint ?? DEFAULT_HINTS[item.name];
1422
+ const hint = item.requiresProcessRestart ? "Screen Recording enabled. Run recappi record again to start." : item.status === "granted" ? void 0 : item.hint ?? DEFAULT_HINTS[item.name];
1423
1423
  return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", children: [
1424
1424
  /* @__PURE__ */ jsxs12(Text13, { children: [
1425
1425
  /* @__PURE__ */ jsx15(Text13, { color: status.color, children: status.glyph }),
@@ -1438,8 +1438,8 @@ var init_PermissionPreflightView = __esm({
1438
1438
  "src/tui/PermissionPreflightView.tsx"() {
1439
1439
  "use strict";
1440
1440
  DEFAULT_HINTS = {
1441
- "Screen Recording": "Open System Settings \u203A Privacy & Security \u203A Screen Recording, enable recappi, then recheck.",
1442
- Microphone: "Open System Settings \u203A Privacy & Security \u203A Microphone, enable recappi, then recheck."
1441
+ "Screen Recording": "Open System Settings \u203A Privacy & Security \u203A Screen Recording, enable Recappi Recorder, then run recappi record again.",
1442
+ Microphone: "Open System Settings \u203A Privacy & Security \u203A Microphone, enable Recappi Recorder, then recheck."
1443
1443
  };
1444
1444
  }
1445
1445
  });
@@ -1688,7 +1688,7 @@ function recordErrorCopy(code, message) {
1688
1688
  case "record.permission_required":
1689
1689
  return {
1690
1690
  title: "Recording needs macOS permission first.",
1691
- detail: "Open System Settings > Privacy & Security, allow recording access, then retry.",
1691
+ detail: "Open System Settings > Privacy & Security, allow recording access, then run recappi record again.",
1692
1692
  tone: "yellow"
1693
1693
  };
1694
1694
  case "record.capture_failed":
@@ -1750,8 +1750,16 @@ function permissionItemsFromRecordError(data) {
1750
1750
  const sidecarData = isRecord7(sidecarError?.data) ? sidecarError.data : void 0;
1751
1751
  const permission = typeof sidecarData?.permission === "string" ? sidecarData.permission : "";
1752
1752
  const hint = typeof sidecarData?.recovery === "string" ? sidecarData.recovery : void 0;
1753
+ const requiresProcessRestart = sidecarData?.requiresProcessRestart === true || sidecarData?.requiresProcessRestart === "true";
1753
1754
  const item = permission === "microphone" ? "Microphone" : permission === "screen_recording" ? "Screen Recording" : "Recording";
1754
- return [{ name: item, status: "denied", ...hint ? { hint } : {} }];
1755
+ return [
1756
+ {
1757
+ name: item,
1758
+ status: requiresProcessRestart ? "granted" : "denied",
1759
+ ...hint ? { hint } : {},
1760
+ ...requiresProcessRestart ? { requiresProcessRestart } : {}
1761
+ }
1762
+ ];
1755
1763
  }
1756
1764
  function settingsUrlFromRecordError(data) {
1757
1765
  const sidecarError = isRecord7(data) ? data : void 0;
@@ -17139,7 +17147,8 @@ var sidecarPermissionStatusSchema = external_exports.enum(["granted", "denied",
17139
17147
  var sidecarPermissionItemSchema = external_exports.object({
17140
17148
  name: sidecarPermissionNameSchema,
17141
17149
  status: sidecarPermissionStatusSchema,
17142
- hint: external_exports.string().optional()
17150
+ hint: external_exports.string().optional(),
17151
+ requiresProcessRestart: external_exports.boolean().optional()
17143
17152
  });
17144
17153
  var sidecarPermissionStatusParamsSchema = external_exports.object({
17145
17154
  options: sidecarRecordingOptionsSchema
@@ -17289,10 +17298,11 @@ var sidecarEventSchema = external_exports.discriminatedUnion("type", [
17289
17298
  external_exports.object({
17290
17299
  type: external_exports.literal("audio.level"),
17291
17300
  sessionId: external_exports.string(),
17292
- input: external_exports.enum(["system", "microphone", "mixed"]),
17301
+ input: external_exports.enum(["system", "microphone"]),
17293
17302
  rmsDb: external_exports.number().optional(),
17294
17303
  peakDb: external_exports.number().optional(),
17295
- at: external_exports.number().int().optional()
17304
+ at: external_exports.number().int().optional(),
17305
+ atMs: external_exports.number().int().optional()
17296
17306
  }),
17297
17307
  external_exports.object({
17298
17308
  type: external_exports.literal("live_caption.delta"),
@@ -20049,13 +20059,16 @@ var CLI_VERSION = readCliVersion();
20049
20059
  // src/record.tsx
20050
20060
  import { chmodSync, existsSync, statSync } from "fs";
20051
20061
  import { createRequire as createRequire2 } from "module";
20052
- import { dirname, join } from "path";
20062
+ import { dirname, join as join2 } from "path";
20053
20063
  import { fileURLToPath } from "url";
20054
20064
  import { render, useInput as useInput2 } from "ink";
20055
20065
  init_recordingCore();
20056
20066
 
20057
20067
  // src/sidecar.ts
20058
- import { spawn as spawn2 } from "child_process";
20068
+ import { spawn as spawn2, spawnSync } from "child_process";
20069
+ import { createReadStream, createWriteStream as createWriteStream2, mkdtempSync, rmSync } from "fs";
20070
+ import { tmpdir } from "os";
20071
+ import { join } from "path";
20059
20072
  import { createInterface } from "readline";
20060
20073
  var MiniSidecarClient = class {
20061
20074
  input;
@@ -20248,6 +20261,9 @@ function isRecord6(value) {
20248
20261
  return typeof value === "object" && value !== null && !Array.isArray(value);
20249
20262
  }
20250
20263
  function spawnMiniSidecar(opts) {
20264
+ if (isLaunchServicesAppCommand(opts.command)) {
20265
+ return spawnLaunchServicesSidecar(opts);
20266
+ }
20251
20267
  const spawnProcess = opts.spawnProcess ?? spawn2;
20252
20268
  const child = spawnProcess(opts.command, opts.args ?? [], {
20253
20269
  env: opts.env,
@@ -20272,6 +20288,73 @@ function defaultSidecarHandshakeParams(params) {
20272
20288
  ...params
20273
20289
  };
20274
20290
  }
20291
+ function isLaunchServicesAppCommand(command, platform = process.platform) {
20292
+ return platform === "darwin" && command.endsWith(".app");
20293
+ }
20294
+ function launchServicesOpenArgs(appPath, pipes, sidecarArgs = []) {
20295
+ return [
20296
+ "-W",
20297
+ "-n",
20298
+ "-g",
20299
+ "--stdin",
20300
+ pipes.stdin,
20301
+ "--stdout",
20302
+ pipes.stdout,
20303
+ "--stderr",
20304
+ pipes.stderr,
20305
+ appPath,
20306
+ "--args",
20307
+ ...sidecarArgs
20308
+ ];
20309
+ }
20310
+ function spawnLaunchServicesSidecar(opts) {
20311
+ const spawnProcess = opts.spawnProcess ?? spawn2;
20312
+ const tempDir = mkdtempSync(join(tmpdir(), "recappi-sidecar-"));
20313
+ const pipes = {
20314
+ stdin: join(tempDir, "stdin.fifo"),
20315
+ stdout: join(tempDir, "stdout.fifo"),
20316
+ stderr: join(tempDir, "stderr.log")
20317
+ };
20318
+ createFifo(pipes.stdin);
20319
+ createFifo(pipes.stdout);
20320
+ const output = createReadStream(pipes.stdout);
20321
+ const input = createWriteStream2(pipes.stdin);
20322
+ const child = spawnProcess("open", launchServicesOpenArgs(opts.command, pipes, opts.args ?? []), {
20323
+ env: opts.env,
20324
+ stdio: ["ignore", "ignore", "pipe"]
20325
+ });
20326
+ const client = new MiniSidecarClient({
20327
+ input,
20328
+ output,
20329
+ requestTimeoutMs: opts.requestTimeoutMs
20330
+ });
20331
+ let cleaned = false;
20332
+ const cleanup = () => {
20333
+ if (cleaned) return;
20334
+ cleaned = true;
20335
+ rmSync(tempDir, { recursive: true, force: true });
20336
+ };
20337
+ child.once("exit", cleanup);
20338
+ child.once("error", cleanup);
20339
+ return {
20340
+ client,
20341
+ kill: () => {
20342
+ client.close();
20343
+ input.end();
20344
+ output.destroy();
20345
+ child.kill();
20346
+ cleanup();
20347
+ }
20348
+ };
20349
+ }
20350
+ function createFifo(path6) {
20351
+ const result = spawnSync("mkfifo", [path6], { encoding: "utf8" });
20352
+ if (result.status !== 0) {
20353
+ throw cliError("record.helper_unavailable", "Recappi recording helper could not start.", {
20354
+ hint: result.stderr || "Could not create the local recorder pipes. Try again."
20355
+ });
20356
+ }
20357
+ }
20275
20358
 
20276
20359
  // src/record.tsx
20277
20360
  init_LiveCaptionsScreen();
@@ -20496,6 +20579,7 @@ function assertRecordingPermissions(permissions) {
20496
20579
  data: {
20497
20580
  cliCode: "record.permission_required",
20498
20581
  permission: blocked.name,
20582
+ ...blocked.requiresProcessRestart ? { requiresProcessRestart: blocked.requiresProcessRestart } : {},
20499
20583
  ...blocked.hint ? { recovery: blocked.hint } : {},
20500
20584
  permissions
20501
20585
  }
@@ -20532,9 +20616,23 @@ function resolveSidecarCommand(opts) {
20532
20616
  });
20533
20617
  }
20534
20618
  function ensureBundledHelperExecutable(path6) {
20619
+ if (process.platform === "darwin" && path6.endsWith(".app")) {
20620
+ const executable = darwinAppExecutablePath(path6);
20621
+ if (!existsSync(executable)) {
20622
+ throw cliError("record.helper_unavailable", "Recappi recording helper is not available.", {
20623
+ hint: `Expected bundled helper executable inside ${path6}. Reinstall recappi, or set ${SIDECAR_COMMAND_ENV} to a compatible helper.`
20624
+ });
20625
+ }
20626
+ ensureExecutableMode(executable);
20627
+ return path6;
20628
+ }
20535
20629
  if (process.platform === "win32") return path6;
20630
+ ensureExecutableMode(path6);
20631
+ return path6;
20632
+ }
20633
+ function ensureExecutableMode(path6) {
20536
20634
  const mode = statSync(path6).mode;
20537
- if ((mode & 73) !== 0) return path6;
20635
+ if ((mode & 73) !== 0) return;
20538
20636
  try {
20539
20637
  chmodSync(path6, mode | 493);
20540
20638
  } catch (error51) {
@@ -20543,7 +20641,6 @@ function ensureBundledHelperExecutable(path6) {
20543
20641
  hint: `Could not make bundled helper executable at ${path6}: ${message}. Reinstall recappi, or set ${SIDECAR_COMMAND_ENV} to a compatible helper.`
20544
20642
  });
20545
20643
  }
20546
- return path6;
20547
20644
  }
20548
20645
  function bundledSidecarCommand(platform, arch) {
20549
20646
  const executable = helperExecutableName(platform);
@@ -20551,16 +20648,19 @@ function bundledSidecarCommand(platform, arch) {
20551
20648
  const helperPackage = helperPackageName(platform, arch);
20552
20649
  if (helperPackage) {
20553
20650
  const packageJson = resolveOptionalHelperPackage(helperPackage);
20554
- if (packageJson) return join(dirname(packageJson), executable);
20651
+ if (packageJson) return join2(dirname(packageJson), executable);
20555
20652
  }
20556
20653
  const packageRoot = new URL("..", import.meta.url);
20557
20654
  return fileURLToPath(new URL(`helpers/${platform}-${arch}/${executable}`, packageRoot));
20558
20655
  }
20559
20656
  function helperExecutableName(platform) {
20560
- if (platform === "darwin") return SIDECAR_HELPER_NAME;
20657
+ if (platform === "darwin") return `${SIDECAR_HELPER_NAME}.app`;
20561
20658
  if (platform === "win32") return `${SIDECAR_HELPER_NAME}.exe`;
20562
20659
  return null;
20563
20660
  }
20661
+ function darwinAppExecutablePath(appPath) {
20662
+ return join2(appPath, "Contents", "MacOS", SIDECAR_HELPER_NAME);
20663
+ }
20564
20664
  function helperPackageName(platform, arch) {
20565
20665
  if (platform === "darwin" && arch === "arm64") return "recappi-helper-darwin-arm64";
20566
20666
  return null;