Files
WeHub Mirror 6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
WeHub snapshot of cb28d14c6f2c081de7a0d8729a8c816c9adef67a
2026-08-10 11:17:50 +08:00

152 lines
5.1 KiB
TypeScript

import { isPlainRecord } from '@sim/utils/object'
import type { ResolvedSecretInputPath } from '@/executor/utils/resolved-secret-trace-registry'
interface ModelBoundFileInputOptions {
includeInlineBase64?: boolean
includeName?: boolean
parseSerializedFile?: boolean
}
interface PreferredModelBoundFileInputPathOptions extends ModelBoundFileInputOptions {
file: unknown
filePath: unknown
fileInputPath: ResolvedSecretInputPath
filePathInputPath: ResolvedSecretInputPath
prefer: 'file' | 'path'
}
function selectFileRecordInputPaths(
input: Record<string, unknown>,
rootPath: ResolvedSecretInputPath,
options: ModelBoundFileInputOptions
): ResolvedSecretInputPath[] {
const paths: ResolvedSecretInputPath[] = []
const hasSource = Boolean(input.base64 || input.key || input.path || input.url)
if (options.includeInlineBase64 && input.base64) {
paths.push([...rootPath, 'base64'])
}
if (typeof input.path === 'string' && isInlineDataUrl(input.path)) {
paths.push([...rootPath, 'path'])
}
if (typeof input.url === 'string' && isInlineDataUrl(input.url)) {
paths.push([...rootPath, 'url'])
}
if (hasSource && options.includeName && input.name !== undefined) {
paths.push([...rootPath, 'name'])
}
return paths
}
function isInlineDataUrl(value: string): boolean {
return /^data:[^,]*;base64,/iu.test(value.trim())
}
/**
* Selects inline file content and explicitly requested model-visible metadata. Storage keys,
* paths, and remote URLs are locators: provenance on a locator says nothing about the fetched
* bytes, which are authorized independently at the owning file boundary.
*/
export function selectModelBoundFileInputPaths(
input: unknown,
rootPath: ResolvedSecretInputPath,
options: ModelBoundFileInputOptions = {}
): ResolvedSecretInputPath[] {
if (Array.isArray(input)) {
return input.flatMap((entry, index) =>
selectModelBoundFileInputPaths(entry, [...rootPath, String(index)], options)
)
}
if (typeof input === 'string') {
if (options.parseSerializedFile) {
try {
const parsed = JSON.parse(input)
return selectModelBoundFileInputPaths(parsed, rootPath, {
...options,
parseSerializedFile: false,
}).length > 0
? [rootPath]
: []
} catch {
return isInlineDataUrl(input) ? [rootPath] : []
}
}
return isInlineDataUrl(input) ? [rootPath] : []
}
if (!isPlainRecord(input)) return []
return selectFileRecordInputPaths(input, rootPath, options)
}
function selectFilePath(input: unknown): string | undefined {
if (typeof input !== 'string' || input === 'null' || input.trim() === '') return undefined
return input.trim()
}
/** Mirrors file-vs-path precedence while selecting exact resolver input paths. */
export function selectPreferredModelBoundFileInputPaths(
options: PreferredModelBoundFileInputPathOptions
): ResolvedSecretInputPath[] {
const hasFile = isPlainRecord(options.file)
const filePath = selectFilePath(options.filePath)
if (options.prefer === 'file' && hasFile) {
return selectModelBoundFileInputPaths(options.file, options.fileInputPath, options)
}
if (filePath !== undefined) {
return isInlineDataUrl(filePath) ? [options.filePathInputPath] : []
}
if (options.prefer === 'path' && hasFile) {
return selectModelBoundFileInputPaths(options.file, options.fileInputPath, options)
}
return []
}
/** Selects only file names that are serialized into model-visible requests. */
export function selectModelVisibleFileNames(input: unknown): unknown {
if (Array.isArray(input)) return input.map(selectModelVisibleFileNames)
if (!isPlainRecord(input)) return undefined
return input.name !== undefined ? { name: input.name } : {}
}
function haveExactKeys(value: Record<string, unknown>, expected: readonly string[]): boolean {
const keys = Object.keys(value)
return keys.length === expected.length && expected.every((key) => Object.hasOwn(value, key))
}
/** Reapplies projected file names while preserving every locator and inline-content field. */
export function applyProjectedModelVisibleFileNames(
original: unknown,
projected: unknown
): unknown {
if (Array.isArray(original)) {
if (!Array.isArray(projected) || projected.length !== original.length) {
throw new Error('Projected file names do not match the original files')
}
return original.map((entry, index) =>
applyProjectedModelVisibleFileNames(entry, projected[index])
)
}
if (!isPlainRecord(original)) {
if (projected !== undefined) {
throw new Error('Projected file name does not match the original file')
}
return original
}
if (!isPlainRecord(projected)) {
throw new Error('Projected file name is invalid')
}
if (original.name === undefined) {
if (!haveExactKeys(projected, [])) {
throw new Error('Projected file name does not match the original file')
}
return { ...original }
}
if (!haveExactKeys(projected, ['name']) || typeof projected.name !== 'string') {
throw new Error('Projected file name is invalid')
}
return { ...original, name: projected.name }
}