@minicor/mcp-server 4.1.0 → 4.3.0

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.
Files changed (35) hide show
  1. package/README.md +15 -2
  2. package/dist/__tests__/recording-frames.test.js +42 -1
  3. package/dist/__tests__/recording-frames.test.js.map +1 -1
  4. package/dist/__tests__/seeer-client.test.d.ts +2 -0
  5. package/dist/__tests__/seeer-client.test.d.ts.map +1 -0
  6. package/dist/__tests__/seeer-client.test.js +57 -0
  7. package/dist/__tests__/seeer-client.test.js.map +1 -0
  8. package/dist/index.js +1 -1
  9. package/dist/lib.js +1 -1
  10. package/dist/recording-frames.d.ts +82 -9
  11. package/dist/recording-frames.d.ts.map +1 -1
  12. package/dist/recording-frames.js +254 -32
  13. package/dist/recording-frames.js.map +1 -1
  14. package/dist/seeer-client.d.ts +173 -0
  15. package/dist/seeer-client.d.ts.map +1 -0
  16. package/dist/seeer-client.js +181 -0
  17. package/dist/seeer-client.js.map +1 -0
  18. package/dist/server-surface.d.ts.map +1 -1
  19. package/dist/server-surface.js +2 -0
  20. package/dist/server-surface.js.map +1 -1
  21. package/dist/tools/core.d.ts.map +1 -1
  22. package/dist/tools/core.js +189 -22
  23. package/dist/tools/core.js.map +1 -1
  24. package/dist/tools/recording-common.d.ts +11 -0
  25. package/dist/tools/recording-common.d.ts.map +1 -0
  26. package/dist/tools/recording-common.js +15 -0
  27. package/dist/tools/recording-common.js.map +1 -0
  28. package/dist/tools/recording.d.ts +3 -0
  29. package/dist/tools/recording.d.ts.map +1 -0
  30. package/dist/tools/recording.js +237 -0
  31. package/dist/tools/recording.js.map +1 -0
  32. package/dist/tools/workflow-ops.d.ts.map +1 -1
  33. package/dist/tools/workflow-ops.js +79 -5
  34. package/dist/tools/workflow-ops.js.map +1 -1
  35. package/package.json +1 -1
package/README.md CHANGED
@@ -270,7 +270,7 @@ After all steps pass individually:
270
270
 
271
271
  Then close the loop at the job level:
272
272
 
273
- - Point the job's workflow step at the finished `workflowId` (`update_job variant=draft`). If the workflow was edited in Dev Mode, `dev_mode_publish` first - jobs execute published workflows, not Dev Mode branches.
273
+ - Point the job's workflow step at the finished `workflowId` (`update_job variant=draft`). If the workflow was edited in Dev Mode, `dev_mode_publish` first - jobs execute published workflows, not drafts.
274
274
  - `run_tests variant=draft` - the test cases from Phase 3 are the pass/fail verdict for the build
275
275
  - On a red case, `resolve_job_state` / `inspect_job_execution` locate the failing step; replay just that slice with `run_job` (`fromStepId` + `seedExecutionId`) or drop back into the workflow, fix, re-run
276
276
  - When the suite is green, `publish_job` puts it live at `/m/:slug/:path`
@@ -345,7 +345,7 @@ You work **backwards from the job's test cases**. The job's test suite is the ma
345
345
  seedExecutionId=<failed execution> (seeds ctx so mid-graph starts
346
346
  resolve earlier steps' values).
347
347
  5b. Workflow-level (microscope) - the failing step's workflowId -> dev_mode_load,
348
- fix/run the branch's flow steps with dev_mode_run,
348
+ fix/run the draft's flow steps (dev_mode_run needs vmId),
349
349
  dev_mode_publish - jobs execute PUBLISHED workflows.
350
350
  6. PUBLISH - when run_tests variant=draft is green, publish_job promotes the draft;
351
351
  runtime traffic at /m/:slug serves it immediately.
@@ -686,6 +686,19 @@ Jobs/middleware base resolution (precedence: **proxy > direct > default**):
686
686
  | `MIDDLEWARE_SERVICE_URL_US` | Direct middleware-service base for US (overrides the generic one) | unset |
687
687
  | `MIDDLEWARE_SERVICE_URL_CA` | Direct middleware-service base for CA (overrides the generic one) | unset |
688
688
  | `EXPERIMENTAL_USE_CODE_MODE` | Expose the experimental single `codemode` tool surface | unset (standard tools) |
689
+ | `SEEER_SERVICE_PROXY_BASE` | Override the frontend origin fronting the video-tool proxy (auth = user bearer, like the agents/stitchy proxies) | derived from API base |
690
+ | `SEEER_SERVICE_PROXY_PREFIX` | video-tool proxy path prefix | `/api/video-tool` |
691
+ | `SEEER_SERVICE_URL` | Direct video-tool service URL (bypasses the frontend proxy) | unset |
692
+ | `SEEER_SERVICE_TOKEN` | App token for direct mode (sent as X-API-Key) | unset |
693
+ | `SEEER_SERVICE_ID_TOKEN` | Static Google identity token for IAM-gated Cloud Run (local testing) | unset |
694
+ | `SEEER_SERVICE_USE_GCP_IDENTITY` | Set to 1 to mint identity tokens from the GCP metadata server | unset |
695
+
696
+ The video-tool (seeer) service needs **no configuration** against the standard
697
+ Laminar backends: the frontend proxy origin is derived from the region's API
698
+ base (`api.laminar.run` → `app.laminar.run`, `ca.api.laminar.run` →
699
+ `ca.app.laminar.run`). The env vars above only override this for dev/local
700
+ setups (a localhost backend has no derivable frontend, so set
701
+ `SEEER_SERVICE_PROXY_BASE` or `SEEER_SERVICE_URL` there).
689
702
 
690
703
  When none are set, Jobs calls the region's public middleware-service Cloud Run directly. To route through the frontend (once it deploys an `/api/middleware/[...path]` proxy), set `MIDDLEWARE_SERVICE_PROXY_BASE` (or the per-region variants).
691
704
 
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { computeSampleTimestamps, buildScaleFilter, runCommand, } from "../recording-frames.js";
2
+ import { computeSampleTimestamps, computeRangeTimestamps, buildScaleFilter, buildCropFilter, cacheKeyForUrl, runCommand, } from "../recording-frames.js";
3
3
  describe("computeSampleTimestamps", () => {
4
4
  it("returns evenly-spaced, centered timestamps within the duration", () => {
5
5
  expect(computeSampleTimestamps(10, 4)).toEqual([1.25, 3.75, 6.25, 8.75]);
@@ -22,6 +22,47 @@ describe("computeSampleTimestamps", () => {
22
22
  expect(computeSampleTimestamps(Number.NaN, 4)).toEqual([]);
23
23
  });
24
24
  });
25
+ describe("computeRangeTimestamps", () => {
26
+ it("samples the window at the requested fps, inclusive of both ends", () => {
27
+ expect(computeRangeTimestamps(10, 14, 1, 16)).toEqual([10, 11, 12, 13, 14]);
28
+ });
29
+ it("falls back to even spacing when fps would exceed maxFrames", () => {
30
+ const ts = computeRangeTimestamps(0, 30, 2, 4);
31
+ expect(ts).toHaveLength(4);
32
+ expect(ts[0]).toBe(0);
33
+ expect(ts[3]).toBe(30);
34
+ });
35
+ it("returns a single frame for a zero-ish span with n=1", () => {
36
+ expect(computeRangeTimestamps(5, 5.4, 1, 16)).toEqual([5]);
37
+ });
38
+ it("returns empty for invalid input", () => {
39
+ expect(computeRangeTimestamps(10, 5, 1, 16)).toEqual([]);
40
+ expect(computeRangeTimestamps(0, 10, 0, 16)).toEqual([]);
41
+ expect(computeRangeTimestamps(0, 10, 1, 0)).toEqual([]);
42
+ expect(computeRangeTimestamps(Number.NaN, 10, 1, 16)).toEqual([]);
43
+ });
44
+ });
45
+ describe("buildCropFilter", () => {
46
+ it("builds a crop for the region with padding", () => {
47
+ expect(buildCropFilter({ x: 100, y: 50, width: 200, height: 100, pad: 10 })).toBe("crop=220:120:90:40");
48
+ });
49
+ it("clamps to video bounds", () => {
50
+ expect(buildCropFilter({ x: -20, y: -20, width: 3000, height: 2000 }, 2560, 1440)).toBe("crop=2560:1440:0:0");
51
+ });
52
+ it("returns null when the region is entirely outside the video", () => {
53
+ expect(buildCropFilter({ x: 5000, y: 5000, width: 100, height: 100 }, 2560, 1440)).toBeNull();
54
+ });
55
+ });
56
+ describe("cacheKeyForUrl", () => {
57
+ it("ignores the SAS query so re-minted URLs share a cache entry", () => {
58
+ const a = cacheKeyForUrl("https://acct.blob.core.windows.net/replay/rpa_1_2.mp4?sig=abc&se=2026");
59
+ const b = cacheKeyForUrl("https://acct.blob.core.windows.net/replay/rpa_1_2.mp4?sig=zzz&se=2027");
60
+ expect(a).toBe(b);
61
+ });
62
+ it("differs for different blobs", () => {
63
+ expect(cacheKeyForUrl("https://acct.blob.core.windows.net/replay/rpa_1_2.mp4")).not.toBe(cacheKeyForUrl("https://acct.blob.core.windows.net/replay/rpa_1_3.mp4"));
64
+ });
65
+ });
25
66
  describe("runCommand", () => {
26
67
  it("resolves on a zero exit code", async () => {
27
68
  const result = await runCommand(process.execPath, ["-e", "process.stdout.write('hi')"], 10_000);
@@ -1 +1 @@
1
- {"version":3,"file":"recording-frames.test.js","sourceRoot":"","sources":["../../src/__tests__/recording-frames.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recording-frames.test.js","sourceRoot":"","sources":["../../src/__tests__/recording-frames.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=seeer-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seeer-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/seeer-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { deriveSeeerProxyBase, seeerAvailable, seeerProxyBase, } from "../seeer-client.js";
3
+ const ENV_KEYS = ["SEEER_SERVICE_PROXY_BASE", "SEEER_SERVICE_URL"];
4
+ const savedEnv = {};
5
+ beforeEach(() => {
6
+ for (const key of ENV_KEYS) {
7
+ savedEnv[key] = process.env[key];
8
+ delete process.env[key];
9
+ }
10
+ });
11
+ afterEach(() => {
12
+ for (const key of ENV_KEYS) {
13
+ if (savedEnv[key] === undefined)
14
+ delete process.env[key];
15
+ else
16
+ process.env[key] = savedEnv[key];
17
+ }
18
+ });
19
+ describe("deriveSeeerProxyBase", () => {
20
+ it("maps the US API base to the US frontend origin", () => {
21
+ expect(deriveSeeerProxyBase("https://api.laminar.run")).toBe("https://app.laminar.run");
22
+ });
23
+ it("maps the CA API base to the CA frontend origin", () => {
24
+ expect(deriveSeeerProxyBase("https://ca.api.laminar.run")).toBe("https://ca.app.laminar.run");
25
+ });
26
+ it("is domain-agnostic (api.X -> app.X)", () => {
27
+ expect(deriveSeeerProxyBase("https://api.minicor.com")).toBe("https://app.minicor.com");
28
+ });
29
+ it("returns undefined for hosts without an api. label", () => {
30
+ expect(deriveSeeerProxyBase("http://localhost:3001")).toBeUndefined();
31
+ expect(deriveSeeerProxyBase("https://backend.example.com")).toBeUndefined();
32
+ });
33
+ it("returns undefined for missing or invalid input", () => {
34
+ expect(deriveSeeerProxyBase(undefined)).toBeUndefined();
35
+ expect(deriveSeeerProxyBase("not a url")).toBeUndefined();
36
+ });
37
+ });
38
+ describe("seeerProxyBase precedence", () => {
39
+ it("prefers the explicit proxy override over derivation", () => {
40
+ process.env.SEEER_SERVICE_PROXY_BASE = "https://staging.laminar.run/";
41
+ expect(seeerProxyBase("https://api.laminar.run")).toBe("https://staging.laminar.run");
42
+ });
43
+ it("yields to an explicit direct URL (no proxy) so direct mode wins", () => {
44
+ process.env.SEEER_SERVICE_URL = "https://video-tool.example.run.app";
45
+ expect(seeerProxyBase("https://api.laminar.run")).toBeUndefined();
46
+ expect(seeerAvailable("https://api.laminar.run")).toBe(true);
47
+ });
48
+ it("derives from the API base when nothing is configured", () => {
49
+ expect(seeerProxyBase("https://api.laminar.run")).toBe("https://app.laminar.run");
50
+ expect(seeerAvailable("https://api.laminar.run")).toBe(true);
51
+ });
52
+ it("is unavailable when nothing is configured and no derivation is possible", () => {
53
+ expect(seeerAvailable("http://localhost:3001")).toBe(false);
54
+ expect(seeerAvailable(undefined)).toBe(false);
55
+ });
56
+ });
57
+ //# sourceMappingURL=seeer-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seeer-client.test.js","sourceRoot":"","sources":["../../src/__tests__/seeer-client.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B,MAAM,QAAQ,GAAG,CAAC,0BAA0B,EAAE,mBAAmB,CAAC,CAAC;AACnE,MAAM,QAAQ,GAAuC,EAAE,CAAC;AAExD,UAAU,CAAC,GAAG,EAAE;IACd,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,GAAG,EAAE;IACb,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;YACpD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAC1D,yBAAyB,CAC1B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,oBAAoB,CAAC,4BAA4B,CAAC,CAAC,CAAC,IAAI,CAC7D,4BAA4B,CAC7B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAC1D,yBAAyB,CAC1B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CAAC,oBAAoB,CAAC,uBAAuB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACtE,MAAM,CAAC,oBAAoB,CAAC,6BAA6B,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACxD,MAAM,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,8BAA8B,CAAC;QACtE,MAAM,CAAC,cAAc,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CACpD,6BAA6B,CAC9B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,oCAAoC,CAAC;QACrE,MAAM,CAAC,cAAc,CAAC,yBAAyB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAClE,MAAM,CAAC,cAAc,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,cAAc,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CACpD,yBAAyB,CAC1B,CAAC;QACF,MAAM,CAAC,cAAc,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,CAAC,cAAc,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5D,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/dist/index.js CHANGED
@@ -153,7 +153,7 @@ async function unsetStoredApiBase(region) {
153
153
  }
154
154
  const codeModeEnabled = shouldUseCodeMode();
155
155
  const instructions = buildInstructions(true);
156
- const server = new McpServer({ name: SERVER_NAME, version: "4.0.0" }, {
156
+ const server = new McpServer({ name: SERVER_NAME, version: "4.2.0" }, {
157
157
  instructions: codeModeEnabled
158
158
  ? buildCodeModeInstructions(instructions, { sessionLifecycle: true })
159
159
  : instructions,
package/dist/lib.js CHANGED
@@ -60,7 +60,7 @@ export async function createMinicorServer(transport, opts = {}) {
60
60
  const localTools = opts.localTools ?? true;
61
61
  const codeModeEnabled = opts.codeMode ?? shouldUseCodeMode();
62
62
  const instructions = buildInstructions(localTools);
63
- const server = new McpServer({ name: SERVER_NAME, version: "4.0.0" }, {
63
+ const server = new McpServer({ name: SERVER_NAME, version: "4.2.0" }, {
64
64
  instructions: codeModeEnabled
65
65
  ? buildCodeModeInstructions(instructions, {
66
66
  sessionLifecycle: localTools,
@@ -1,12 +1,22 @@
1
1
  /**
2
- * Extracts sampled still frames from a recording (MP4) so that a vision-capable
3
- * model can visually verify or debug an execution.
2
+ * Extracts still frames from a recording (MP4) so that a vision-capable model
3
+ * can visually verify or debug an execution.
4
4
  *
5
5
  * The recording endpoints return a signed URL to an MP4. A model cannot "watch"
6
- * a video URL, so this module downloads the video and uses ffmpeg to sample a
7
- * handful of frames, returned as base64 JPEGs. ffmpeg (and ideally ffprobe) must
8
- * be available on the MCP host; when they are missing we throw a clear error so
9
- * the caller can fall back to handing the raw URL to a human.
6
+ * a video URL, so this module downloads the video and uses ffmpeg to extract
7
+ * frames, returned as base64 JPEGs. Supports:
8
+ * - evenly-spaced sampling across the whole video (default)
9
+ * - exact timestamps ("show me t=41.5s")
10
+ * - dense sampling of a time range ("what happened between t=30s and t=45s")
11
+ * - region cropping at the recording's native resolution ("zoom in here")
12
+ *
13
+ * Downloads are cached under ~/.minicor/cache/recordings keyed on the blob
14
+ * identity (URL minus the SAS query), so successive calls against the same
15
+ * recording skip the download entirely.
16
+ *
17
+ * ffmpeg (and ideally ffprobe) must be available on the MCP host; when they are
18
+ * missing we throw a clear error so the caller can fall back to handing the raw
19
+ * URL to a human.
10
20
  */
11
21
  export interface ExtractedFrame {
12
22
  /** Human-readable label, e.g. "t=12.3s" or "frame 2". */
@@ -16,15 +26,49 @@ export interface ExtractedFrame {
16
26
  /** Base64-encoded JPEG (no data: prefix). */
17
27
  base64: string;
18
28
  }
29
+ export interface FrameRegion {
30
+ /** Left edge in native video pixels. */
31
+ x: number;
32
+ /** Top edge in native video pixels. */
33
+ y: number;
34
+ width: number;
35
+ height: number;
36
+ /** Extra context padding around the region, in pixels. Default 0. */
37
+ pad?: number;
38
+ }
39
+ export interface FrameRange {
40
+ /** Window start in seconds. */
41
+ start: number;
42
+ /** Window end in seconds. */
43
+ end: number;
44
+ /** Sampling rate within the window. Default 1 fps. */
45
+ fps?: number;
46
+ }
19
47
  export interface ExtractFramesOptions {
20
- /** Number of evenly-spaced frames to sample. Default 4. */
48
+ /**
49
+ * Number of evenly-spaced frames to sample. Default 4. Ignored when
50
+ * `timestamps` or `range` is provided.
51
+ */
21
52
  count?: number;
53
+ /** Exact timestamps (seconds) to extract. Takes precedence over `range`/`count`. */
54
+ timestamps?: number[];
55
+ /** Dense sampling of a time window. Takes precedence over `count`. */
56
+ range?: FrameRange;
57
+ /** Safety cap on total frames for timestamps/range modes. Default 16. */
58
+ maxFrames?: number;
59
+ /**
60
+ * Crop to this region at the recording's NATIVE resolution before any
61
+ * scaling — the way to keep small on-screen text readable.
62
+ */
63
+ region?: FrameRegion;
22
64
  /**
23
65
  * Optional max output width in px. When omitted (the default) frames keep the
24
66
  * recording's ORIGINAL resolution — important for reading fine on-screen detail.
25
67
  * Only set this if you explicitly want to trade resolution for smaller frames.
26
68
  */
27
69
  maxWidth?: number;
70
+ /** Use the on-disk download cache. Default true. */
71
+ cache?: boolean;
28
72
  /** Max bytes to download before aborting. Default 200 MB. */
29
73
  maxBytes?: number;
30
74
  /** Overall download timeout in ms. Default 60s. */
@@ -39,11 +83,22 @@ export interface ExtractFramesOptions {
39
83
  * avoid black/blank frames at the very start and end.
40
84
  */
41
85
  export declare function computeSampleTimestamps(durationSeconds: number, count: number): number[];
86
+ /**
87
+ * Computes timestamps for dense sampling of a [start, end] window at `fps`,
88
+ * capped at `maxFrames` (falling back to even spacing across the window when
89
+ * the requested rate would exceed the cap).
90
+ */
91
+ export declare function computeRangeTimestamps(start: number, end: number, fps: number, maxFrames: number): number[];
42
92
  /**
43
93
  * Builds an ffmpeg scale filter that caps width while preserving aspect ratio.
44
94
  * Returns null when no cap is requested, so frames keep their original resolution.
45
95
  */
46
96
  export declare function buildScaleFilter(maxWidth?: number): string | null;
97
+ /**
98
+ * Builds an ffmpeg crop filter for a region (with optional padding), clamped to
99
+ * the video bounds when they are known. Returns null for degenerate regions.
100
+ */
101
+ export declare function buildCropFilter(region: FrameRegion, videoWidth?: number | null, videoHeight?: number | null): string | null;
47
102
  export interface CommandResult {
48
103
  code: number | null;
49
104
  stdout: string;
@@ -51,8 +106,26 @@ export interface CommandResult {
51
106
  }
52
107
  export declare function runCommand(command: string, args: string[], timeoutMs: number): Promise<CommandResult>;
53
108
  /**
54
- * Downloads the recording at `url` and returns sampled JPEG frames.
55
- * Throws with a clear message when ffmpeg is unavailable.
109
+ * Stable cache key for a recording URL: the blob identity without the SAS
110
+ * query, so re-minted signed URLs for the same blob hit the same cache entry.
111
+ */
112
+ export declare function cacheKeyForUrl(url: string): string;
113
+ /**
114
+ * Returns a local path to the recording, downloading it if not already cached.
115
+ * The returned file must NOT be deleted by the caller (it is shared).
116
+ */
117
+ export declare function getCachedRecording(url: string, opts: {
118
+ maxBytes: number;
119
+ downloadTimeoutMs: number;
120
+ }): Promise<string>;
121
+ export interface VideoInfo {
122
+ durationSeconds: number | null;
123
+ width: number | null;
124
+ height: number | null;
125
+ }
126
+ /**
127
+ * Downloads the recording at `url` (via the shared cache) and returns extracted
128
+ * JPEG frames. Throws with a clear message when ffmpeg is unavailable.
56
129
  */
57
130
  export declare function extractFramesFromUrl(url: string, options?: ExtractFramesOptions): Promise<ExtractedFrame[]>;
58
131
  //# sourceMappingURL=recording-frames.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"recording-frames.d.ts","sourceRoot":"","sources":["../src/recording-frames.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CASxF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKjE;AAID,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CA4CxB;AAqHD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,cAAc,EAAE,CAAC,CAuF3B"}
1
+ {"version":3,"file":"recording-frames.d.ts","sourceRoot":"","sources":["../src/recording-frames.ts"],"names":[],"mappings":"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"}