


























@@ -1,3 +1,4 @@
1+// Camera payload validation and artifact writers for node media commands.
12import * as fs from "node:fs/promises";
23import * as path from "node:path";
34import { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
@@ -15,8 +16,10 @@ import {
1516const MAX_CAMERA_URL_DOWNLOAD_BYTES = 250 * 1024 * 1024;
1617const MAX_CAMERA_BASE64_BYTES = MAX_CAMERA_URL_DOWNLOAD_BYTES;
171819+/** Camera orientation accepted by node camera commands. */
1820export type CameraFacing = "front" | "back";
192122+/** Validated still-image payload from `nodes camera snap`. */
2023export type CameraSnapPayload = {
2124format: string;
2225base64?: string;
@@ -25,6 +28,7 @@ export type CameraSnapPayload = {
2528height: number;
2629};
273031+/** Validated video payload from `nodes camera clip`. */
2832export type CameraClipPayload = {
2933format: string;
3034base64?: string;
@@ -33,6 +37,7 @@ export type CameraClipPayload = {
3337hasAudio: boolean;
3438};
353940+/** Validate and normalize an unknown camera still-image payload. */
3641export function parseCameraSnapPayload(value: unknown): CameraSnapPayload {
3742const obj = asRecord(value);
3843const format = asString(obj.format);
@@ -46,6 +51,7 @@ export function parseCameraSnapPayload(value: unknown): CameraSnapPayload {
4651return { format, ...(base64 ? { base64 } : {}), ...(url ? { url } : {}), width, height };
4752}
485354+/** Validate and normalize an unknown camera clip payload. */
4955export function parseCameraClipPayload(value: unknown): CameraClipPayload {
5056const obj = asRecord(value);
5157const format = asString(obj.format);
@@ -59,6 +65,7 @@ export function parseCameraClipPayload(value: unknown): CameraClipPayload {
5965return { format, ...(base64 ? { base64 } : {}), ...(url ? { url } : {}), durationMs, hasAudio };
6066}
616768+/** Build a deterministic temp path for a camera artifact. */
6269export function cameraTempPath(opts: {
6370kind: "snap" | "clip";
6471facing?: CameraFacing;
@@ -76,6 +83,7 @@ export function cameraTempPath(opts: {
7683return path.join(tmpDir, `${cliName}-camera-${opts.kind}${facingPart}-${id}${ext}`);
7784}
788586+/** Download a node-hosted media URL to disk after HTTPS, host, redirect, and size checks. */
7987export async function writeUrlToFile(
8088filePath: string,
8189url: string,
@@ -95,6 +103,7 @@ export async function writeUrlToFile(
95103);
96104}
97105106+// The node host is allowed even when private because the RPC response supplied its remote IP.
98107const policy = {
99108allowPrivateNetwork: true,
100109allowedHostnames: [expectedHost],
@@ -184,6 +193,7 @@ function estimateDecodedBase64Bytes(base64: string): number {
184193return Math.floor((normalized.length * 3) / 4) - padding;
185194}
186195196+/** Decode a base64 media payload to disk with preflight and post-decode size checks. */
187197export async function writeBase64ToFile(
188198filePath: string,
189199base64: string,
@@ -201,6 +211,7 @@ export async function writeBase64ToFile(
201211return { path: filePath, bytes: buf.length };
202212}
203213214+/** Require the node remote IP needed to validate URL-backed camera payloads. */
204215export function requireNodeRemoteIp(remoteIp?: string): string {
205216const normalized = remoteIp?.trim();
206217if (!normalized) {
@@ -209,6 +220,7 @@ export function requireNodeRemoteIp(remoteIp?: string): string {
209220return normalized;
210221}
211222223+/** Write either a URL-backed or base64-backed camera payload to disk. */
212224export async function writeCameraPayloadToFile(params: {
213225filePath: string;
214226payload: { url?: string; base64?: string };
@@ -228,6 +240,7 @@ export async function writeCameraPayloadToFile(params: {
228240throw new Error(params.invalidPayloadMessage ?? "invalid camera payload");
229241}
230242243+/** Write a camera clip payload to a generated temp file and return its path. */
231244export async function writeCameraClipPayloadToFile(params: {
232245payload: CameraClipPayload;
233246facing: CameraFacing;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。