mirror of
https://github.com/hanzoai/insights.git
synced 2026-11-03 00:52:32 +00:00
Every first-party Python caller and test now names the mount the API actually answers on. Mechanical: URL literals only, and only where the /api/ was ours -- a preceding word character means it was a module path like insights/api/ or a third-party host like slack.com/api/, and those are untouched. Left saying /api/, each for a reason rather than for safety: - products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints - services/llm-gateway deploys separately and is under a code freeze, so moving its two calls would break it during the skew - the mcp_store and Slack/Teams redirect_uris are registered with third parties and must byte-match what was registered - the Wizard and Raycast client-metadata paths are OAuth client_ids - /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the ingress routes to the Rust fleets, held by released SDK versions Test URLconfs moved with the requests that call them, and the Datastore user-selection fixtures now carry the leading slash that request.path has.
51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
/** Frontend twin of the ingestion pointer module: the only place phaiblob:// URIs are interpreted. */
|
|
export interface AiBlobPointer {
|
|
version: string
|
|
algo: string
|
|
hash: string
|
|
mime: string | null
|
|
size: number | null
|
|
}
|
|
|
|
const POINTER_SCHEME = 'phaiblob://'
|
|
const POINTER_RE = /^phaiblob:\/\/(v1)\/(sha256)\/([0-9a-f]{64})(?:\?(.*))?$/
|
|
// TODO: replace the whole URL rather than its data component, so a valid data URL can't be clobbered.
|
|
const DATA_URI_WRAPPED_POINTER = /^data:[\w.+-]+\/[\w.+-]+;base64,(phaiblob:\/\/.*)$/
|
|
|
|
export function parseAiBlobPointer(value: string): AiBlobPointer | null {
|
|
const unwrapped = DATA_URI_WRAPPED_POINTER.exec(value)?.[1] ?? value
|
|
if (!unwrapped.startsWith(POINTER_SCHEME)) {
|
|
return null
|
|
}
|
|
const match = POINTER_RE.exec(unwrapped)
|
|
if (!match) {
|
|
return null
|
|
}
|
|
const params = new URLSearchParams(match[4] ?? '')
|
|
const sizeRaw = params.get('size')
|
|
return {
|
|
version: match[1],
|
|
algo: match[2],
|
|
hash: match[3],
|
|
mime: params.get('mime'),
|
|
size: sizeRaw && /^\d+$/.test(sizeRaw) ? parseInt(sizeRaw, 10) : null,
|
|
}
|
|
}
|
|
|
|
export function resolveAiBlobUrl(value: string, teamId: number | string | null): string {
|
|
const pointer = parseAiBlobPointer(value)
|
|
if (!pointer || teamId === null || teamId === undefined || teamId === '') {
|
|
return value
|
|
}
|
|
return `/v1/projects/${teamId}/ai_blob/${pointer.version}/${pointer.algo}/${pointer.hash}`
|
|
}
|
|
|
|
/**
|
|
* Resolves a raw `data` field (base64 payload or, post-offload, a phaiblob:// pointer) to a
|
|
* renderable src: the blob endpoint URL if it's a pointer, otherwise the `data:` URI it always was.
|
|
*/
|
|
export function resolveDataUri(rawData: string, mimeType: string, teamId: number | string | null): string {
|
|
const resolved = resolveAiBlobUrl(rawData, teamId)
|
|
return resolved !== rawData ? resolved : `data:${mimeType};base64,${rawData}`
|
|
}
|