2026-05-30 06:07:55 +00:00
import esbuild from "esbuild" ;
import { mkdir , rm , writeFile } from "node:fs/promises" ;
import { join , resolve } from "node:path" ;
import { fileURLToPath } from "node:url" ;
const rootDir = resolve ( fileURLToPath ( new URL ( ".." , import . meta . url ) ) ) ;
const outDir = join ( rootDir , ".tmp" , "remote-draft" ) ;
const entryPath = join ( outDir , "entry.ts" ) ;
const bundlePath = join ( outDir , "bundle.mjs" ) ;
const obsidianShimPath = join ( outDir , "obsidian-shim.ts" ) ;
await rm ( outDir , { recursive : true , force : true } ) ;
await mkdir ( outDir , { recursive : true } ) ;
await writeFile ( obsidianShimPath , `
export async function requestUrl ( request ) {
globalThis . _ _remoteDraftRequests = [ ... ( globalThis . _ _remoteDraftRequests ? ? [ ] ) , request ] ;
2026-06-22 04:48:09 +00:00
if ( globalThis . _ _remoteDraftNeverResolve ) {
return new Promise ( ( ) => { } ) ;
}
2026-05-30 06:07:55 +00:00
return globalThis . _ _remoteDraftResponse ? ? {
status : 200 ,
json : {
title : "Generated draft" ,
summary : "Grounded remote summary." ,
sections : [ { heading : "Evidence" , body : "Uses the sanitized payload." } ] ,
suggestedTags : [ "remote-draft" ] ,
warnings : [ "review before publishing" ] ,
model : "test-model" ,
} ,
} ;
}
` , "utf8");
await writeFile ( entryPath , `
import { createRemoteDraftDecision , requestRemoteDraft } from "../../src/view/workbench/remote-draft" ;
import { DEFAULT _SETTINGS } from "../../src/domain/constants" ;
function assert ( condition , message ) {
if ( ! condition ) throw new Error ( message ) ;
}
const input = {
task : "Draft" ,
model : {
path : "models/example.glb" ,
title : "example" ,
format : "glb" ,
summary : {
meshCount : 2 ,
triangleCount : 100 ,
vertexCount : 80 ,
materialCount : 1 ,
boundingSize : { x : 1 , y : 2 , z : 3 } ,
rootName : "__root__" ,
} ,
tags : [ "demo" ] ,
notes : "local note" ,
} ,
evidence : {
rawModelIncluded : false ,
previewImages : [ "Media/3D Previews/example.png" ] ,
warnings : [ ] ,
generatedAt : "2026-01-01T00:00:00.000Z" ,
} ,
partCandidates : [ { partId : "p1" , name : "Part" , triangleCount : 100 , observations : [ "Observed" ] } ] ,
annotationLinks : [ { annotationId : "a1" , label : "Pin" , position : [ 1 , 2 , 3 ] , nearestPartId : "p1" , nearestPartName : "Part" , confidence : 0.8 } ] ,
knowledgeNodes : [ { id : "k1" , title : "Node" , domain : "geometry" , summary : "Summary" , relatedPartIds : [ "p1" ] , relatedAssetIds : [ "models/example.glb" ] , confidence : 0.7 , source : "rule" } ] ,
} ;
const local = createRemoteDraftDecision ( DEFAULT _SETTINGS , input , "local-evidence-v1" ) ;
assert ( local . enabled === false && local . reason === "analysisMode=local" , "Local mode should not enable remote draft" ) ;
const emptyUrl = createRemoteDraftDecision ( { ... DEFAULT _SETTINGS , analysisMode : "hybrid" } , input , "local-evidence-v1" ) ;
assert ( emptyUrl . enabled === false , "Remote draft should require serviceBaseUrl" ) ;
const badUrl = createRemoteDraftDecision ( { ... DEFAULT _SETTINGS , analysisMode : "hybrid" , serviceBaseUrl : "file:///tmp/draft" } , input , "local-evidence-v1" ) ;
assert ( badUrl . enabled === false , "Remote draft should reject non-http URLs" ) ;
const badQueryUrl = createRemoteDraftDecision ( { ... DEFAULT _SETTINGS , analysisMode : "hybrid" , serviceBaseUrl : "https://example.invalid/api?token=bad" } , input , "local-evidence-v1" ) ;
assert ( badQueryUrl . enabled === false , "Remote draft should reject base URLs with query strings" ) ;
const localPort = createRemoteDraftDecision ( { ... DEFAULT _SETTINGS , analysisMode : "hybrid" , serviceBaseUrl : "localhost:8787" } , input , "local-evidence-v1" ) ;
assert ( localPort . enabled === true && localPort . endpoint === "http://localhost:8787/draft-note" , "Localhost shorthand URL failed" ) ;
const rawModel = createRemoteDraftDecision ( {
... DEFAULT _SETTINGS ,
analysisMode : "hybrid" ,
serviceBaseUrl : "https://example.invalid/" ,
sendRawModelToRemote : true ,
} , input , "local-evidence-v1" ) ;
assert ( rawModel . enabled === false , "Remote draft must refuse raw model upload" ) ;
const stripped = createRemoteDraftDecision ( {
... DEFAULT _SETTINGS ,
analysisMode : "hybrid" ,
serviceBaseUrl : "https://example.invalid/" ,
sendGeometrySummaryToRemote : false ,
sendPreviewImagesToRemote : false ,
} , input , "local-evidence-v1" ) ;
assert ( stripped . enabled === true , "Hybrid mode with URL should enable remote draft" ) ;
assert ( stripped . endpoint === "https://example.invalid/draft-note" , "Endpoint normalization failed" ) ;
assert ( stripped . request . draftingInput . evidence . previewImages . length === 0 , "Preview images were not stripped" ) ;
assert ( stripped . request . draftingInput . model . summary === undefined , "Geometry summary was not stripped" ) ;
assert ( stripped . request . draftingInput . partCandidates . length === 0 , "Part candidates were not stripped" ) ;
assert ( stripped . request . draftingInput . annotationLinks [ 0 ] . nearestPartName === undefined , "Annotation nearest part was not stripped" ) ;
assert ( stripped . request . draftingInput . annotationLinks [ 0 ] . position . every ( ( value ) => value === 0 ) , "Annotation coordinates were not stripped" ) ;
assert ( stripped . request . draftingInput . annotationLinks [ 0 ] . confidence <= 0.25 , "Geometry confidence was not reduced" ) ;
assert ( stripped . request . draftingInput . knowledgeNodes [ 0 ] . summary === "Geometry details were withheld by privacy settings." , "Knowledge node geometry summary was not stripped" ) ;
assert ( stripped . request . draftingInput . knowledgeNodes [ 0 ] . relatedPartIds . length === 0 , "Knowledge node part links were not stripped" ) ;
assert ( stripped . request . draftingInput . evidence . rawModelIncluded === false , "Raw model flag must stay false" ) ;
const included = createRemoteDraftDecision ( {
... DEFAULT _SETTINGS ,
analysisMode : "remote" ,
serviceBaseUrl : "https://example.invalid/api" ,
sendGeometrySummaryToRemote : true ,
sendPreviewImagesToRemote : true ,
} , input , "local-evidence-v1" ) ;
assert ( included . enabled === true , "Remote mode with URL should enable remote draft" ) ;
assert ( included . endpoint === "https://example.invalid/api/draft-note" , "Remote endpoint failed" ) ;
assert ( included . request . draftingInput . evidence . previewImages . length === 1 , "Preview images should be included when allowed" ) ;
assert ( included . request . draftingInput . model . summary . meshCount === 2 , "Geometry summary should be included when allowed" ) ;
globalThis . _ _remoteDraftRequests = [ ] ;
const draft = await requestRemoteDraft ( included ) ;
assert ( draft . summary === "Grounded remote summary." , "Remote draft response was not normalized" ) ;
assert ( draft . sections . length === 1 , "Remote draft sections were not normalized" ) ;
assert ( globalThis . _ _remoteDraftRequests . length === 1 , "Remote draft request was not sent" ) ;
assert ( globalThis . _ _remoteDraftRequests [ 0 ] . url === "https://example.invalid/api/draft-note" , "Remote draft request URL mismatch" ) ;
assert ( JSON . parse ( globalThis . _ _remoteDraftRequests [ 0 ] . body ) . draftingInput . evidence . rawModelIncluded === false , "Posted payload should not include raw model data" ) ;
globalThis . _ _remoteDraftResponse = { status : 503 , json : { summary : "unavailable" } } ;
let failed = false ;
try {
await requestRemoteDraft ( included ) ;
} catch ( error ) {
failed = String ( error ) . includes ( "HTTP 503" ) ;
}
assert ( failed , "Remote draft HTTP failure was not surfaced" ) ;
2026-06-22 04:48:09 +00:00
globalThis . _ _remoteDraftNeverResolve = true ;
let timedOut = false ;
try {
await requestRemoteDraft ( included , { timeoutMs : 5 } ) ;
} catch ( error ) {
timedOut = String ( error ) . includes ( "timed out after 5ms" ) ;
} finally {
globalThis . _ _remoteDraftNeverResolve = false ;
}
assert ( timedOut , "Remote draft timeout was not surfaced" ) ;
2026-05-30 06:07:55 +00:00
console . log ( "Remote draft privacy verification passed" ) ;
` , "utf8");
await esbuild . build ( {
entryPoints : [ entryPath ] ,
bundle : true ,
platform : "node" ,
format : "esm" ,
outfile : bundlePath ,
2026-06-22 04:48:09 +00:00
banner : { js : "globalThis.activeWindow = globalThis;" } ,
2026-05-30 06:07:55 +00:00
logLevel : "silent" ,
plugins : [
{
name : "obsidian-shim" ,
setup ( build ) {
build . onResolve ( { filter : /^obsidian$/ } , ( ) => ( { path : obsidianShimPath } ) ) ;
} ,
} ,
] ,
} ) ;
await import ( ` file:// ${ bundlePath } ` ) ;