@realsee/vr-cli 0.1.5 → 0.2.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.
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import{parseArgs as Mr}from"node:util";var Y="@realsee/vr-cli";var F="https://registry.npmjs.org/",N="public";var j="0.1.5",O=["portable","all","none"],B="22.23.2",C="22.18.0",te=`>=${C}`,D="11.19.0",se=">=11.19.0",ae=`npm@${D}`,G=Object.freeze({"@fastify/static":"10.1.3","@realsee/dnalogel":"3.81.2","@realsee/five":"6.8.9","@realsee/vapor":"0.2.12",fastify:"5.12.1",react:"18.3.1","react-dom":"18.3.1",three:"0.117.1"}),V=Object.freeze({three:G.three}),oe=(r,t)=>r in V?t:`>=${t}`,H=r=>{if(typeof r!="string")return null;let t=/^>=(\d+\.\d+\.\d+(?:[-+][\w.]+)?)$/.exec(r.trim());return t?t[1]:null},P=(r,t)=>{let e=i=>{if(typeof i!="string")return null;let o=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(i.trim());return o?{core:o.slice(1,4).map(Number),prerelease:o[4]??null}:null},s=e(r),n=e(t);if(!s||!n)return!1;for(let i=0;i<3;i+=1){let o=s.core[i]-n.core[i];if(o>0)return!0;if(o<0)return!1}return s.prerelease===n.prerelease||s.prerelease===null?!0:n.prerelease===null?!1:s.prerelease.localeCompare(n.prerelease,"en",{numeric:!0})>=0},q=Object.freeze({"@eslint/js":"10.0.1","@types/node":"24.13.3","@types/react":"18.3.12","@types/react-dom":"18.3.1","@vitejs/plugin-react":"4.3.1",concurrently:"10.0.5",eslint:"10.9.1","eslint-plugin-react-hooks":"7.1.1",globals:"17.11.0",prettier:"3.9.6",typescript:"5.9.3","typescript-eslint":"8.68.0",vite:"5.4.14"}),ie=Object.freeze(["APP_ID","REALSEE_GATEWAY_ENDPOINT","REALSEE_GATEWAY_APP_KEY","REALSEE_GATEWAY_APP_SECRET","REALSEE_DEFAULT_RESOURCE_CODE","REALSEE_SOURCE"]),ne=Object.freeze(["REALSEE_GATEWAY_APP_KEY","REALSEE_GATEWAY_APP_SECRET"]),le=Object.freeze(["REALSEE_GATEWAY_APP_SECRET"]),ce=Object.freeze(["package.json","package-lock.json","README.md","index.html","tsconfig.json","tsconfig.server.json","vite.config.ts",".editorconfig",".nvmrc",".node-version",".npmrc",".prettierignore","eslint.config.mjs","prettier.config.mjs","scripts/setup-config.mjs","scripts/setup-config.test.mjs","scripts/setup.mjs","server/types.ts","server/config.ts","server/check-config.ts","server/realsee-open-api.ts","server/work-service.ts","server/work-route.ts","server/app.ts","server/index.ts","shared/model-floorplan.ts","src/main.tsx","src/app/App.tsx","src/shared/useElementSize.ts","src/vr/five/api.ts","src/vr/five/fiveConfig.ts","src/vr/five/SpatialRuntimeProvider.tsx","src/vr/five/initialState.ts","src/vr/plugins/dnalogel.ts","src/vr/plugins/SpatialPluginRegistry.ts","src/vr/plugins/examples/FeatureOwnedMeshPlugin.ts","src/vr/work/WorkSource.ts","src/features/floorplan/ModelFloorplanFeature.ts","src/features/navigation/ModeSwitcher.tsx","src/features/tags/PanoTagFeature.ts","src/ui/ViewerOverlay.tsx","src/styles.css",".env.example",".gitignore",".vr-cli.json"]);import{spawn as er}from"node:child_process";import{rename as ye,writeFile as rr}from"node:fs/promises";import A from"node:path";import{fileURLToPath as tr}from"node:url";import _ from"node:path";import{mkdir as qe,readFile as Ze,writeFile as me}from"node:fs/promises";import{mkdir as ue,readFile as Be,readdir as Z,stat as fe,writeFile as pe}from"node:fs/promises";import L from"node:path";var He=new Set(["",".css",".html",".json",".md",".mjs",".ts",".tsx",".txt",".yml",".yaml"]);function Q(r,t){return Object.entries(t).reduce((e,[s,n])=>e.replaceAll(`{{${s}}}`,String(n)).replaceAll(`__${s}__`,String(n)),r)}async function u(r){try{return await fe(r),!0}catch(t){if(t?.code==="ENOENT")return!1;throw t}}async function de(r){if(!await u(r)){await ue(r,{recursive:!0});return}if(!(await fe(r)).isDirectory())throw new Error(`\u76EE\u6807\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${r}`);if((await Z(r)).length)throw new Error(`\u76EE\u6807\u76EE\u5F55\u5FC5\u987B\u4E3A\u7A7A\uFF1A${r}`)}async function T(r,t,e={}){if(!await u(r))throw new Error(`\u6A21\u677F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${r}`);await ue(t,{recursive:!0});for(let s of await Z(r,{withFileTypes:!0})){let n=L.join(r,s.name),i=L.join(t,s.name);if(s.isDirectory()){await T(n,i,e);continue}if(!s.isFile())continue;let o=L.extname(s.name),m=await Be(n);if(!He.has(o)){await pe(i,m);continue}await pe(i,Q(m.toString("utf8"),e))}}async function K(r,t=r){let e=[];for(let s of await Z(t,{withFileTypes:!0})){let n=L.join(t,s.name);s.isDirectory()?e.push(...await K(r,n)):s.isFile()&&e.push(L.relative(r,n).split(L.sep).join("/"))}return e.sort()}var ve=Object.freeze(["realsee-vr-app","realsee-image-resources","threejs-r117-five"]),Qe=Object.freeze(["realsee-vr-app/SKILL.md","realsee-vr-app/references/dnalogel-api-capabilities.md","realsee-vr-app/references/dnalogel-openapi-recipes.md","realsee-vr-app/references/continuous-improvement.md","realsee-vr-app/references/five-api-capabilities.md","realsee-vr-app/references/five-runtime.md","realsee-vr-app/references/official-docs.md","realsee-vr-app/references/plugin-lifecycle.md","realsee-vr-app/references/ui-and-validation.md","realsee-vr-app/references/vapor-dom-layer.md","realsee-vr-app/scripts/verify-project.mjs","realsee-image-resources/SKILL.md","realsee-image-resources/references/cors-and-three-textures.md","realsee-image-resources/references/data-wanxiang.md","realsee-image-resources/references/five-image-url-transform.md","realsee-image-resources/references/source-and-domain-policy.md","threejs-r117-five/SKILL.md","threejs-r117-five/references/five-ownership.md","threejs-r117-five/references/r117-capabilities.md","threejs-r117-five/references/r117-version-guards.md"]),X=r=>Qe.map(t=>_.posix.join(r,t)),ge=Object.freeze(["AGENTS.md",...X(".agents/skills")]),Xe=Object.freeze([".agents/README.md",...X(".claude/skills"),...X(".kiro/skills"),"CLAUDE.md",".cursor/rules/realsee-vr.mdc",".github/copilot-instructions.md",".kiro/steering/realsee-vr.md"]),he=Object.freeze([".agents",".claude/skills",".kiro/skills",".cursor/rules",".kiro/steering"]),we=Object.freeze(["AGENTS.md","CLAUDE.md",".github/copilot-instructions.md"]),Ee=Object.freeze([".claude/skills",".kiro/skills",".cursor/rules",".kiro/steering"]),Se=Object.freeze([".agents/README.md","CLAUDE.md",".github/copilot-instructions.md"]),Je=Object.freeze([...ge,...Xe]);function W(r){if(r==="none")return[];if(r==="portable")return[...ge];if(r==="all")return[...Je];throw new Error(`\u672A\u77E5 agent assets \u6A21\u5F0F\uFF1A${r}`)}async function ke({toolRoot:r,targetRoot:t,mode:e,tokens:s}){if(e==="none")return[];for(let i of ve)await T(_.join(r,"skills",i),_.join(t,".agents","skills",i),s);let n=await Ze(_.join(r,"agent-assets","AGENTS.md"),"utf8");if(await me(_.join(t,"AGENTS.md"),Q(n,s)),e!=="all")return W(e);for(let i of[".claude/skills",".kiro/skills"])for(let o of ve)await T(_.join(r,"skills",o),_.join(t,i,o),s);return await T(_.join(r,"agent-assets","adapters"),t,s),await qe(_.join(t,".agents"),{recursive:!0}),await me(_.join(t,".agents","README.md"),"# Agent assets\n\n`skills/realsee-vr-app`, `skills/realsee-image-resources` and `skills/threejs-r117-five` are the three canonical portable Skills. Vendor-specific copies are generated; edit the canonical copies and regenerate adapters.\n"),W(e)}var sr=!0,J=tr(new URL(sr?"..":"../..",import.meta.url)),ar=process.platform==="win32"?"npm.cmd":"npm";function or(r,t){let e=t||A.basename(r),s=e.trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"");if(!s||!/^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/.test(s))throw new Error(`\u65E0\u6CD5\u751F\u6210\u5408\u6CD5 npm \u5305\u540D\uFF1A${e}`);return s}function ir(r){return new Promise((t,e)=>{let s=er(ar,["ci"],{cwd:r,stdio:"inherit",shell:!1});s.once("error",e),s.once("exit",(n,i)=>{n===0?t():e(new Error(`npm ci \u5931\u8D25\uFF08code=${n??"null"}, signal=${i??"none"}\uFF09`))})})}async function be({directory:r,preset:t,name:e,agentAssets:s="portable",install:n=!0}){if(!r)throw new Error("\u7F3A\u5C11\u76EE\u6807\u76EE\u5F55\u3002\u7528\u6CD5\uFF1Avr-cli create <directory>");if(t&&t!=="viewer")throw new Error("--preset \u6682\u65F6\u53EA\u652F\u6301 viewer\u3002");if(!O.includes(s))throw new Error(`\u672A\u77E5 agent assets \u6A21\u5F0F\uFF1A${s}\u3002\u53EF\u9009\u503C\uFF1A${O.join(", ")}`);let i=A.resolve(r),o=or(i,e),m={PACKAGE_NAME:o,TOOL_VERSION:j};await de(i),await T(A.join(J,"templates","base"),i,m),await ye(A.join(i,".gitignore.template"),A.join(i,".gitignore")),await ye(A.join(i,".npmrc.template"),A.join(i,".npmrc")),await T(A.join(J,"templates","viewer"),i,m);let f=await ke({toolRoot:J,targetRoot:i,mode:s,tokens:m}),p={schemaVersion:1,toolVersion:j,preset:"viewer",packageName:o,agentAssets:s,generatedAt:new Date().toISOString()};return await rr(A.join(i,".vr-cli.json"),`${JSON.stringify(p,null,2)}
2
- `),n&&await ir(i),{targetRoot:i,packageName:o,installedAgentAssets:f,installed:n}}import{execFile as nr}from"node:child_process";import{readFile as E,readdir as lr,stat as cr}from"node:fs/promises";import l from"node:path";import{promisify as pr}from"node:util";var ur=pr(nr),a=(r,t,e,s)=>({level:r,code:t,message:e,...s?{detail:s}:{}}),_e=r=>/(?:^|_)(?:AK|SK|SECRET|TOKEN|PASSWORD|CREDENTIALS?)(?:_|$)/i.test(r),fr=Object.freeze([/\bAPP_ID\b/,/\bREALSEE_(?:GATEWAY_[A-Z0-9_]+|SOURCE|ACCESS_TOKEN)\b/,/\b(?:VITE_)?(?:AK|SK|ACCESS_TOKEN)\b/]),ee=r=>{for(let t of fr){let e=t.exec(r);if(e)return e[0]}return null},k=r=>r.replace(/\/\*[\s\S]*?\*\//g,"").replace(/(^|[^:])\/\/.*$/gm,"$1"),dr=Object.freeze([{pattern:/three\/addons\//,message:"r117 \u6269\u5C55\u6A21\u5757\u5FC5\u987B\u4ECE three/examples/jsm \u5BFC\u5165\u3002"},{pattern:/three\/tsl\b/,message:"Three r117 \u4E0D\u63D0\u4F9B\u73B0\u4EE3 TSL \u5165\u53E3\u3002"},{pattern:/\b(?:SRGBColorSpace|LinearSRGBColorSpace|NoColorSpace|CapsuleGeometry|WebGPURenderer)\b/,message:"\u6E90\u7801\u4F7F\u7528\u4E86 Three r117 \u4E0D\u5B58\u5728\u7684\u73B0\u4EE3\u7B26\u53F7\u3002"},{pattern:/\b(?:renderer|[\w$]*Renderer)\.outputColorSpace\b/,message:"Three r117 \u4F7F\u7528 renderer.outputEncoding\u3002"},{pattern:/\b(?:texture|[\w$]*(?:map|Map|texture|Texture))\.colorSpace\s*=/,message:"Three r117 \u7EB9\u7406\u4F7F\u7528 encoding\uFF0C\u800C\u4E0D\u662F colorSpace\u3002"}]),mr=Object.freeze(["from '@realsee/five'","from 'three'","FEATURE_OWNED_MESH_PLUGIN_NAME","example.featureOwnedMesh","new Group(","new BoxBufferGeometry(","new MeshBasicMaterial(","new Mesh(","five.scene.add(root)","root.parent?.remove(root)","five.needsRender = true","geometry.dispose()","material.dispose()","plugins.register(FEATURE_OWNED_MESH_PLUGIN_NAME, plugin)","plugin.dispose()"]),vr=Object.freeze(["new Five(","new WebGLRenderer(","renderer.render(","requestAnimationFrame(","five.dispose(","three/addons/"]),gr=Object.freeze(["Image resources use approved Realsee CDN domains only; never use `ljcdn.com`, `lianjia.com` or their subdomains.","Prefer Five `defaultImageURLTransform` over hand-built \u6570\u636E\u4E07\u8C61 query strings when its installed contract satisfies the request.","URL transformation does not grant CORS access.","Ordinary images use the deployment's approved image-4 host.","Feature media from OpenAPI or Work data is displayed through a derived sized variant, never the original: pass ordinary images through the reviewed `defaultImageURLTransform` adapter with a bounded `size`, and do not hand a full-resolution URL to a tag, tour, card or DOM thumbnail."]),hr=Object.freeze(["When the user explicitly requests Vapor components, use matching named exports from the installed `@realsee/vapor` package for every applicable DOM control; importing `Vapor` or `vapor.css` alone does not satisfy the request.","Do not replace an available Vapor component with raw HTML or a locally styled replica; use raw DOM only for semantics or capabilities absent from the installed Vapor declarations, and record that gap.","Before implementing Vapor UI, read `node_modules/@realsee/vapor/AI_CONTEXT.md`, `llms.txt`, `vapor.d.ts`, and the selected component declarations; never invent component names or props.","Use the installed `ai_guides` only for component discovery and composition recipes; the target project's root `vapor.d.ts` and selected component declarations are the API contract, and conflicts resolve in favor of installed declarations.","Keep the Five canvas in its dedicated sibling host outside `Vapor`; Vapor layout components own DOM chrome only, even when an external recipe nests a placeholder VR component inside a stack."]),wr=Object.freeze(["The current generated baseline is `@realsee/vapor@0.2.12`.","The 0.2.12 baseline requires exactly one executable `@realsee/vapor/vapor.css` import, not `vapor.scss`","Choose by product semantics before styling:","`HStack` arranges DOM horizontally, `VStack` vertically and `ZStack` overlays DOM children.","The 0.2.12 match union is exact:","if both represent the same physical inset, de-duplicate rather than adding it twice."]),Er=Object.freeze(["For an explicit Vapor request, executable source must import and render the matching non-provider Vapor component; the provider and stylesheet alone are insufficient.","exactly one executable `@realsee/vapor/vapor.css` import and no `vapor.scss` or copied internal stylesheet;","the `Vapor` provider rectangle matches the DOM overlay host","safe-area behavior uses the declared source/merge rule without double-counting","correct portal order, focus entry/return, Escape/backdrop behavior and close reasons;","no application stylesheet overrides `.vapor-*` internals","typecheck validates every component prop, compound child and symbol"]),Sr=/\bimport\s*['"]@realsee\/vapor\/vapor\.css['"]\s*;?/,Ae=r=>r.match(new RegExp(Sr.source,"g"))?.length??0,z=(r,t)=>new RegExp(`\\bimport\\s*\\{[^}]*\\b${t}\\b[^}]*\\}\\s*from\\s*['"]@realsee/vapor['"]`).test(r),U=(r,t)=>new RegExp(`<${t}(?:\\s|/?>)`).test(r),kr=Object.freeze(["Spatial capability resolution order: installed @realsee/dnalogel preset -> native Five -> Three.js.","Tags, hotspots, and anchors default to PanoTagPlugin with GET /open/v3/hot/list.","Normalize optional OpenAPI configuration fields such as `version`, `globalConfig` and `contentTypeConfig`: treat `null` as absent before validating the installed SDK contract.","Load ordinary PanoTag point/plane tags after `five.load` without waiting for `five.model.loaded`; gate only model-dependent tag content or anchors on model readiness.","Tours use Five State snapshots with GET /open/v3/observerpicture/search.","Model annotations default to ModelMakerPlugin with GET /open/v3/annotation/search.","Routes use GuideLinePlugin for rendering and shape-selected CruisePlugin or MovePlugin playback with GET /open/v3/route/search.","Tour adapters read `data.list`, map each validated record to `Partial<State>` with the active `workCode`, and never cast an observer-picture record directly to `State`.","Model annotation adapters read `data.list`, validate `object_data` for `box`, `triangles` or `prism`, and pass `{ list }` to ModelMaker only for a model-capable Work.","Feature API, schema, or plugin failures disable only the affected feature; keep the base Viewer usable and never runtime-fallback to raw data or a custom Three implementation.","Scope tour, ModelMaker, GuideLine, Cruise and Move work to an active `workCode`/generation token; abort fetches, ignore stale continuations, pause the owned playback controller, remove hooks, clear GuideLine data, wait for pending loads, then let identity-safe unregister dispose each registered controller exactly once before `five.dispose()`.","SDK-shaped custom data requires an explicit developer opt-out from Open Platform data.","VR spatial tag (spatial annotation) is the umbrella concept","Classify content, renderer, anchor, and lifecycle/data ownership","Never infer PanoTag from","Never directly edit `work.json`, downloaded Work, or customer Work"]),R=Object.freeze(["dnalogel 3.81.2 route playback is shape-selected: a validated pano group uses `CruisePlugin` `{ panoIndexList }`, while a validated model-space curve uses `MovePlugin` `{ path }`; `GuideLinePlugin` renders route geometry and is not playback input.","`CruisePlugin` accepts exactly one of `panoIndexList` or `keyframes`; its installed union has no type discriminator.","OpenAPI route records are never passed raw: rename validated style config only for GuideLine `lines`, and never pass GuideLine `PathItem` arrays or style objects to `CruisePlugin` or `MovePlugin`.","If a source record contains both `pano_group` and `path`, the route adapter must choose product semantics explicitly and pass only the selected geometry branch to GuideLine.","`GuideLinePlugin.load` waits for model readiness; `CruisePlugin` waits only for Work and observers; `MovePlugin` enters Model and awaits Five readiness when playback starts.","Reject empty routes, unknown pano indexes, unsupported curves, non-finite coordinates, timing or speed, and ambiguous multi-segment Move paths before plugin load.","Route cleanup pauses the owned playback controller before hook removal and disposal; `clear` belongs to GuideLine, is not a public Cruise operation, and GuideLine disposal does not own an external or embedded Cruise controller automatically.","Do not invent `RoutePlugin`, `PanoRove`, `stop()`, or a universal route controller; use only the exports and methods declared by the installed package."]),Oe=Object.freeze(["Floorplans default to ModelFloorplanPlugin with GET /open/v3/floorplan/plugins.","`data.format_floorplan_url`, not `format_model_url`, resolves to the preferred `{ version, data: FloorplanServerData }` plugin input.","Expose `capabilities.modelFloorplan` only after validating the complete wrapper and nested floorplan shape; missing, empty, malformed or failed floorplan data must leave the base Viewer usable.","The default tabs are `\u6F2B\u6E38` for `Panorama` and `\u4E09\u7EF4\u6A21\u578B` for `Mapview`; do not expose `Topview` as `\u5730\u56FE`.","Add the `\u5E73\u9762\u56FE` tab only after the capability validates and `ModelFloorplanPlugin.load` succeeds.","Configure `ModelFloorplanPlugin` with `autoShowEnable: false`; the application tab owns explicit `show()`/`hide()`, uses `modelOpacity: 0` while the floorplan is visible, and restores the Five model surface on hide, failure, and disposal.","Resolve the Five canvas parent and call `ModelFloorplanPlugin.appendTo(wrapper)` before `load`; `five.addExtraElement` registers input handling but does not mount the plugin DOM.","For multi-floor Work, ModelFloorplanPlugin and Five pano/model floor synchronization are the floor source of truth; do not invent a React-only `floorIndex`.","Floorplan show/hide, hooks, pending load and identity-safe disposal must finish before `five.dispose()`."]),yr=Object.freeze(Oe.slice(0,3)),br="The complete model floorplan contract lives in",_r="The complete route playback contract lives in",Ar=Object.freeze([R[0],R[7],_r]),Te=Object.freeze(["Vapor","Text","TitleBar","ZStack","VStack","Layer"]),Re=Object.freeze(["Button","Font","HStack","VStack"]),je=Object.freeze(["title?: string | null",'className="viewer-vapor"','width="100%"','height="100%"','theme="arc_beauty"',"useSaveAreaInsets()","useDimension()","<Layer vignetting />","labelMaxWidth={titleMaxWidth}",'className="viewer-controls"',"align={{ horizontal: 'center' }}",'className="viewer-status"','appearance="solid"',"role={error ? 'alert' : 'status'}","aria-live={error ? 'assertive' : 'polite'}","env(safe-area-inset-${side}, 0px)"]),Pe=Object.freeze(['role="group"',"useDimension()","width={controlWidth}","height={compact ? '3rem' : '5rem'}","align={{ horizontal: 'space-around' }}",'appearance="shadow"',"weight={selected ? 'bold' : 'regular'}","opacity={selected ? '100' : '80'}",'className="mode-switcher-indicator"',"onClick={() => void changeTab(option.value)}"]),Le=Object.freeze([".dom-overlay",".viewer-controls",".mode-switcher","pointer-events: auto",".mode-switcher-indicator","bottom: -0.5rem","width: 3.375rem","height: 0.125rem","background-image: linear-gradient(",".viewer-status"]),Ne=Object.freeze(["viewer-glass",'className="viewer-topbar"',"<h1"]),xe=Object.freeze(["Segmented","viewer-segmented","viewer-glass",'appearance="horizontal"','maxWidth="100%"']),Me=Object.freeze(["--viewer-glass-","--viewer-control-size",".vapor-segmented","backdrop-filter"]);async function re(r){return JSON.parse(await E(r,"utf8"))}async function Or(){let r=process.platform==="win32"?"npm.cmd":"npm",{stdout:t}=await ur(r,["--version"],{encoding:"utf8"});return t.trim()}async function $e(r,t){let e=[];for(let[n,i,o]of[["packageManager",t.packageManager,ae],["engines.node",t.engines?.node,te],["engines.npm",t.engines?.npm,se]])i!==o&&e.push(a("error","toolchain.version",`package.json ${n} \u5FC5\u987B\u4E3A ${o}\uFF0C\u5F53\u524D\u4E3A ${i??"\u672A\u58F0\u660E"}`));for(let n of[".nvmrc",".node-version"]){let i=l.join(r,n);if(await u(i)){let o=(await E(i,"utf8")).trim();o!==B&&e.push(a("error","toolchain.node-pin",`${n} \u5FC5\u987B\u56FA\u5B9A Node ${B}\uFF0C\u5F53\u524D\u4E3A ${o||"\u7A7A"}`))}}let s=l.join(r,".npmrc");if(await u(s)){let n=new Set((await E(s,"utf8")).split(/\r?\n/).map(i=>i.trim()).filter(Boolean));for(let i of["engine-strict=true","package-lock=true","save-prefix=>="])n.has(i)||e.push(a("error","toolchain.npmrc",`.npmrc \u5FC5\u987B\u5305\u542B ${i}`))}return e}async function Tr(r,t){let e=l.join(r,"package-lock.json");if(!await u(e))return[];try{let s=await re(e),n=s.packages?.[""],i=[];if(s.lockfileVersion!==3||!n)return[a("error","lockfile.invalid","package-lock.json \u5FC5\u987B\u4F7F\u7528 lockfileVersion 3 \u5E76\u5305\u542B\u6839\u5305\u8BB0\u5F55\u3002")];(s.name!==t.name||n.name!==t.name||n.version!==t.version)&&i.push(a("error","lockfile.package","package-lock.json \u6839\u5305\u540D\u79F0\u6216\u7248\u672C\u4E0E package.json \u4E0D\u4E00\u81F4\u3002"));for(let[o,m,f,p]of[["dependency",n.dependencies,t.dependencies,G],["dev-dependency",n.devDependencies,t.devDependencies,q]])for(let[w,y]of Object.entries(f??{})){m?.[w]!==y&&i.push(a("error",`lockfile.${o}`,`package-lock.json \u4E0E package.json \u4E0D\u4E00\u81F4\uFF1A${w}@${y}\uFF1B\u8BF7\u8FD0\u884C npm run deps:relock\u3002`));let b=p[w];if(!b)continue;let c=s.packages?.[`node_modules/${w}`]?.version;if(!c){i.push(a("error","lockfile.resolved",`package-lock.json \u7F3A\u5C11\u76F4\u63A5\u4F9D\u8D56 ${w} \u7684\u89E3\u6790\u7248\u672C\u3002`));continue}if(w in V){c!==b&&i.push(a("error","lockfile.locked",`package-lock.json \u5FC5\u987B\u5C06 ${w} \u89E3\u6790\u4E3A ${b}\uFF0C\u5F53\u524D\u4E3A ${c}\u3002`));continue}let d=H(y);d&&(!P(c,b)||!P(c,d))&&i.push(a("error","lockfile.minimum",`${w} \u5728 package-lock.json \u4E2D\u89E3\u6790\u4E3A ${c}\uFF0C\u5FC5\u987B\u540C\u65F6\u6EE1\u8DB3\u751F\u6210\u5668\u57FA\u7EBF >=${b} \u4E0E\u58F0\u660E ${y}\u3002`))}return i}catch(s){return[a("error","lockfile.invalid","package-lock.json \u65E0\u6CD5\u89E3\u6790\u3002",String(s))]}}async function x(r){let t=[];for(let e of await lr(r,{withFileTypes:!0})){let s=l.join(r,e.name);e.isDirectory()?t.push(...await x(s)):e.isFile()&&/\.(?:[cm]?[jt]sx?)$/.test(e.name)&&t.push(s)}return t}async function Fe(r,t){let e=l.join(r,t);if(!await u(e))return[];let s=k(await E(e,"utf8")),n=[];for(let m of mr)s.includes(m)||n.push(a("error","architecture.custom-plugin-example",`${t} \u7F3A\u5C11\u81EA\u5B9A\u4E49\u63D2\u4EF6\u6240\u6709\u6743\u6807\u8BB0\uFF1A${m}`));for(let m of vr)s.includes(m)&&n.push(a("error","architecture.custom-plugin-example",`${t} \u7981\u6B62\u62E5\u6709 Five \u8FD0\u884C\u65F6\u6216\u7B2C\u4E8C\u6E32\u67D3\u5FAA\u73AF\uFF1A${m}`));let i=t.startsWith("templates/")?"templates/viewer/src/features/tags/PanoTagFeature.ts":"src/features/tags/PanoTagFeature.ts",o=l.join(r,i);if(await u(o)){let m=k(await E(o,"utf8"));for(let f of["from '@realsee/dnalogel/libs/PanoTagPlugin'","const PLUGIN_NAME = 'dnalogel.panoTag'","const candidate = PanoTagPlugin(","plugins.register(PLUGIN_NAME, candidate)","candidate.dispose()","async load(panoTagList: Tags['tagList'])","plugin.load({ tagList: panoTagList })","const disposeError = plugins.unregister(PLUGIN_NAME, plugin)"])m.includes(f)||n.push(a("error","architecture.pano-tag-contract",`${i} \u7F3A\u5C11 PanoTag \u517C\u5BB9\u6027\u3001\u663E\u5F0F\u7C7B\u578B\u6216\u6240\u6709\u6743\u6807\u8BB0\uFF1A${f}`));for(let f of["model_file","as Tags['tagList']","from '../../vr/work/WorkSource'"])m.includes(f)&&n.push(a("error","architecture.pano-tag-contract",`${i} \u542B\u975E\u6807\u51C6 content type \u6216\u672A\u9A8C\u8BC1\u7684 PanoTag \u8F6C\u6362\uFF1A${f}`))}return n}async function Ie(r,t){let e=[];for(let s of t){let n=l.join(r,s);if(!await u(n))continue;let i=await E(n,"utf8");for(let o of gr)i.includes(o)||e.push(a("error","agents.image-resource-guidance",`${s} \u7F3A\u5C11\u56FE\u7247\u8D44\u6E90\u57DF\u540D\u3001Five URL transform\u3001\u6570\u636E\u4E07\u8C61\u6216 CORS \u89C4\u5219\uFF1A${o}`))}return e}async function Ce(r,t){let e=[];for(let s of t){let n=l.join(r,s);if(!await u(n))continue;let i=await E(n,"utf8");for(let o of hr)i.includes(o)||e.push(a("error","agents.vapor-guidance",`${s} \u7F3A\u5C11 Vapor \u771F\u5B9E\u7EC4\u4EF6\u9009\u62E9\u3001\u58F0\u660E\u6838\u5BF9\u6216 raw DOM \u4F8B\u5916\u89C4\u5219\uFF1A${o}`))}return e}async function De(r,t,e){let s=[];for(let[n,i,o,m]of[[t,wr,"agents.vapor-reference","\u7248\u672C\u5316\u8D44\u6599\u8DEF\u7531\u3001\u7EC4\u4EF6\u9009\u578B\u3001\u5E03\u5C40\u6216 safe-area \u89C4\u5219"],[e,Er,"agents.vapor-acceptance","\u8FD0\u884C\u65F6 Vapor \u9A8C\u6536\u89C4\u5219"]]){let f=l.join(r,n);if(!await u(f))continue;let p=await E(f,"utf8");for(let w of i)p.includes(w)||s.push(a("error",o,`${n} \u7F3A\u5C11 ${m}\uFF1A${w}`))}return s}async function Ge(r,t,e){let s=[];for(let i of t){let o=l.join(r,i);if(!await u(o))continue;let m=await E(o,"utf8");for(let f of kr)m.includes(f)||s.push(a("error","agents.spatial-tag-guidance",`${i} \u7F3A\u5C11\u7A7A\u95F4\u6807\u7B7E\u51B3\u7B56\u6216\u53EA\u8BFB\u89C4\u5219\uFF1A${f}`))}let n=l.join(r,e);if(await u(n)){let i=await E(n,"utf8");for(let o of["Spatial capability resolution order: installed @realsee/dnalogel preset -> native Five -> Three.js.","Tags, hotspots, and anchors default to PanoTagPlugin with GET /open/v3/hot/list.","Normalize optional OpenAPI configuration fields such as `version`, `globalConfig` and `contentTypeConfig`: treat `null` as absent before validating the installed SDK contract.","Load ordinary PanoTag point/plane tags after `five.load` without waiting for `five.model.loaded`; gate only model-dependent tag content or anchors on model readiness.","Tours use Five State snapshots with GET /open/v3/observerpicture/search.","Model annotations default to ModelMakerPlugin with GET /open/v3/annotation/search.","Routes use GuideLinePlugin for rendering and shape-selected CruisePlugin or MovePlugin playback with GET /open/v3/route/search.","Tour adapters read `data.list`, map each validated record to `Partial<State>` with the active `workCode`, and never cast an observer-picture record directly to `State`.","Model annotation adapters read `data.list`, validate `object_data` for `box`, `triangles` or `prism`, and pass `{ list }` to ModelMaker only for a model-capable Work.","Feature API, schema, or plugin failures disable only the affected feature; keep the base Viewer usable and never runtime-fallback to raw data or a custom Three implementation.","Scope tour, ModelMaker, GuideLine, Cruise and Move work to an active `workCode`/generation token; abort fetches, ignore stale continuations, pause the owned playback controller, remove hooks, clear GuideLine data, wait for pending loads, then let identity-safe unregister dispose each registered controller exactly once before `five.dispose()`.","SDK-shaped custom data requires an explicit developer opt-out from Open Platform data.","`model_file` is not canonical","`ModelTVVideoPlugin`","`CSS3DRenderPlugin.create3DDomContainer`","`PanoVideoPlugin`","generic `tags`, `tagList` and `tag_list`","`ServerData` with `box`, `triangles` or `prism` items"])i.includes(o)||s.push(a("error","agents.spatial-tag-capabilities",`${e} \u7F3A\u5C11\u7A7A\u95F4\u80FD\u529B\u4F18\u5148\u7EA7\u3001\u6570\u636E\u6E90\u6216\u63D2\u4EF6\u7C7B\u578B\u8DEF\u7531\uFF1A${o}`))}return s}async function Ve(r,t,e){let s=[],n=[...t.map(i=>[i,i.endsWith("SKILL.md")?Ar:R]),[`${e}/references/dnalogel-api-capabilities.md`,R],[`${e}/references/five-runtime.md`,R.slice(4,6)],[`${e}/references/plugin-lifecycle.md`,R.slice(6,8)],[`${e}/references/official-docs.md`,R.slice(2,4)]];for(let[i,o]of n){let m=l.join(r,i);if(!await u(m))continue;let f=await E(m,"utf8");for(let p of o)f.includes(p)||s.push(a("error","agents.route-guidance",`${i} \u7F3A\u5C11\u70B9\u4F4D/\u81EA\u5B9A\u4E49\u8DEF\u7EBF\u5206\u6D41\u3001\u6821\u9A8C\u6216\u751F\u547D\u5468\u671F\u89C4\u5219\uFF1A${p}`))}return s}async function Ke(r,t,e){let s=[],n=[[`${e}/references/dnalogel-api-capabilities.md`,Oe],...t.map(i=>[i,yr]),...["five-runtime.md","official-docs.md","plugin-lifecycle.md","ui-and-validation.md"].map(i=>[`${e}/references/${i}`,[br]])];for(let[i,o]of n){let m=l.join(r,i);if(!await u(m))continue;let f=await E(m,"utf8");for(let p of o)f.includes(p)||s.push(a("error","agents.floorplan-guidance",`${i} \u7F3A\u5C11\u5E73\u9762\u56FE endpoint\u3001\u6821\u9A8C\u3001\u52A8\u6001 tab\u3001\u591A\u697C\u5C42\u6216\u6E05\u7406\u89C4\u5219\uFF1A${p}`))}return s}async function We(r,t){let e=[],s=[[t.validator,["export function asModelFloorplanServerData(","!isInteger(value.version)","value.version < 0","!isFloorplanServerData(value.data)","value.outlines.length > 0","value.floor_datas.length > 0","isArrayOf(value.rooms, isRoom)","isRules(value.rules)","value.entrance === null || isEntrance(value.entrance)","isBounding(value.bounding)","isArrayOf(value.observers, isObserver)","isArrayOf(value.doors, isDoor)"]],[t.types,["WorkCapabilities","modelFloorplan?: ModelFloorplanServerData","ModelFloorplanClient","RealseeSpatialClient","extends WorkDetailClient, ModelFloorplanClient"]],[t.client,["implements RealseeSpatialClient","FLOORPLAN_PLUGINS_PATH = '/open/v3/floorplan/plugins'","asRecord(descriptor.data).format_floorplan_url","floorplanUrl.protocol !== 'https:'","floorplanUrl.username","floorplanUrl.password","redirect: 'error'","asModelFloorplanServerData(payload)"]],[t.service,[".getModelFloorplan(resourceCode, { signal })",".catch((error: unknown) =>","modelFloorplan ? { capabilities: { modelFloorplan } } : {}"]],[t.workSource,["modelFloorplan?: ModelFloorplanServerData","asModelFloorplanServerData(","asRecord(envelope.capabilities).modelFloorplan","modelFloorplan ? { capabilities: { modelFloorplan } } : {}"]],[t.feature,["@realsee/dnalogel/libs/floorplan/ModelFloorplanPlugin","dnalogel.modelFloorplan","ModelFloorplanPlugin(five, { autoShowEnable: false })","plugins.register(PLUGIN_NAME, candidate)","candidate.dispose()","const modelSurface = five.getElement()","const wrapper = modelSurface?.parentElement","plugin.appendTo(wrapper)","plugin.load(serverData)","modelOpacity: 0","plugin.hide({ userAction: true })",".finally(restoreModelSurface)","if (!state.visible) restoreModelSurface()","plugins.unregister(PLUGIN_NAME, plugin)","stopStateChange()","if (disposeRequested) finalize()","if (!loading) finalize()"]],[t.app,["payload.capabilities?.modelFloorplan","if (modelFloorplan)","createModelFloorplanFeature(five, plugins)","await currentFloorplan.load(modelFloorplan)","currentFloorplan.dispose()","setFloorplanFeature(floorplan)","floorplan?.dispose()"]],[t.modeSwitcher,["floorplan?.subscribeVisibility","await floorplan.show()","if (floorplan?.visible) await floorplan.hide()","floorplan?.visible ? 'floorplan' : tabFromFiveMode(five.currentMode)","Five.Mode.Panorama","Five.Mode.Mapview"]]];for(let[o,m]of s){let f=l.join(r,o);if(!await u(f)){e.push(a("error","architecture.floorplan-missing",`\u7F3A\u5C11\u5E73\u9762\u56FE\u5408\u540C\u6587\u4EF6\uFF1A${o}`));continue}let p=k(await E(f,"utf8"));for(let w of m)p.includes(w)||e.push(a("error","architecture.floorplan-contract",`${o} \u7F3A\u5C11\u5E73\u9762\u56FE\u6821\u9A8C\u3001capability \u6216\u751F\u547D\u5468\u671F\u6807\u8BB0\uFF1A${w}`))}let n=l.join(r,t.feature);if(await u(n)){let o=k(await E(n,"utf8"));for(let[v,h]of[[/plugin\.hooks\.on\(\s*['"]show['"]/,"show hook"],[/plugin\.hooks\.on\(\s*['"]hide['"]/,"hide hook"],[/plugin\.hooks\.on\(\s*['"]stateChange['"]/,"stateChange hook"]])v.test(o)||e.push(a("error","architecture.floorplan-lifecycle",`${t.feature} \u7F3A\u5C11 ${h}\u3002`));/autoShowEnable\s*:\s*true/.test(o)&&e.push(a("error","architecture.floorplan-lifecycle",`${t.feature} \u5FC5\u987B\u4FDD\u6301 autoShowEnable: false\u3002`));let m=o.indexOf("plugin.appendTo(wrapper)"),f=o.indexOf("plugin.load(serverData)");(m<0||f<m)&&e.push(a("error","architecture.floorplan-mount",`${t.feature} \u5FC5\u987B\u5148\u628A\u63D2\u4EF6 DOM appendTo Five \u5BBF\u4E3B\uFF0C\u518D\u52A0\u8F7D format_floorplan_url \u6570\u636E\u3002`));let p=o.indexOf("stopShow()"),w=o.indexOf("stopHide()"),y=o.indexOf("stopStateChange()"),b=o.indexOf("if (!loading) finalize()"),c=o.indexOf("const finalize ="),d=o.indexOf("restoreModelSurface()",c),g=o.indexOf("plugins.unregister(PLUGIN_NAME, plugin)",c);(p<0||w<p||y<w||b<y||c<0||d<c||g<d)&&e.push(a("error","architecture.floorplan-cleanup",`${t.feature} \u5FC5\u987B\u5728 hide\u3001\u5931\u8D25\u4E0E\u9500\u6BC1\u65F6\u6062\u590D Five model surface\uFF0C\u5E76\u5148\u79FB\u9664\u5168\u90E8 hooks\uFF0C\u518D\u6309 pending load \u72B6\u6001 identity-safe \u6E05\u7406\u3002`))}let i=l.join(r,t.modeSwitcher);if(await u(i)){let o=k(await E(i,"utf8"));for(let[f,p]of[[/type\s+ViewerTab\s*=\s*['"]tour['"]\s*\|\s*['"]model['"]\s*\|\s*['"]floorplan['"]/,"\u4E09\u4E2A ViewerTab"],[/value\s*:\s*['"]tour['"]\s*,\s*text\s*:\s*['"]漫游['"]/,"Panorama \u6F2B\u6E38 tab"],[/value\s*:\s*['"]model['"]\s*,\s*text\s*:\s*['"]三维模型['"]/,"Mapview \u4E09\u7EF4\u6A21\u578B tab"],[/\.\.\.\(\s*floorplan\s*\?\s*\[\{\s*value\s*:\s*['"]floorplan['"](?:\s+as\s+const)?\s*,\s*text\s*:\s*['"]平面图['"]\s*\}\]\s*:\s*\[\]\s*\)/,"\u6761\u4EF6\u5E73\u9762\u56FE tab"]])f.test(o)||e.push(a("error","architecture.floorplan-tabs",`${t.modeSwitcher} \u7F3A\u5C11 ${p}\u3002`));(o.match(/floorplan\?\.visible\s*\?\s*['"]floorplan['"]\s*:\s*tabFromFiveMode\(five\.currentMode\)/g)?.length??0)<2&&e.push(a("error","architecture.floorplan-tabs",`${t.modeSwitcher} \u5FC5\u987B\u5728\u521D\u59CB\u72B6\u6001\u548C\u5207\u6362\u5931\u8D25\u540E\u90FD\u6309\u63D2\u4EF6\u771F\u5B9E\u53EF\u89C1\u6027\u540C\u6B65 Tab\u3002`)),(o.includes("Five.Mode.Topview")||/text\s*:\s*['"]地图['"]/.test(o))&&e.push(a("error","architecture.floorplan-tabs",`${t.modeSwitcher} \u4E0D\u5F97\u628A Topview \u66B4\u9732\u4E3A\u5730\u56FE tab\u3002`))}for(let[o,m]of[[t.browserTsconfig,'"include": ["src", "shared", "vite.config.ts"]'],[t.serverTsconfig,'"include": ["server", "shared"]']]){let f=l.join(r,o);if(!await u(f))continue;(await E(f,"utf8")).includes(m)||e.push(a("error","tooling.floorplan-include",`${o} \u5FC5\u987B\u5305\u542B shared floorplan validator\uFF1A${m}`))}return e}function Rr(r){let t=new Map;for(let e of r.split(/\r?\n/)){let s=e.trim();if(!s||s.startsWith("#"))continue;let n=/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(s);n&&t.set(n[1],n[2].trim())}return t}async function ze(r,t){let e=[],s=l.join(r,".env.example");if(await u(s)){let f=Rr(await E(s,"utf8"));for(let p of ie)f.has(p)||e.push(a("error","env.required-key",`.env.example \u7F3A\u5C11 ${p}\u3002`));for(let[p,w]of f)p.startsWith("VITE_WORK_")&&e.push(a("error","env.legacy-work-url",`.env.example \u4E0D\u5F97\u58F0\u660E\u8DEF\u7531\u6A21\u5F0F\u4E0D\u9700\u8981\u7684 ${p}\u3002`)),p.startsWith("VITE_")&&_e(p)&&e.push(a("error","env.client-secret",`.env.example \u7981\u6B62\u58F0\u660E\u6D4F\u89C8\u5668\u53EF\u89C1\u7684\u5BC6\u94A5\u53D8\u91CF ${p}\u3002`)),_e(p)&&!le.includes(p)&&w&&w!=="*"&&e.push(a("error","env.secret",`.env.example \u7684 ${p} \u53EA\u80FD\u4E3A\u7A7A\u6216\u4F7F\u7528 * \u5360\u4F4D\uFF0C\u4E0D\u80FD\u5305\u542B\u771F\u5B9E\u5BC6\u94A5\u3002`));for(let p of ne){let w=f.get(p);w!==void 0&&w!==""&&w!=="*"&&e.push(a("error","env.credential",`.env.example \u7684 ${p} \u53EA\u80FD\u4E3A\u7A7A\u6216\u4F7F\u7528 * \u5360\u4F4D\u3002`))}}await u(l.join(r,".env.server.example"))&&e.push(a("error","env.legacy-file","\u73AF\u5883\u914D\u7F6E\u5DF2\u5408\u5E76\u5230 .env.example\uFF0C\u4E0D\u5F97\u4FDD\u7559 .env.server.example\u3002"));let n=l.join(r,"src");if(await u(n))for(let f of await x(n)){let p=k(await E(f,"utf8")),w=ee(p);w&&e.push(a("error","env.client-server-only-reference",`${l.relative(r,f)} \u4E0D\u5F97\u8BFB\u53D6\u6216\u5F15\u7528 server-only \u914D\u7F6E\u6216\u51ED\u636E\uFF1A${w}\u3002`))}let i=l.join(r,"src","vr","work","WorkSource.ts");if(await u(i)){let f=k(await E(i,"utf8"));for(let p of["resourceCodeFromPathname","window.location.pathname","segments.length === 0","segments.length !== 1",": '/api/work'","`/api/work/${encodeURIComponent(resourceCode)}`","asExplicitPanoTagList","hasCanonicalPanoTagData(value.contentType, value.data)","hasCanonicalPanoTagAnchor(value)","asRecord(envelope.presetData)","presetData: { panoTagList }","displayName: string","displayName: displayNameFrom(work.name) ?? workCode","new RouteWorkSource()"])f.includes(p)||e.push(a("error","architecture.resource-route",`WorkSource \u7F3A\u5C11\u9ED8\u8BA4/\u517C\u5BB9\u8DEF\u7531\u3001\u540C\u6E90 API \u6216\u663E\u5F0F preset \u6807\u8BB0\uFF1A${p}`));for(let p of["VITE_WORK_URL","/work.json","envelope.tags","envelope.tagList","envelope.tag_list","payload.tags"])f.includes(p)&&e.push(a("error","architecture.resource-route",`WorkSource \u4E0D\u5F97\u4F9D\u8D56\u65E7 Work URL\u3001\u672C\u5730 Work \u6587\u4EF6\u6216\u628A generic tags \u6620\u5C04\u5230 PanoTag\uFF1A${p}`))}for(let f of["vite.config.ts","vite.config.js","vite.config.mjs"]){let p=l.join(r,f);if(!await u(p))continue;let w=k(await E(p,"utf8")),y=ee(w);(y||/envPrefix\s*:\s*[^\n]*REALSEE_/.test(w))&&e.push(a("error","env.vite-secret",`${f} \u4E0D\u5F97\u628A server-only \u914D\u7F6E\u3001AK/SK/token \u6216 REALSEE_* \u524D\u7F00\u6CE8\u5165\u6D4F\u89C8\u5668${y?`\uFF1A${y}`:"\u3002"}`));let b=w.match(/\bhost\s*:\s*['"]127\.0\.0\.1['"]/g)?.length??0,c=w.match(/\bport\s*:\s*8087\b/g)?.length??0,d=w.match(/\bstrictPort\s*:\s*true\b/g)?.length??0,g=/target\s*:\s*['"]http:\/\/127\.0\.0\.1:8088['"]/.test(w);(b<2||c<2||d<2||!g)&&e.push(a("error","architecture.dev-route",`${f} \u5FC5\u987B\u8BA9 Vite server/preview \u4F7F\u7528 strict IPv4 127.0.0.1:8087\uFF0C\u5E76\u628A /api \u4EE3\u7406\u5230 Fastify 127.0.0.1:8088\u3002`))}let o=[["server/types.ts",["ServerConfig","WorkEnvelope","WorkDetailClient","WorkService"]],["server/config.ts",["loadServerConfig","REALSEE_GATEWAY_APP_KEY","REALSEE_GATEWAY_APP_SECRET","REALSEE_DEFAULT_RESOURCE_CODE","gatewayEndpoint.protocol !== 'https:'","): ServerConfig {"]],["server/check-config.ts",["loadServerConfig()","BFF server configuration is valid."]],["server/realsee-open-api.ts",["RealseeOpenApiClient","implements RealseeSpatialClient","ACCESS_TOKEN_PATH = '/auth/access_token'","'application/x-www-form-urlencoded'","WORK_INFO_PATH = '/open/v3/vr/info'","Authorization: accessToken","resource_code: resourceCode","UNAUTHORIZED_UPSTREAM_CODES","TOKEN_REFRESH_SKEW_MS"]],["server/work-service.ts",["createWorkService","): WorkService {","getWorkByResourceCode","openApiClient.getWorkDetail(resourceCode, { signal })"]],["server/work-route.ts",["'/api/work'","'/api/work/:resourceCode'","getWorkByResourceCode(resourceCode, {","signal: requestAbort.signal","WORK_UPSTREAM_FAILED"]],["server/app.ts",["Fastify({ logger })","'/api/health'","fastifyStatic","serveFrontend","reply.sendFile('index.html')","Promise<FastifyInstance>"]],["server/index.ts",["loadServerConfig","RealseeOpenApiClient","createWorkService","createApp","app.listen({ host: config.host, port: config.port })"]]];for(let[f,p]of o){let w=l.join(r,f);if(!await u(w)){e.push(a("error","architecture.bff-missing",`\u7F3A\u5C11 Fastify BFF \u6587\u4EF6\uFF1A${f}`));continue}let y=k(await E(w,"utf8"));for(let b of p)y.includes(b)||e.push(a("error","architecture.bff-contract",`${f} \u7F3A\u5C11 BFF \u89E3\u8026\u6216 OpenAPI \u5951\u7EA6\uFF1A${b}`))}let m=l.join(r,t);if(await u(m)){let f=new Set((await E(m,"utf8")).split(/\r?\n/).map(p=>p.trim()).filter(Boolean));for(let p of[".env*","!.env.example"])f.has(p)||e.push(a("error","env.gitignore",`${t} \u5FC5\u987B\u5305\u542B ${p}\u3002`))}return e}async function jr(r,t){return t.name!==Y?!1:Object.hasOwn(t.bin??{},"vr-cli")||await u(l.join(r,"bin","vr-cli.mjs"))||await u(l.join(r,"templates","base"))}async function Pr(r,t){let e=[];t.private!==!1&&e.push(a("error","tool.private","\u53EF\u53D1\u5E03\u5DE5\u5177\u5FC5\u987B\u663E\u5F0F\u58F0\u660E private: false\u3002"));let s=Object.entries(t.bin??{});(s.length!==1||s[0]?.[0]!=="vr-cli"||s[0]?.[1]!=="bin/vr-cli.mjs")&&e.push(a("error","tool.bin","\u5F00\u53D1\u5DE5\u5177\u5FC5\u987B\u53EA\u66B4\u9732 vr-cli -> bin/vr-cli.mjs\u3002")),((typeof t.repository=="string"?t.repository:t.repository?.url)??null)!==null&&e.push(a("error","tool.repository",null===null?"\u5F53\u524D\u53D1\u5E03 profile \u8981\u6C42 package.json \u7701\u7565 repository\u3002":`package.json repository \u5FC5\u987B\u4E3A ${null}\u3002`));let i=t.publishConfig?.registry??null,o=t.publishConfig?.access??null;(i!==F||o!==N)&&e.push(a("error","tool.publish-profile",`package.json publishConfig \u5FC5\u987B\u5339\u914D\u5F53\u524D\u53D1\u5E03 profile\uFF1Aregistry=${F}\uFF0Caccess=${N??"\u672A\u8BBE\u7F6E"}\u3002`)),e.push(...await $e(r,t));let m=["README.md",...await u(l.join(r,".git"))?[".nvmrc",".node-version",".npmrc"]:[],"agent-assets/AGENTS.md","bin","bin/vr-cli.mjs",...N==="public"?["dist/cli.mjs"]:["src","src/core/spatial-policy.mjs"],"templates/base","templates/base/README.md","templates/base/.editorconfig","templates/base/.env.example","templates/base/.gitignore.template","templates/base/.nvmrc","templates/base/.node-version","templates/base/.npmrc.template","templates/base/.prettierignore","templates/base/eslint.config.mjs","templates/base/package-lock.json","templates/base/prettier.config.mjs","templates/base/tsconfig.server.json","templates/base/server/types.ts","templates/base/server/config.ts","templates/base/server/check-config.ts","templates/base/server/realsee-open-api.ts","templates/base/server/work-service.ts","templates/base/server/work-route.ts","templates/base/server/app.ts","templates/base/server/index.ts","templates/base/shared/model-floorplan.ts","templates/base/src/vr/five/api.ts","templates/base/src/vr/five/fiveConfig.ts","templates/base/src/vr/five/SpatialRuntimeProvider.tsx","templates/base/src/vr/five/initialState.ts","templates/base/src/vr/plugins/dnalogel.ts","templates/base/src/vr/plugins/SpatialPluginRegistry.ts","templates/base/src/vr/plugins/examples/FeatureOwnedMeshPlugin.ts","templates/base/src/vr/work/WorkSource.ts","templates/viewer/src/app/App.tsx","templates/viewer/src/features/floorplan/ModelFloorplanFeature.ts","templates/viewer/src/features/navigation/ModeSwitcher.tsx","templates/viewer/src/features/tags/PanoTagFeature.ts","templates/viewer/src/ui/ViewerOverlay.tsx","skills/realsee-vr-app","skills/realsee-vr-app/references/five-api-capabilities.md","skills/realsee-vr-app/references/dnalogel-api-capabilities.md","skills/realsee-vr-app/references/five-runtime.md","skills/realsee-vr-app/references/official-docs.md","skills/realsee-vr-app/references/plugin-lifecycle.md","skills/realsee-vr-app/references/ui-and-validation.md","skills/realsee-vr-app/references/vapor-dom-layer.md","skills/realsee-image-resources/SKILL.md","skills/realsee-image-resources/references/cors-and-three-textures.md","skills/realsee-image-resources/references/data-wanxiang.md","skills/realsee-image-resources/references/five-image-url-transform.md","skills/realsee-image-resources/references/source-and-domain-policy.md","skills/threejs-r117-five/SKILL.md","skills/threejs-r117-five/references/r117-capabilities.md","skills/threejs-r117-five/references/r117-version-guards.md","skills/threejs-r117-five/references/five-ownership.md"];for(let v of m)await u(l.join(r,v))||e.push(a("error","tool.missing",`\u7F3A\u5C11\u5DE5\u5177\u8DEF\u5F84\uFF1A${v}`));for(let v of["templates/base/README.md","agent-assets/AGENTS.md"]){let h=l.join(r,v);if(!await u(h))continue;let S=await E(h,"utf8");S.includes("{{TOOL_VERSION}}")||e.push(a("error","tool.token",`${v} \u5FC5\u987B\u4F7F\u7528 {{TOOL_VERSION}} \u7248\u672C token\u3002`)),S.includes("__TOOL_VERSION__")&&e.push(a("error","tool.token",`${v} \u4E0D\u5F97\u4F7F\u7528\u4E0E Markdown \u7C97\u4F53\u51B2\u7A81\u7684 __TOOL_VERSION__ token\u3002`))}let f=l.join(r,"templates","base");if(await u(f)){let v=new Set(["package-lock.json","package.json","tsconfig.json","tsconfig.server.json"]);for(let h of await K(f))h.endsWith(".json")&&!v.has(h)&&e.push(a("error","tool.data-asset",`\u6A21\u677F\u4E0D\u5F97\u5206\u53D1\u672A\u7ECF\u660E\u786E\u5BA1\u6838\u7684 JSON/Work \u6570\u636E\uFF1Atemplates/base/${h}`))}e.push(...await Ge(r,["skills/realsee-vr-app/SKILL.md","agent-assets/AGENTS.md"],"skills/realsee-vr-app/references/dnalogel-api-capabilities.md")),e.push(...await Ve(r,["skills/realsee-vr-app/SKILL.md","agent-assets/AGENTS.md"],"skills/realsee-vr-app")),e.push(...await Ke(r,["agent-assets/AGENTS.md","skills/realsee-vr-app/SKILL.md"],"skills/realsee-vr-app")),e.push(...await We(r,{validator:"templates/base/shared/model-floorplan.ts",types:"templates/base/server/types.ts",client:"templates/base/server/realsee-open-api.ts",service:"templates/base/server/work-service.ts",workSource:"templates/base/src/vr/work/WorkSource.ts",feature:"templates/viewer/src/features/floorplan/ModelFloorplanFeature.ts",app:"templates/viewer/src/app/App.tsx",modeSwitcher:"templates/viewer/src/features/navigation/ModeSwitcher.tsx",browserTsconfig:"templates/base/tsconfig.json",serverTsconfig:"templates/base/tsconfig.server.json"})),e.push(...await Ie(r,["agent-assets/AGENTS.md","skills/realsee-vr-app/SKILL.md","skills/realsee-image-resources/SKILL.md"])),e.push(...await Ce(r,["agent-assets/AGENTS.md","skills/realsee-vr-app/SKILL.md","skills/realsee-vr-app/references/vapor-dom-layer.md"])),e.push(...await De(r,"skills/realsee-vr-app/references/vapor-dom-layer.md","skills/realsee-vr-app/references/ui-and-validation.md")),e.push(...await Fe(r,"templates/base/src/vr/plugins/examples/FeatureOwnedMeshPlugin.ts"));let p=l.join(r,"templates","base","src","main.tsx");if(await u(p)){let v=k(await E(p,"utf8"));(Ae(v)!==1||v.includes("@realsee/vapor/vapor.scss"))&&e.push(a("error","tool.vapor-css","\u9ED8\u8BA4\u6A21\u677F\u5FC5\u987B\u6070\u597D\u4E00\u6B21\u4EE5\u53EF\u6267\u884C side-effect import \u52A0\u8F7D @realsee/vapor/vapor.css\uFF0C\u4E14\u4E0D\u5F97\u4F7F\u7528 vapor.scss\u3002"))}let w=l.join(r,"templates","base","src","vr","work","WorkSource.ts");if(await u(w)){let v=k(await E(w,"utf8"));for(let h of["displayName: string","displayNameFrom(work.name) ?? workCode"])v.includes(h)||e.push(a("error","tool.work-name",`\u9ED8\u8BA4 WorkSource \u5FC5\u987B\u4ECE work.name \u5B89\u5168\u6D3E\u751F VR \u5C55\u793A\u540D\u79F0\uFF1A${h}`))}let y=l.join(r,"templates","viewer","src","ui","ViewerOverlay.tsx");if(await u(y)){let v=k(await E(y,"utf8")),h=Te.filter(S=>!z(v,S)||!U(v,S));h.length>0&&e.push(a("error","tool.vapor-provider",`\u9ED8\u8BA4 ViewerOverlay \u5FC5\u987B\u4ECE @realsee/vapor \u5BFC\u5165\u5E76\u5B9E\u9645\u6E32\u67D3\u5B98\u65B9 Demo \u7EC4\u4EF6\uFF1A${h.join(", ")}`));for(let S of je)v.includes(S)||e.push(a("error","tool.viewer-topbar",`\u9ED8\u8BA4 ViewerOverlay \u5FC5\u987B\u4F7F\u7528 Vapor TitleBar\u3001Layer \u4E0E\u54CD\u5E94\u5F0F Stack\uFF0C\u5E76\u6E32\u67D3\u5DE6\u4E0A\u6807\u9898\u3001\u5C45\u4E2D controls \u4E0E\u72B6\u6001\u8BED\u4E49\uFF1A${S}`));for(let S of Ne)v.includes(S)&&e.push(a("error","tool.viewer-native-vapor",`\u9ED8\u8BA4 ViewerOverlay \u4E0D\u5F97\u7528\u539F\u751F\u6807\u9898\u6216\u81EA\u5B9A\u4E49 glass \u6837\u5F0F\u66FF\u4EE3 Vapor \u7EC4\u4EF6\u89C6\u89C9\uFF1A${S}`))}let b=l.join(r,"templates","base","src","styles.css");if(await u(b)){let v=await E(b,"utf8");for(let h of Le)v.includes(h)||e.push(a("error","tool.viewer-layout",`\u9ED8\u8BA4 Viewer \u5FC5\u987B\u4FDD\u6301 safe-area \u53CA\u684C\u9762/\u5E73\u677F/\u624B\u673A/\u77EE\u5C4F\u5916\u5C42\u54CD\u5E94\u5F0F\u5E03\u5C40\uFF1A${h}`));for(let h of Me)v.includes(h)&&e.push(a("error","tool.viewer-native-vapor",`\u9ED8\u8BA4 Viewer \u6837\u5F0F\u4E0D\u5F97\u8986\u76D6 Vapor \u5185\u90E8\u7C7B\u6216\u91CD\u7ED8\u7EC4\u4EF6\u89C6\u89C9\uFF1A${h}`))}let c=l.join(r,"templates","viewer","src","features","navigation","ModeSwitcher.tsx");if(await u(c)){let v=k(await E(c,"utf8")),h=Re.filter(S=>!z(v,S)||!U(v,S));h.length>0&&e.push(a("error","tool.vapor-component",`\u9ED8\u8BA4 ModeSwitcher \u5FC5\u987B\u4ECE @realsee/vapor \u5BFC\u5165\u5E76\u5B9E\u9645\u6E32\u67D3\u5B98\u65B9 Demo \u7684\u6A21\u5F0F\u5207\u6362\u7EC4\u4EF6\uFF1A${h.join(", ")}`));for(let S of Pe)v.includes(S)||e.push(a("error","tool.viewer-control-size",`\u9ED8\u8BA4 ModeSwitcher \u5FC5\u987B\u4F7F\u7528\u5B98\u65B9 Demo \u7684 Font/HStack/VStack \u4E0E shadow Button \u7EC4\u5408\uFF1A${S}`));for(let S of xe)v.includes(S)&&e.push(a("error","tool.viewer-native-vapor",`\u9ED8\u8BA4 ModeSwitcher \u4E0D\u5F97\u9000\u56DE Segmented \u6216\u6DFB\u52A0\u65E7\u7684\u62C9\u4F38/glass \u6837\u5F0F\uFF1A${S}`))}let d=l.join(r,"templates","viewer","src");if(await u(d))for(let v of await x(d)){let h=k(await E(v,"utf8")),S=ee(h);S&&e.push(a("error","env.client-server-only-reference",`${l.relative(r,v)} \u4E0D\u5F97\u8BFB\u53D6\u6216\u5F15\u7528 server-only \u914D\u7F6E\u6216\u51ED\u636E\uFF1A${S}\u3002`))}let g=l.join(r,"templates","viewer","src","app","App.tsx");if(await u(g)){let v=k(await E(g,"utf8"));for(let h of["setWorkName(payload.displayName)","title={workName}"])v.includes(h)||e.push(a("error","tool.work-name",`\u9ED8\u8BA4 App \u5FC5\u987B\u628A WorkSource \u5C55\u793A\u540D\u79F0\u4F20\u7ED9 ViewerOverlay\uFF1A${h}`));for(let h of["FeatureOwnedMeshPlugin","registerFeatureOwnedMeshPlugin","vr/plugins/examples"])v.includes(h)&&e.push(a("error","tool.custom-plugin-enabled",`\u9ED8\u8BA4 Viewer \u4E0D\u5F97\u542F\u7528\u81EA\u5B9A\u4E49\u63D2\u4EF6\u793A\u4F8B\uFF1A${h}`))}return e.push(...await ze(l.join(r,"templates","base"),".gitignore.template")),e}async function Lr(r,t,e){let s=new Set;for(let n of e)await u(l.join(r,n))&&s.add(n);for(let n of t){let i=l.join(r,n);if(!await u(i))continue;if(!(await cr(i)).isDirectory()){s.add(n);continue}for(let m of await K(i))s.add(l.posix.join(n,m))}return s}async function Nr(r){let t=[],e=l.join(r,".vr-cli.json");if(!await u(e)){let o=l.join(r,".agents"),m=await u(l.join(r,"AGENTS.md")),f=await u(o);if(!m&&!f)t.push(a("warning","agents.missing","\u672A\u5B89\u88C5\u8DE8 Agent \u6307\u4EE4\u8D44\u4EA7\u3002"));else for(let p of["skills/realsee-vr-app/SKILL.md","skills/realsee-image-resources/SKILL.md","skills/realsee-image-resources/references/cors-and-three-textures.md","skills/realsee-image-resources/references/data-wanxiang.md","skills/realsee-image-resources/references/five-image-url-transform.md","skills/realsee-image-resources/references/source-and-domain-policy.md","skills/threejs-r117-five/SKILL.md","skills/threejs-r117-five/references/r117-capabilities.md","skills/threejs-r117-five/references/r117-version-guards.md","skills/threejs-r117-five/references/five-ownership.md"])await u(l.join(o,p))||t.push(a("error","agents.skill-missing",`.agents/${p} \u7F3A\u5931\u3002`));return t}let s;try{s=await re(e)}catch(o){return[a("error","manifest.invalid",".vr-cli.json \u65E0\u6CD5\u89E3\u6790\u3002",String(o))]}if(!(s&&typeof s=="object"&&!Array.isArray(s)&&s.schemaVersion===1&&O.includes(s.agentAssets)))return[a("error","manifest.invalid",".vr-cli.json \u7F3A\u5C11\u6709\u6548\u7684 schemaVersion \u6216 agentAssets\u3002")];for(let o of W(s.agentAssets))await u(l.join(r,o))||t.push(a("error","agents.asset-missing",`agentAssets=${s.agentAssets} \u7F3A\u5C11\uFF1A${o}`));let i=s.agentAssets==="portable"?{directoryRoots:Ee,singleFiles:Se}:s.agentAssets==="none"?{directoryRoots:he,singleFiles:we}:null;if(i)for(let o of await Lr(r,i.directoryRoots,i.singleFiles))t.push(a("error","agents.unexpected",`agentAssets=${s.agentAssets} \u4E0D\u5E94\u5305\u542B\uFF1A${o}`));return t}async function xr(r,t){let e=[];t.private!==!0&&e.push(a("error","project.public","\u751F\u6210\u9879\u76EE\u5FC5\u987B\u4FDD\u6301 private\u3002")),t.type!=="module"&&e.push(a("error","project.module","\u751F\u6210\u9879\u76EE\u5FC5\u987B\u4F7F\u7528 ESM\uFF1Atype=module\u3002")),e.push(...await $e(r,t));for(let c of ce)await u(l.join(r,c))||e.push(a("error","project.missing",`\u7F3A\u5C11\u751F\u6210\u6587\u4EF6\uFF1A${c}`));for(let[c,d,g]of[["dependency",t.dependencies,G],["dev-dependency",t.devDependencies,q]])for(let[v,h]of Object.entries(g)){let S=d?.[v];if(S===void 0){e.push(a("error",`${c}.missing`,`${v} \u5FC5\u987B\u58F0\u660E\uFF0C\u751F\u6210\u5668\u57FA\u7EBF\u4E3A ${h}`));continue}if(v in V){S!==h&&e.push(a("error",`${c}.locked`,`${v} \u5FC5\u987B\u9501\u5B9A\u4E3A ${h}\uFF0C\u5F53\u524D\u4E3A ${S}\uFF1BFive 6.8.9 \u4E0E Three \u80FD\u529B\u53C2\u8003\u5747\u9488\u5BF9\u8BE5\u7248\u672C\uFF0C\u5347\u7EA7\u9700\u6574\u4F53\u8BC4\u4F30\u5E76\u540C\u6B65\u751F\u6210\u5668\u3002`));continue}let $=H(S);if(!$){e.push(a("error",`${c}.range`,`${v} \u5FC5\u987B\u4F7F\u7528\u5F00\u653E\u6700\u4F4E\u7248\u672C\u8303\u56F4 >=x.y.z\uFF0C\u751F\u6210\u5668\u57FA\u7EBF\u4E3A >=${h}\uFF0C\u5F53\u524D\u4E3A ${S}\u3002`));continue}if(!P($,h)){e.push(a("error",`${c}.minimum`,`${v} \u7684\u58F0\u660E\u4E0B\u9650 ${$} \u4F4E\u4E8E\u751F\u6210\u5668\u57FA\u7EBF ${h}\u3002`));continue}S!==oe(v,h)&&e.push(a("warning",`${c}.updated`,`${v} \u7684\u6700\u4F4E\u7248\u672C\u5DF2\u4ECE\u751F\u6210\u5668\u57FA\u7EBF ${h} \u63D0\u9AD8\u5230 ${$}\uFF1B\u8BF7\u786E\u8BA4 npm run check \u4E0E npm run build \u901A\u8FC7\uFF0C\u5E76\u5728\u5347\u7EA7 SDK \u65F6\u590D\u6838\u7248\u672C\u5316 fixture\u3002`))}for(let[c,d]of[["dev",["check:env","concurrently"]],["setup",["scripts/setup.mjs"]],["test:setup",["--test","scripts/setup-config.test.mjs"]],["dev:bff",["--env-file-if-exists=.env.development","server/index.ts"]],["dev:web",["vite"]],["check:env",["--env-file-if-exists=.env.development","server/check-config.ts"]],["typecheck",["npm run typecheck:web","npm run typecheck:bff"]],["typecheck:web",["tsc -p tsconfig.json"]],["typecheck:bff",["tsc -p tsconfig.server.json"]],["lint",["eslint . --max-warnings=0"]],["format",["prettier --write ."]],["format:check",["prettier --check ."]],["check",["npm run typecheck","npm run lint","npm run format:check"]],["check:bff",["typecheck:bff"]],["deps:relock",["npm install --package-lock-only"]],["deps:install",["npm ci"]],["start",["--env-file-if-exists=.env.development","server/index.ts","--serve-frontend"]]])for(let g of d)t.scripts?.[c]?.includes(g)||e.push(a("error","script.contract",`package.json scripts.${c} \u5FC5\u987B\u5305\u542B\uFF1A${g}`));e.push(...await Tr(r,t));for(let[c,d]of[["eslint.config.mjs",["from '@eslint/js'","from 'typescript-eslint'","from 'eslint-plugin-react-hooks'","recommendedTypeChecked","tsconfig.server.json","globals.browser","globals.node","'react-hooks/rules-of-hooks'","'react-hooks/exhaustive-deps'","no-floating-promises","disableTypeChecked"]],["prettier.config.mjs",["semi: false","singleQuote: true","trailingComma: 'all'"]],["tsconfig.json",['"strict": true','"noUncheckedIndexedAccess": true','"noEmit": true','"include": ["src", "shared", "vite.config.ts"]']],["tsconfig.server.json",['"strict": true','"erasableSyntaxOnly": true','"verbatimModuleSyntax": true','"exactOptionalPropertyTypes": true','"noUncheckedIndexedAccess": true','"types": ["node"]','"include": ["server", "shared"]']]]){let g=l.join(r,c);if(!await u(g))continue;let v=await E(g,"utf8");for(let h of d)v.includes(h)||e.push(a("error","tooling.config",`${c} \u7F3A\u5C11\u914D\u7F6E\u6807\u8BB0\uFF1A${h}`))}let s=l.join(r,"src","main.tsx");if(await u(s)){let c=await E(s,"utf8"),d=k(c);(Ae(d)!==1||d.includes("@realsee/vapor/vapor.scss"))&&e.push(a("error","vapor.css","src/main.tsx \u5FC5\u987B\u6070\u597D\u4E00\u6B21\u4EE5\u53EF\u6267\u884C side-effect import \u5BFC\u5165 @realsee/vapor/vapor.css\uFF0C\u4E14\u4E0D\u5F97\u4F7F\u7528 vapor.scss\u3002")),d.includes("./app/App")||e.push(a("error","architecture.app","src/main.tsx \u5FC5\u987B\u4ECE app \u5C42\u7EC4\u5408\u7A7A\u95F4\u8FD0\u884C\u65F6\u4E0E DOM UI\u3002"))}let n=l.join(r,"src","vr","five","api.ts");if(await u(n)){let c=await E(n,"utf8"),d=k(c);for(let g of["@realsee/five","@realsee/five/plugins","@realsee/five/line","@realsee/five/sticker","@realsee/five/vfx","@realsee/five/gltf-loader","@realsee/five/shader-lib","@realsee/five/work-downloader"])d.includes(`'${g}'`)||e.push(a("error","architecture.five-api",`Five \u80FD\u529B\u5165\u53E3\u7F3A\u5931\uFF1A${g}`))}let i=l.join(r,"src","vr","plugins","dnalogel.ts");await u(i)&&(k(await E(i,"utf8")).includes("from '@realsee/dnalogel'")||e.push(a("error","architecture.dnalogel-api","dnalogel \u9884\u8BBE\u63D2\u4EF6\u6839\u80FD\u529B\u5165\u53E3\u7F3A\u5931\u3002"))),e.push(...await Fe(r,"src/vr/plugins/examples/FeatureOwnedMeshPlugin.ts"));let o=l.join(r,"src");if(await u(o))for(let c of await x(o)){let d=k(await E(c,"utf8"));d.includes("@realsee/five/react")&&e.push(a("error","architecture.five-react",`${l.relative(r,c)} \u7981\u6B62\u4F7F\u7528\u66F4\u65B0\u6EDE\u540E\u7684 @realsee/five/react\uFF1B\u8BF7\u76F4\u63A5\u4F7F\u7528\u539F\u751F Five API\u3002`));for(let g of dr)g.pattern.test(d)&&e.push(a("error","compatibility.three-r117",`${l.relative(r,c)}\uFF1A${g.message}`))}let m=l.join(r,"src","vr","five","SpatialRuntimeProvider.tsx");if(await u(m)){let c=await E(m,"utf8"),d=k(c);(d.includes("unsafe__useFiveInstance")||d.includes("provider?.five"))&&e.push(a("error","architecture.five-instance","\u7A7A\u95F4\u8FD0\u884C\u65F6\u7981\u6B62\u901A\u8FC7 Five React wrapper \u83B7\u53D6\u5B9E\u4F8B\u3002")),/import\s*\{[^}]*\bFive\b[^}]*\}\s*from\s*['"]@realsee\/five['"]/.test(d)||e.push(a("error","architecture.five-native","SpatialRuntimeProvider \u5FC5\u987B\u4ECE @realsee/five \u5BFC\u5165\u539F\u751F Five\u3002"));for(let h of["new Five(",".appendTo(",".refresh(",".getElement()",".dispose()","new ResizeObserver","new SpatialRuntimeLifetime","runExclusive<T>(","ownedLifetime?.disposeFeatures()","ownedLifetime.afterPending(finalize)","width <= 0 || height <= 0",'className="five-surface"'])d.includes(h)||e.push(a("error","architecture.five-native",`\u539F\u751F Five \u751F\u547D\u5468\u671F\u7F3A\u5C11\uFF1A${h}`));let g=d.indexOf("ownedLifetime?.disposeFeatures()"),v=d.indexOf("ownedLifetime.afterPending(finalize)");(g<0||v<g)&&e.push(a("error","architecture.five-cleanup","\u5FC5\u987B\u5148\u540C\u6B65\u505C\u6B62 feature\uFF0C\u518D\u7B49\u5F85\u539F\u751F\u5F02\u6B65\u4EFB\u52A1\u5B8C\u6210\u540E\u6E05\u7406\u63D2\u4EF6\u548C Five\u3002"))}let f=l.join(r,"src","app","App.tsx");if(await u(f)){let c=await E(f,"utf8"),d=k(c);for(let g of["lifetime.own(","lifetime.runExclusive(","await five.load(","await waitForActiveModel(","const panoTagList = payload.presetData?.panoTagList","if (panoTagList?.length)","await currentTags.load(panoTagList)","setWorkName(payload.displayName)","title={workName}"])d.includes(g)||e.push(a("error","architecture.feature-lifetime",`App \u7684\u539F\u751F\u5F02\u6B65\u751F\u547D\u5468\u671F\u6216\u663E\u5F0F PanoTag preset \u6761\u4EF6\u7F3A\u5C11\uFF1A${g}`));d.includes("payload.tags")&&e.push(a("error","architecture.pano-tag-contract","App \u4E0D\u5F97\u628A generic payload.tags \u76F4\u63A5\u52A0\u8F7D\u4E3A PanoTag\u3002"))}let p=l.join(r,"src","ui"),w=l.join(p,"ViewerOverlay.tsx");if(await u(w)){let c=k(await E(w,"utf8")),d=Te.filter(g=>!z(c,g)||!U(c,g));d.length>0&&e.push(a("error","architecture.vapor-provider",`ViewerOverlay \u5FC5\u987B\u4ECE @realsee/vapor \u5BFC\u5165\u5E76\u5B9E\u9645\u6E32\u67D3\u5B98\u65B9 Demo \u7EC4\u4EF6\uFF1A${d.join(", ")}`));for(let g of je)c.includes(g)||e.push(a("error","architecture.viewer-topbar",`ViewerOverlay \u5FC5\u987B\u4F7F\u7528 Vapor TitleBar\u3001Layer \u4E0E\u54CD\u5E94\u5F0F Stack\uFF0C\u5E76\u6E32\u67D3\u5DE6\u4E0A\u6807\u9898\u3001\u5C45\u4E2D controls \u4E0E\u72B6\u6001\u8BED\u4E49\uFF1A${g}`));for(let g of Ne)c.includes(g)&&e.push(a("error","architecture.viewer-native-vapor",`ViewerOverlay \u4E0D\u5F97\u7528\u539F\u751F\u6807\u9898\u6216\u81EA\u5B9A\u4E49 glass \u6837\u5F0F\u66FF\u4EE3 Vapor \u7EC4\u4EF6\u89C6\u89C9\uFF1A${g}`))}let y=l.join(r,"src","features","navigation","ModeSwitcher.tsx");if(await u(y)){let c=k(await E(y,"utf8")),d=Re.filter(g=>!z(c,g)||!U(c,g));d.length>0&&e.push(a("error","architecture.vapor-component",`ModeSwitcher \u5FC5\u987B\u4ECE @realsee/vapor \u5BFC\u5165\u5E76\u5B9E\u9645\u6E32\u67D3\u5B98\u65B9 Demo \u7684\u6A21\u5F0F\u5207\u6362\u7EC4\u4EF6\uFF1A${d.join(", ")}`));for(let g of Pe)c.includes(g)||e.push(a("error","architecture.viewer-control-size",`ModeSwitcher \u5FC5\u987B\u4F7F\u7528\u5B98\u65B9 Demo \u7684 Font/HStack/VStack \u4E0E shadow Button \u7EC4\u5408\uFF1A${g}`));for(let g of xe)c.includes(g)&&e.push(a("error","architecture.viewer-native-vapor",`ModeSwitcher \u4E0D\u5F97\u9000\u56DE Segmented \u6216\u6DFB\u52A0\u65E7\u7684\u62C9\u4F38/glass \u6837\u5F0F\uFF1A${g}`))}if(await u(p))for(let c of await x(p)){let d=k(await E(c,"utf8")),g=/(?:from|import)\s*(?:\(\s*)?['"][^'"]*\/vr\//.test(d);(d.includes("@realsee/five")||d.includes("@realsee/dnalogel")||g)&&e.push(a("error","architecture.ui-boundary",`${l.relative(r,c)} \u4E0D\u5F97\u5BFC\u5165 src/vr\u3001Five \u6216 dnalogel \u7A7A\u95F4\u8FD0\u884C\u65F6\u3002`))}let b=l.join(r,"src","styles.css");if(await u(b)){let c=await E(b,"utf8");(!c.includes(".five-surface")||!c.includes(".dom-overlay")||!c.includes("pointer-events: none"))&&e.push(a("error","architecture.dom-overlay","\u539F\u751F Five surface \u4E0E DOM overlay \u5FC5\u987B\u4FDD\u6301\u72EC\u7ACB\u5206\u5C42\u53CA pointer-events \u9694\u79BB\u3002"));for(let d of Le)c.includes(d)||e.push(a("error","architecture.viewer-layout",`Viewer \u5FC5\u987B\u4FDD\u6301 safe-area \u53CA\u684C\u9762/\u5E73\u677F/\u624B\u673A/\u77EE\u5C4F\u5916\u5C42\u54CD\u5E94\u5F0F\u5E03\u5C40\uFF1A${d}`));for(let d of Me)c.includes(d)&&e.push(a("error","architecture.viewer-native-vapor",`Viewer \u6837\u5F0F\u4E0D\u5F97\u8986\u76D6 Vapor \u5185\u90E8\u7C7B\u6216\u91CD\u7ED8\u7EC4\u4EF6\u89C6\u89C9\uFF1A${d}`))}return e.push(...await ze(r,".gitignore")),e.push(...await Ge(r,[".agents/skills/realsee-vr-app/SKILL.md","AGENTS.md"],".agents/skills/realsee-vr-app/references/dnalogel-api-capabilities.md")),e.push(...await Ve(r,[".agents/skills/realsee-vr-app/SKILL.md","AGENTS.md"],".agents/skills/realsee-vr-app")),e.push(...await Ke(r,["AGENTS.md",".agents/skills/realsee-vr-app/SKILL.md"],".agents/skills/realsee-vr-app")),e.push(...await We(r,{validator:"shared/model-floorplan.ts",types:"server/types.ts",client:"server/realsee-open-api.ts",service:"server/work-service.ts",workSource:"src/vr/work/WorkSource.ts",feature:"src/features/floorplan/ModelFloorplanFeature.ts",app:"src/app/App.tsx",modeSwitcher:"src/features/navigation/ModeSwitcher.tsx",browserTsconfig:"tsconfig.json",serverTsconfig:"tsconfig.server.json"})),e.push(...await Ie(r,["AGENTS.md",".agents/skills/realsee-vr-app/SKILL.md",".agents/skills/realsee-image-resources/SKILL.md"])),e.push(...await Ce(r,["AGENTS.md",".agents/skills/realsee-vr-app/SKILL.md",".agents/skills/realsee-vr-app/references/vapor-dom-layer.md"])),e.push(...await De(r,".agents/skills/realsee-vr-app/references/vapor-dom-layer.md",".agents/skills/realsee-vr-app/references/ui-and-validation.md")),e.push(...await Nr(r)),e}async function Ue({directory:r=".",json:t=!1}){let e=l.resolve(r),s=l.join(e,"package.json"),n=[];P(process.versions.node,C)||n.push(a("error","node.version",`\u9700\u8981 Node >=${C}\uFF0C\u5F53\u524D ${process.versions.node}`));try{let o=await Or();P(o,D)||n.push(a("error","npm.version",`\u9700\u8981 npm >=${D}\uFF0C\u5F53\u524D ${o}`))}catch(o){n.push(a("error","npm.unavailable","\u65E0\u6CD5\u6267\u884C npm --version\u3002",String(o)))}if(!await u(s))n.push(a("error","package.missing",`\u627E\u4E0D\u5230 package.json\uFF1A${s}`));else try{let o=await re(s);n.push(...await jr(e,o)?await Pr(e,o):await xr(e,o))}catch(o){n.push(a("error","package.invalid","package.json \u65E0\u6CD5\u89E3\u6790\u3002",String(o)))}let i={ok:!n.some(o=>o.level==="error"),root:e,errors:n.filter(o=>o.level==="error").length,warnings:n.filter(o=>o.level==="warning").length,diagnostics:n};if(t)console.log(JSON.stringify(i,null,2));else{for(let o of n)console.log(`${o.level==="error"?"ERROR":"WARN "} [${o.code}] ${o.message}`);console.log(i.ok?`OK ${e}`:`FAILED ${e}`)}return i}var M=Object.freeze({version:1,capabilityOrder:["@realsee/dnalogel","@realsee/five","three"],intentRoutes:Object.freeze([Object.freeze({intent:"tag|hotspot|anchor",defaultCapability:"PanoTagPlugin",source:"GET /open/v3/hot/list"}),Object.freeze({intent:"tour",defaultCapability:"Five State snapshot",source:"GET /open/v3/observerpicture/search"}),Object.freeze({intent:"model-annotation",defaultCapability:"ModelMakerPlugin",source:"GET /open/v3/annotation/search"}),Object.freeze({intent:"floorplan|house-layout",defaultCapability:"ModelFloorplanPlugin",source:"GET /open/v3/floorplan/plugins -> data.format_floorplan_url"}),Object.freeze({intent:"route",defaultCapability:"GuideLinePlugin + CruisePlugin|MovePlugin",source:"GET /open/v3/route/search"})]),invariants:Object.freeze(["OpenAPI data is fetched by the BFF, validated, then adapted to installed SDK types.","SDK-shaped local data requires an explicit Open Platform opt-out.","A fallback records the missing preset capability before using Five or Three.","Only a completely validated { version, data: FloorplanServerData } wrapper from data.format_floorplan_url may expose the modelFloorplan capability.","Missing, empty, malformed or failed floorplan data is feature-local and leaves the base Viewer usable.","Each feature owns cancellation, registrations, hooks, media and Three resources.","Persistent Work or customer-data mutations require an authoritative write contract and confirmation."])});var Ye=`vr-cli ${j}
1
+ import{parseArgs as Gr}from"node:util";var H="@realsee/vr-cli";var D="https://registry.npmjs.org/",$="public";var j="0.2.0",O=["portable","all","none"],Z="22.23.2",V="22.18.0",oe=`>=${V}`,K="11.19.0",ie=">=11.19.0",ne=`npm@${K}`,Q="node-vite-bff-v1",P="adm-zip",L="0.6.0",le=Object.freeze(["REALSEE_GATEWAY_APP_KEY","REALSEE_GATEWAY_APP_SECRET"]),z=Object.freeze({"@fastify/static":"10.1.3","@realsee/dnalogel":"3.81.2","@realsee/five":"6.8.9","@realsee/vapor":"0.2.12",fastify:"5.12.1",react:"18.3.1","react-dom":"18.3.1",three:"0.117.1"}),U=Object.freeze({three:z.three}),ce=(r,t)=>r in U?t:`>=${t}`,X=r=>{if(typeof r!="string")return null;let t=/^>=(\d+\.\d+\.\d+(?:[-+][\w.]+)?)$/.exec(r.trim());return t?t[1]:null},N=(r,t)=>{let e=n=>{if(typeof n!="string")return null;let i=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(n.trim());return i?{core:i.slice(1,4).map(Number),prerelease:i[4]??null}:null},a=e(r),o=e(t);if(!a||!o)return!1;for(let n=0;n<3;n+=1){let i=a.core[n]-o.core[n];if(i>0)return!0;if(i<0)return!1}return a.prerelease===o.prerelease||a.prerelease===null?!0:o.prerelease===null?!1:a.prerelease.localeCompare(o.prerelease,"en",{numeric:!0})>=0},J=Object.freeze({"@eslint/js":"10.0.1","@types/node":"24.13.3","@types/react":"18.3.12","@types/react-dom":"18.3.1","@vitejs/plugin-react":"4.3.1",concurrently:"10.0.5",eslint:"10.9.1","eslint-plugin-react-hooks":"7.1.1",globals:"17.11.0",prettier:"3.9.6",typescript:"5.9.3","typescript-eslint":"8.68.0",vite:"5.4.14"}),pe=Object.freeze(["APP_ID","REALSEE_GATEWAY_ENDPOINT","REALSEE_GATEWAY_APP_KEY","REALSEE_GATEWAY_APP_SECRET","REALSEE_SOURCE"]),ue=Object.freeze(["# REALSEE_DEFAULT_RESOURCE_CODE=example-resource-code"]),fe=Object.freeze(["REALSEE_GATEWAY_APP_KEY","REALSEE_GATEWAY_APP_SECRET"]),de=Object.freeze(["REALSEE_GATEWAY_APP_SECRET"]),me=Object.freeze(["package.json","package-lock.json","deploy.json","README.md","index.html","tsconfig.json","tsconfig.server.json","vite.config.ts",".editorconfig",".nvmrc",".node-version",".npmrc",".prettierignore","eslint.config.mjs","prettier.config.mjs","scripts/setup-config.mjs","scripts/setup-config.test.mjs","scripts/setup.mjs","scripts/pairing.mjs","scripts/pairing.test.mjs","scripts/project-identity.mjs","scripts/project-identity.test.mjs","scripts/package-vibe-release.mjs","server/types.ts","server/config.ts","server/check-config.ts","server/realsee-open-api.ts","server/work-service.ts","server/work-route.ts","server/app.ts","server/index.ts","shared/model-floorplan.ts","src/main.tsx","src/app/App.tsx","src/shared/useElementSize.ts","src/vr/five/api.ts","src/vr/five/fiveConfig.ts","src/vr/five/SpatialRuntimeProvider.tsx","src/vr/five/initialState.ts","src/vr/plugins/dnalogel.ts","src/vr/plugins/SpatialPluginRegistry.ts","src/vr/plugins/examples/FeatureOwnedMeshPlugin.ts","src/vr/work/WorkSource.ts","src/features/floorplan/ModelFloorplanFeature.ts","src/features/navigation/ModeSwitcher.tsx","src/features/tags/PanoTagFeature.ts","src/ui/ViewerOverlay.tsx","src/styles.css",".env.example",".gitignore",".vr-cli.json"]);import{spawn as or}from"node:child_process";import{rename as Re,writeFile as ir}from"node:fs/promises";import A from"node:path";import{fileURLToPath as nr}from"node:url";import _ from"node:path";import{mkdir as er,readFile as rr,writeFile as we}from"node:fs/promises";import{mkdir as he,readFile as Xe,readdir as ee,stat as ge,writeFile as ve}from"node:fs/promises";import x from"node:path";var Je=new Set(["",".css",".html",".json",".md",".mjs",".ts",".tsx",".txt",".yml",".yaml"]);function re(r,t){return Object.entries(t).reduce((e,[a,o])=>e.replaceAll(`{{${a}}}`,String(o)).replaceAll(`__${a}__`,String(o)),r)}async function f(r){try{return await ge(r),!0}catch(t){if(t?.code==="ENOENT")return!1;throw t}}async function Ee(r){if(!await f(r)){await he(r,{recursive:!0});return}if(!(await ge(r)).isDirectory())throw new Error(`\u76EE\u6807\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${r}`);if((await ee(r)).length)throw new Error(`\u76EE\u6807\u76EE\u5F55\u5FC5\u987B\u4E3A\u7A7A\uFF1A${r}`)}async function R(r,t,e={}){if(!await f(r))throw new Error(`\u6A21\u677F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${r}`);await he(t,{recursive:!0});for(let a of await ee(r,{withFileTypes:!0})){let o=x.join(r,a.name),n=x.join(t,a.name);if(a.isDirectory()){await R(o,n,e);continue}if(!a.isFile())continue;let i=x.extname(a.name),v=await Xe(o);if(!Je.has(i)){await ve(n,v);continue}await ve(n,re(v.toString("utf8"),e))}}async function W(r,t=r){let e=[];for(let a of await ee(t,{withFileTypes:!0})){let o=x.join(t,a.name);a.isDirectory()?e.push(...await W(r,o)):a.isFile()&&e.push(x.relative(r,o).split(x.sep).join("/"))}return e.sort()}var Se=Object.freeze(["realsee-vr-app","realsee-image-resources","threejs-r117-five"]),tr=Object.freeze(["realsee-vr-app/SKILL.md","realsee-vr-app/references/dnalogel-api-capabilities.md","realsee-vr-app/references/dnalogel-openapi-recipes.md","realsee-vr-app/references/continuous-improvement.md","realsee-vr-app/references/five-api-capabilities.md","realsee-vr-app/references/five-runtime.md","realsee-vr-app/references/official-docs.md","realsee-vr-app/references/plugin-lifecycle.md","realsee-vr-app/references/ui-and-validation.md","realsee-vr-app/references/vapor-dom-layer.md","realsee-vr-app/scripts/verify-project.mjs","realsee-image-resources/SKILL.md","realsee-image-resources/references/cors-and-three-textures.md","realsee-image-resources/references/data-wanxiang.md","realsee-image-resources/references/five-image-url-transform.md","realsee-image-resources/references/source-and-domain-policy.md","threejs-r117-five/SKILL.md","threejs-r117-five/references/five-ownership.md","threejs-r117-five/references/r117-capabilities.md","threejs-r117-five/references/r117-version-guards.md"]),te=r=>tr.map(t=>_.posix.join(r,t)),ke=Object.freeze(["AGENTS.md",...te(".agents/skills")]),sr=Object.freeze([".agents/README.md",...te(".claude/skills"),...te(".kiro/skills"),"CLAUDE.md",".cursor/rules/realsee-vr.mdc",".github/copilot-instructions.md",".kiro/steering/realsee-vr.md"]),ye=Object.freeze([".agents",".claude/skills",".kiro/skills",".cursor/rules",".kiro/steering"]),be=Object.freeze(["AGENTS.md","CLAUDE.md",".github/copilot-instructions.md"]),_e=Object.freeze([".claude/skills",".kiro/skills",".cursor/rules",".kiro/steering"]),Ae=Object.freeze([".agents/README.md","CLAUDE.md",".github/copilot-instructions.md"]),ar=Object.freeze([...ke,...sr]);function Y(r){if(r==="none")return[];if(r==="portable")return[...ke];if(r==="all")return[...ar];throw new Error(`\u672A\u77E5 agent assets \u6A21\u5F0F\uFF1A${r}`)}async function Oe({toolRoot:r,targetRoot:t,mode:e,tokens:a}){if(e==="none")return[];for(let n of Se)await R(_.join(r,"skills",n),_.join(t,".agents","skills",n),a);let o=await rr(_.join(r,"agent-assets","AGENTS.md"),"utf8");if(await we(_.join(t,"AGENTS.md"),re(o,a)),e!=="all")return Y(e);for(let n of[".claude/skills",".kiro/skills"])for(let i of Se)await R(_.join(r,"skills",i),_.join(t,n,i),a);return await R(_.join(r,"agent-assets","adapters"),t,a),await er(_.join(t,".agents"),{recursive:!0}),await we(_.join(t,".agents","README.md"),"# Agent assets\n\n`skills/realsee-vr-app`, `skills/realsee-image-resources` and `skills/threejs-r117-five` are the three canonical portable Skills. Vendor-specific copies are generated; edit the canonical copies and regenerate adapters.\n"),Y(e)}var lr=!0,se=nr(new URL(lr?"..":"../..",import.meta.url)),cr=process.platform==="win32"?"npm.cmd":"npm";function pr(r,t){let e=t||A.basename(r),a=e.trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"");if(!a||!/^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/.test(a))throw new Error(`\u65E0\u6CD5\u751F\u6210\u5408\u6CD5 npm \u5305\u540D\uFF1A${e}`);return a}function ur(r){return new Promise((t,e)=>{let a=or(cr,["ci"],{cwd:r,stdio:"inherit",shell:!1});a.once("error",e),a.once("exit",(o,n)=>{o===0?t():e(new Error(`npm ci \u5931\u8D25\uFF08code=${o??"null"}, signal=${n??"none"}\uFF09`))})})}async function Te({directory:r,preset:t,name:e,agentAssets:a="portable",install:o=!0}){if(!r)throw new Error("\u7F3A\u5C11\u76EE\u6807\u76EE\u5F55\u3002\u7528\u6CD5\uFF1Avr-cli create <directory>");if(t&&t!=="viewer")throw new Error("--preset \u6682\u65F6\u53EA\u652F\u6301 viewer\u3002");if(!O.includes(a))throw new Error(`\u672A\u77E5 agent assets \u6A21\u5F0F\uFF1A${a}\u3002\u53EF\u9009\u503C\uFF1A${O.join(", ")}`);let n=A.resolve(r),i=pr(n,e),v={PACKAGE_NAME:i,TOOL_VERSION:j};await Ee(n),await R(A.join(se,"templates","base"),n,v),await Re(A.join(n,".gitignore.template"),A.join(n,".gitignore")),await Re(A.join(n,".npmrc.template"),A.join(n,".npmrc")),await R(A.join(se,"templates","viewer"),n,v);let u=await Oe({toolRoot:se,targetRoot:n,mode:a,tokens:v}),d={schemaVersion:1,toolVersion:j,preset:"viewer",packageName:i,agentAssets:a,generatedAt:new Date().toISOString()};return await ir(A.join(n,".vr-cli.json"),`${JSON.stringify(d,null,2)}
2
+ `),o&&await ur(n),{targetRoot:n,packageName:i,installedAgentAssets:u,installed:o}}import{execFile as fr}from"node:child_process";import{readFile as w,readdir as dr,stat as mr}from"node:fs/promises";import p from"node:path";import{promisify as vr}from"node:util";var hr=vr(fr),s=(r,t,e,a)=>({level:r,code:t,message:e,...a?{detail:a}:{}}),je=r=>/(?:^|_)(?:AK|SK|SECRET|TOKEN|PASSWORD|CREDENTIALS?)(?:_|$)/i.test(r),gr=Object.freeze([/\bAPP_ID\b/,/\bREALSEE_(?:GATEWAY_[A-Z0-9_]+|SOURCE|ACCESS_TOKEN)\b/,/\b(?:VITE_)?(?:AK|SK|ACCESS_TOKEN)\b/]),ae=r=>{for(let t of gr){let e=t.exec(r);if(e)return e[0]}return null},b=r=>r.replace(/\/\*[\s\S]*?\*\//g,"").replace(/(^|[^:])\/\/.*$/gm,"$1"),Er=Object.freeze([{pattern:/three\/addons\//,message:"r117 \u6269\u5C55\u6A21\u5757\u5FC5\u987B\u4ECE three/examples/jsm \u5BFC\u5165\u3002"},{pattern:/three\/tsl\b/,message:"Three r117 \u4E0D\u63D0\u4F9B\u73B0\u4EE3 TSL \u5165\u53E3\u3002"},{pattern:/\b(?:SRGBColorSpace|LinearSRGBColorSpace|NoColorSpace|CapsuleGeometry|WebGPURenderer)\b/,message:"\u6E90\u7801\u4F7F\u7528\u4E86 Three r117 \u4E0D\u5B58\u5728\u7684\u73B0\u4EE3\u7B26\u53F7\u3002"},{pattern:/\b(?:renderer|[\w$]*Renderer)\.outputColorSpace\b/,message:"Three r117 \u4F7F\u7528 renderer.outputEncoding\u3002"},{pattern:/\b(?:texture|[\w$]*(?:map|Map|texture|Texture))\.colorSpace\s*=/,message:"Three r117 \u7EB9\u7406\u4F7F\u7528 encoding\uFF0C\u800C\u4E0D\u662F colorSpace\u3002"}]),wr=Object.freeze(["from '@realsee/five'","from 'three'","FEATURE_OWNED_MESH_PLUGIN_NAME","example.featureOwnedMesh","new Group(","new BoxBufferGeometry(","new MeshBasicMaterial(","new Mesh(","five.scene.add(root)","root.parent?.remove(root)","five.needsRender = true","geometry.dispose()","material.dispose()","plugins.register(FEATURE_OWNED_MESH_PLUGIN_NAME, plugin)","plugin.dispose()"]),Sr=Object.freeze(["new Five(","new WebGLRenderer(","renderer.render(","requestAnimationFrame(","five.dispose(","three/addons/"]),kr=Object.freeze(["Image resources use approved Realsee CDN domains only; never use `ljcdn.com`, `lianjia.com` or their subdomains.","Prefer Five `defaultImageURLTransform` over hand-built \u6570\u636E\u4E07\u8C61 query strings when its installed contract satisfies the request.","URL transformation does not grant CORS access.","Ordinary images use the deployment's approved image-4 host.","Feature media from OpenAPI or Work data is displayed through a derived sized variant, never the original: pass ordinary images through the reviewed `defaultImageURLTransform` adapter with a bounded `size`, and do not hand a full-resolution URL to a tag, tour, card or DOM thumbnail."]),yr=Object.freeze(["When the user explicitly requests Vapor components, use matching named exports from the installed `@realsee/vapor` package for every applicable DOM control; importing `Vapor` or `vapor.css` alone does not satisfy the request.","Do not replace an available Vapor component with raw HTML or a locally styled replica; use raw DOM only for semantics or capabilities absent from the installed Vapor declarations, and record that gap.","Before implementing Vapor UI, read `node_modules/@realsee/vapor/AI_CONTEXT.md`, `llms.txt`, `vapor.d.ts`, and the selected component declarations; never invent component names or props.","Use the installed `ai_guides` only for component discovery and composition recipes; the target project's root `vapor.d.ts` and selected component declarations are the API contract, and conflicts resolve in favor of installed declarations.","Keep the Five canvas in its dedicated sibling host outside `Vapor`; Vapor layout components own DOM chrome only, even when an external recipe nests a placeholder VR component inside a stack."]),br=Object.freeze(["The current generated baseline is `@realsee/vapor@0.2.12`.","The 0.2.12 baseline requires exactly one executable `@realsee/vapor/vapor.css` import, not `vapor.scss`","Choose by product semantics before styling:","`HStack` arranges DOM horizontally, `VStack` vertically and `ZStack` overlays DOM children.","The 0.2.12 match union is exact:","if both represent the same physical inset, de-duplicate rather than adding it twice."]),_r=Object.freeze(["For an explicit Vapor request, executable source must import and render the matching non-provider Vapor component; the provider and stylesheet alone are insufficient.","exactly one executable `@realsee/vapor/vapor.css` import and no `vapor.scss` or copied internal stylesheet;","the `Vapor` provider rectangle matches the DOM overlay host","safe-area behavior uses the declared source/merge rule without double-counting","correct portal order, focus entry/return, Escape/backdrop behavior and close reasons;","no application stylesheet overrides `.vapor-*` internals","typecheck validates every component prop, compound child and symbol"]),Ar=/\bimport\s*['"]@realsee\/vapor\/vapor\.css['"]\s*;?/,Pe=r=>r.match(new RegExp(Ar.source,"g"))?.length??0,B=(r,t)=>new RegExp(`\\bimport\\s*\\{[^}]*\\b${t}\\b[^}]*\\}\\s*from\\s*['"]@realsee/vapor['"]`).test(r),q=(r,t)=>new RegExp(`<${t}(?:\\s|/?>)`).test(r),Or=Object.freeze(["Spatial capability resolution order: installed @realsee/dnalogel preset -> native Five -> Three.js.","Tags, hotspots, and anchors default to PanoTagPlugin with GET /open/v3/hot/list.","Normalize optional OpenAPI configuration fields such as `version`, `globalConfig` and `contentTypeConfig`: treat `null` as absent before validating the installed SDK contract.","Load ordinary PanoTag point/plane tags after `five.load` without waiting for `five.model.loaded`; gate only model-dependent tag content or anchors on model readiness.","Tours use Five State snapshots with GET /open/v3/observerpicture/search.","Model annotations default to ModelMakerPlugin with GET /open/v3/annotation/search.","Routes use GuideLinePlugin for rendering and shape-selected CruisePlugin or MovePlugin playback with GET /open/v3/route/search.","Tour adapters read `data.list`, map each validated record to `Partial<State>` with the active `workCode`, and never cast an observer-picture record directly to `State`.","Model annotation adapters read `data.list`, validate `object_data` for `box`, `triangles` or `prism`, and pass `{ list }` to ModelMaker only for a model-capable Work.","Feature API, schema, or plugin failures disable only the affected feature; keep the base Viewer usable and never runtime-fallback to raw data or a custom Three implementation.","Scope tour, ModelMaker, GuideLine, Cruise and Move work to an active `workCode`/generation token; abort fetches, ignore stale continuations, pause the owned playback controller, remove hooks, clear GuideLine data, wait for pending loads, then let identity-safe unregister dispose each registered controller exactly once before `five.dispose()`.","SDK-shaped custom data requires an explicit developer opt-out from Open Platform data.","VR spatial tag (spatial annotation) is the umbrella concept","Classify content, renderer, anchor, and lifecycle/data ownership","Never infer PanoTag from","Never directly edit `work.json`, downloaded Work, or customer Work"]),T=Object.freeze(["dnalogel 3.81.2 route playback is shape-selected: a validated pano group uses `CruisePlugin` `{ panoIndexList }`, while a validated model-space curve uses `MovePlugin` `{ path }`; `GuideLinePlugin` renders route geometry and is not playback input.","`CruisePlugin` accepts exactly one of `panoIndexList` or `keyframes`; its installed union has no type discriminator.","OpenAPI route records are never passed raw: rename validated style config only for GuideLine `lines`, and never pass GuideLine `PathItem` arrays or style objects to `CruisePlugin` or `MovePlugin`.","If a source record contains both `pano_group` and `path`, the route adapter must choose product semantics explicitly and pass only the selected geometry branch to GuideLine.","`GuideLinePlugin.load` waits for model readiness; `CruisePlugin` waits only for Work and observers; `MovePlugin` enters Model and awaits Five readiness when playback starts.","Reject empty routes, unknown pano indexes, unsupported curves, non-finite coordinates, timing or speed, and ambiguous multi-segment Move paths before plugin load.","Route cleanup pauses the owned playback controller before hook removal and disposal; `clear` belongs to GuideLine, is not a public Cruise operation, and GuideLine disposal does not own an external or embedded Cruise controller automatically.","Do not invent `RoutePlugin`, `PanoRove`, `stop()`, or a universal route controller; use only the exports and methods declared by the installed package."]),Le=Object.freeze(["Floorplans default to ModelFloorplanPlugin with GET /open/v3/floorplan/plugins.","`data.format_floorplan_url`, not `format_model_url`, resolves to the preferred `{ version, data: FloorplanServerData }` plugin input.","Expose `capabilities.modelFloorplan` only after validating the complete wrapper and nested floorplan shape; missing, empty, malformed or failed floorplan data must leave the base Viewer usable.","The default tabs are `\u6F2B\u6E38` for `Panorama` and `\u4E09\u7EF4\u6A21\u578B` for `Mapview`; do not expose `Topview` as `\u5730\u56FE`.","Add the `\u5E73\u9762\u56FE` tab only after the capability validates and `ModelFloorplanPlugin.load` succeeds.","Configure `ModelFloorplanPlugin` with `autoShowEnable: false`; the application tab owns explicit `show()`/`hide()`, uses `modelOpacity: 0` while the floorplan is visible, and restores the Five model surface on hide, failure, and disposal.","Resolve the Five canvas parent and call `ModelFloorplanPlugin.appendTo(wrapper)` before `load`; `five.addExtraElement` registers input handling but does not mount the plugin DOM.","For multi-floor Work, ModelFloorplanPlugin and Five pano/model floor synchronization are the floor source of truth; do not invent a React-only `floorIndex`.","Floorplan show/hide, hooks, pending load and identity-safe disposal must finish before `five.dispose()`."]),Rr=Object.freeze(Le.slice(0,3)),Tr="The complete model floorplan contract lives in",jr="The complete route playback contract lives in",Pr=Object.freeze([T[0],T[7],jr]),Ne=Object.freeze(["Vapor","Text","TitleBar","ZStack","VStack","Layer"]),xe=Object.freeze(["Button","Font","HStack","VStack"]),$e=Object.freeze(["title?: string | null",'className="viewer-vapor"','width="100%"','height="100%"','theme="arc_beauty"',"useSaveAreaInsets()","useDimension()","<Layer vignetting />","labelMaxWidth={titleMaxWidth}",'className="viewer-controls"',"align={{ horizontal: 'center' }}",'className="viewer-status"','appearance="solid"',"role={error ? 'alert' : 'status'}","aria-live={error ? 'assertive' : 'polite'}","env(safe-area-inset-${side}, 0px)"]),Me=Object.freeze(['role="group"',"useDimension()","width={controlWidth}","height={compact ? '3rem' : '5rem'}","align={{ horizontal: 'space-around' }}",'appearance="shadow"',"weight={selected ? 'bold' : 'regular'}","opacity={selected ? '100' : '80'}",'className="mode-switcher-indicator"',"onClick={() => void changeTab(option.value)}"]),Ce=Object.freeze([".dom-overlay",".viewer-controls",".mode-switcher","pointer-events: auto",".mode-switcher-indicator","bottom: -0.5rem","width: 3.375rem","height: 0.125rem","background-image: linear-gradient(",".viewer-status"]),Fe=Object.freeze(["viewer-glass",'className="viewer-topbar"',"<h1"]),Ie=Object.freeze(["Segmented","viewer-segmented","viewer-glass",'appearance="horizontal"','maxWidth="100%"']),De=Object.freeze(["--viewer-glass-","--viewer-control-size",".vapor-segmented","backdrop-filter"]);async function M(r){return JSON.parse(await w(r,"utf8"))}async function Lr(){let r=process.platform==="win32"?"npm.cmd":"npm",{stdout:t}=await hr(r,["--version"],{encoding:"utf8"});return t.trim()}async function Ge(r,t){let e=[];for(let[o,n,i]of[["packageManager",t.packageManager,ne],["engines.node",t.engines?.node,oe],["engines.npm",t.engines?.npm,ie]])n!==i&&e.push(s("error","toolchain.version",`package.json ${o} \u5FC5\u987B\u4E3A ${i}\uFF0C\u5F53\u524D\u4E3A ${n??"\u672A\u58F0\u660E"}`));for(let o of[".nvmrc",".node-version"]){let n=p.join(r,o);if(await f(n)){let i=(await w(n,"utf8")).trim();i!==Z&&e.push(s("error","toolchain.node-pin",`${o} \u5FC5\u987B\u56FA\u5B9A Node ${Z}\uFF0C\u5F53\u524D\u4E3A ${i||"\u7A7A"}`))}}let a=p.join(r,".npmrc");if(await f(a)){let o=new Set((await w(a,"utf8")).split(/\r?\n/).map(n=>n.trim()).filter(Boolean));for(let n of["engine-strict=true","package-lock=true","save-prefix=>="])o.has(n)||e.push(s("error","toolchain.npmrc",`.npmrc \u5FC5\u987B\u5305\u542B ${n}`))}return e}async function Nr(r,t){let e=p.join(r,"package-lock.json");if(!await f(e))return[];try{let a=await M(e),o=a.packages?.[""],n=[];if(a.lockfileVersion!==3||!o)return[s("error","lockfile.invalid","package-lock.json \u5FC5\u987B\u4F7F\u7528 lockfileVersion 3 \u5E76\u5305\u542B\u6839\u5305\u8BB0\u5F55\u3002")];(a.name!==t.name||o.name!==t.name||o.version!==t.version)&&n.push(s("error","lockfile.package","package-lock.json \u6839\u5305\u540D\u79F0\u6216\u7248\u672C\u4E0E package.json \u4E0D\u4E00\u81F4\u3002"));for(let[i,v,u,d]of[["dependency",o.dependencies,t.dependencies,z],["dev-dependency",o.devDependencies,t.devDependencies,J]])for(let[m,S]of Object.entries(u??{})){v?.[m]!==S&&n.push(s("error",`lockfile.${i}`,`package-lock.json \u4E0E package.json \u4E0D\u4E00\u81F4\uFF1A${m}@${S}\uFF1B\u8BF7\u8FD0\u884C npm run deps:relock\u3002`));let y=d[m];if(!y)continue;let l=a.packages?.[`node_modules/${m}`]?.version;if(!l){n.push(s("error","lockfile.resolved",`package-lock.json \u7F3A\u5C11\u76F4\u63A5\u4F9D\u8D56 ${m} \u7684\u89E3\u6790\u7248\u672C\u3002`));continue}if(m in U){l!==y&&n.push(s("error","lockfile.locked",`package-lock.json \u5FC5\u987B\u5C06 ${m} \u89E3\u6790\u4E3A ${y}\uFF0C\u5F53\u524D\u4E3A ${l}\u3002`));continue}let c=X(S);c&&(!N(l,y)||!N(l,c))&&n.push(s("error","lockfile.minimum",`${m} \u5728 package-lock.json \u4E2D\u89E3\u6790\u4E3A ${l}\uFF0C\u5FC5\u987B\u540C\u65F6\u6EE1\u8DB3\u751F\u6210\u5668\u57FA\u7EBF >=${y} \u4E0E\u58F0\u660E ${S}\u3002`))}return n}catch(a){return[s("error","lockfile.invalid","package-lock.json \u65E0\u6CD5\u89E3\u6790\u3002",String(a))]}}async function C(r){let t=[];for(let e of await dr(r,{withFileTypes:!0})){let a=p.join(r,e.name);e.isDirectory()?t.push(...await C(a)):e.isFile()&&/\.(?:[cm]?[jt]sx?)$/.test(e.name)&&t.push(a)}return t}async function Ve(r,t){let e=p.join(r,t);if(!await f(e))return[];let a=b(await w(e,"utf8")),o=[];for(let v of wr)a.includes(v)||o.push(s("error","architecture.custom-plugin-example",`${t} \u7F3A\u5C11\u81EA\u5B9A\u4E49\u63D2\u4EF6\u6240\u6709\u6743\u6807\u8BB0\uFF1A${v}`));for(let v of Sr)a.includes(v)&&o.push(s("error","architecture.custom-plugin-example",`${t} \u7981\u6B62\u62E5\u6709 Five \u8FD0\u884C\u65F6\u6216\u7B2C\u4E8C\u6E32\u67D3\u5FAA\u73AF\uFF1A${v}`));let n=t.startsWith("templates/")?"templates/viewer/src/features/tags/PanoTagFeature.ts":"src/features/tags/PanoTagFeature.ts",i=p.join(r,n);if(await f(i)){let v=b(await w(i,"utf8"));for(let u of["from '@realsee/dnalogel/libs/PanoTagPlugin'","const PLUGIN_NAME = 'dnalogel.panoTag'","const candidate = PanoTagPlugin(","plugins.register(PLUGIN_NAME, candidate)","candidate.dispose()","async load(panoTagList: Tags['tagList'])","plugin.load({ tagList: panoTagList })","const disposeError = plugins.unregister(PLUGIN_NAME, plugin)"])v.includes(u)||o.push(s("error","architecture.pano-tag-contract",`${n} \u7F3A\u5C11 PanoTag \u517C\u5BB9\u6027\u3001\u663E\u5F0F\u7C7B\u578B\u6216\u6240\u6709\u6743\u6807\u8BB0\uFF1A${u}`));for(let u of["model_file","as Tags['tagList']","from '../../vr/work/WorkSource'"])v.includes(u)&&o.push(s("error","architecture.pano-tag-contract",`${n} \u542B\u975E\u6807\u51C6 content type \u6216\u672A\u9A8C\u8BC1\u7684 PanoTag \u8F6C\u6362\uFF1A${u}`))}return o}async function Ke(r,t){let e=[];for(let a of t){let o=p.join(r,a);if(!await f(o))continue;let n=await w(o,"utf8");for(let i of kr)n.includes(i)||e.push(s("error","agents.image-resource-guidance",`${a} \u7F3A\u5C11\u56FE\u7247\u8D44\u6E90\u57DF\u540D\u3001Five URL transform\u3001\u6570\u636E\u4E07\u8C61\u6216 CORS \u89C4\u5219\uFF1A${i}`))}return e}async function ze(r,t){let e=[];for(let a of t){let o=p.join(r,a);if(!await f(o))continue;let n=await w(o,"utf8");for(let i of yr)n.includes(i)||e.push(s("error","agents.vapor-guidance",`${a} \u7F3A\u5C11 Vapor \u771F\u5B9E\u7EC4\u4EF6\u9009\u62E9\u3001\u58F0\u660E\u6838\u5BF9\u6216 raw DOM \u4F8B\u5916\u89C4\u5219\uFF1A${i}`))}return e}async function Ue(r,t,e){let a=[];for(let[o,n,i,v]of[[t,br,"agents.vapor-reference","\u7248\u672C\u5316\u8D44\u6599\u8DEF\u7531\u3001\u7EC4\u4EF6\u9009\u578B\u3001\u5E03\u5C40\u6216 safe-area \u89C4\u5219"],[e,_r,"agents.vapor-acceptance","\u8FD0\u884C\u65F6 Vapor \u9A8C\u6536\u89C4\u5219"]]){let u=p.join(r,o);if(!await f(u))continue;let d=await w(u,"utf8");for(let m of n)d.includes(m)||a.push(s("error",i,`${o} \u7F3A\u5C11 ${v}\uFF1A${m}`))}return a}async function We(r,t,e){let a=[];for(let n of t){let i=p.join(r,n);if(!await f(i))continue;let v=await w(i,"utf8");for(let u of Or)v.includes(u)||a.push(s("error","agents.spatial-tag-guidance",`${n} \u7F3A\u5C11\u7A7A\u95F4\u6807\u7B7E\u51B3\u7B56\u6216\u53EA\u8BFB\u89C4\u5219\uFF1A${u}`))}let o=p.join(r,e);if(await f(o)){let n=await w(o,"utf8");for(let i of["Spatial capability resolution order: installed @realsee/dnalogel preset -> native Five -> Three.js.","Tags, hotspots, and anchors default to PanoTagPlugin with GET /open/v3/hot/list.","Normalize optional OpenAPI configuration fields such as `version`, `globalConfig` and `contentTypeConfig`: treat `null` as absent before validating the installed SDK contract.","Load ordinary PanoTag point/plane tags after `five.load` without waiting for `five.model.loaded`; gate only model-dependent tag content or anchors on model readiness.","Tours use Five State snapshots with GET /open/v3/observerpicture/search.","Model annotations default to ModelMakerPlugin with GET /open/v3/annotation/search.","Routes use GuideLinePlugin for rendering and shape-selected CruisePlugin or MovePlugin playback with GET /open/v3/route/search.","Tour adapters read `data.list`, map each validated record to `Partial<State>` with the active `workCode`, and never cast an observer-picture record directly to `State`.","Model annotation adapters read `data.list`, validate `object_data` for `box`, `triangles` or `prism`, and pass `{ list }` to ModelMaker only for a model-capable Work.","Feature API, schema, or plugin failures disable only the affected feature; keep the base Viewer usable and never runtime-fallback to raw data or a custom Three implementation.","Scope tour, ModelMaker, GuideLine, Cruise and Move work to an active `workCode`/generation token; abort fetches, ignore stale continuations, pause the owned playback controller, remove hooks, clear GuideLine data, wait for pending loads, then let identity-safe unregister dispose each registered controller exactly once before `five.dispose()`.","SDK-shaped custom data requires an explicit developer opt-out from Open Platform data.","`model_file` is not canonical","`ModelTVVideoPlugin`","`CSS3DRenderPlugin.create3DDomContainer`","`PanoVideoPlugin`","generic `tags`, `tagList` and `tag_list`","`ServerData` with `box`, `triangles` or `prism` items"])n.includes(i)||a.push(s("error","agents.spatial-tag-capabilities",`${e} \u7F3A\u5C11\u7A7A\u95F4\u80FD\u529B\u4F18\u5148\u7EA7\u3001\u6570\u636E\u6E90\u6216\u63D2\u4EF6\u7C7B\u578B\u8DEF\u7531\uFF1A${i}`))}return a}async function Ye(r,t,e){let a=[],o=[...t.map(n=>[n,n.endsWith("SKILL.md")?Pr:T]),[`${e}/references/dnalogel-api-capabilities.md`,T],[`${e}/references/five-runtime.md`,T.slice(4,6)],[`${e}/references/plugin-lifecycle.md`,T.slice(6,8)],[`${e}/references/official-docs.md`,T.slice(2,4)]];for(let[n,i]of o){let v=p.join(r,n);if(!await f(v))continue;let u=await w(v,"utf8");for(let d of i)u.includes(d)||a.push(s("error","agents.route-guidance",`${n} \u7F3A\u5C11\u70B9\u4F4D/\u81EA\u5B9A\u4E49\u8DEF\u7EBF\u5206\u6D41\u3001\u6821\u9A8C\u6216\u751F\u547D\u5468\u671F\u89C4\u5219\uFF1A${d}`))}return a}async function Be(r,t,e){let a=[],o=[[`${e}/references/dnalogel-api-capabilities.md`,Le],...t.map(n=>[n,Rr]),...["five-runtime.md","official-docs.md","plugin-lifecycle.md","ui-and-validation.md"].map(n=>[`${e}/references/${n}`,[Tr]])];for(let[n,i]of o){let v=p.join(r,n);if(!await f(v))continue;let u=await w(v,"utf8");for(let d of i)u.includes(d)||a.push(s("error","agents.floorplan-guidance",`${n} \u7F3A\u5C11\u5E73\u9762\u56FE endpoint\u3001\u6821\u9A8C\u3001\u52A8\u6001 tab\u3001\u591A\u697C\u5C42\u6216\u6E05\u7406\u89C4\u5219\uFF1A${d}`))}return a}async function qe(r,t){let e=[],a=[[t.validator,["export function asModelFloorplanServerData(","!isInteger(value.version)","value.version < 0","!isFloorplanServerData(value.data)","value.outlines.length > 0","value.floor_datas.length > 0","isArrayOf(value.rooms, isRoom)","isRules(value.rules)","value.entrance === null || isEntrance(value.entrance)","isBounding(value.bounding)","isArrayOf(value.observers, isObserver)","isArrayOf(value.doors, isDoor)"]],[t.types,["WorkCapabilities","modelFloorplan?: ModelFloorplanServerData","ModelFloorplanClient","RealseeSpatialClient","extends WorkDetailClient, ModelFloorplanClient"]],[t.client,["implements RealseeSpatialClient","FLOORPLAN_PLUGINS_PATH = '/open/v3/floorplan/plugins'","asRecord(descriptor.data).format_floorplan_url","floorplanUrl.protocol !== 'https:'","floorplanUrl.username","floorplanUrl.password","redirect: 'error'","asModelFloorplanServerData(payload)"]],[t.service,[".getModelFloorplan(resourceCode, { signal })",".catch((error: unknown) =>","modelFloorplan ? { capabilities: { modelFloorplan } } : {}"]],[t.workSource,["modelFloorplan?: ModelFloorplanServerData","asModelFloorplanServerData(","asRecord(envelope.capabilities).modelFloorplan","modelFloorplan ? { capabilities: { modelFloorplan } } : {}"]],[t.feature,["@realsee/dnalogel/libs/floorplan/ModelFloorplanPlugin","dnalogel.modelFloorplan","ModelFloorplanPlugin(five, { autoShowEnable: false })","plugins.register(PLUGIN_NAME, candidate)","candidate.dispose()","const modelSurface = five.getElement()","const wrapper = modelSurface?.parentElement","plugin.appendTo(wrapper)","plugin.load(serverData)","modelOpacity: 0","plugin.hide({ userAction: true })",".finally(restoreModelSurface)","if (!state.visible) restoreModelSurface()","plugins.unregister(PLUGIN_NAME, plugin)","stopStateChange()","if (disposeRequested) finalize()","if (!loading) finalize()"]],[t.app,["payload.capabilities?.modelFloorplan","if (modelFloorplan)","createModelFloorplanFeature(five, plugins)","await currentFloorplan.load(modelFloorplan)","currentFloorplan.dispose()","setFloorplanFeature(floorplan)","floorplan?.dispose()"]],[t.modeSwitcher,["floorplan?.subscribeVisibility","await floorplan.show()","if (floorplan?.visible) await floorplan.hide()","floorplan?.visible ? 'floorplan' : tabFromFiveMode(five.currentMode)","Five.Mode.Panorama","Five.Mode.Mapview"]]];for(let[i,v]of a){let u=p.join(r,i);if(!await f(u)){e.push(s("error","architecture.floorplan-missing",`\u7F3A\u5C11\u5E73\u9762\u56FE\u5408\u540C\u6587\u4EF6\uFF1A${i}`));continue}let d=b(await w(u,"utf8"));for(let m of v)d.includes(m)||e.push(s("error","architecture.floorplan-contract",`${i} \u7F3A\u5C11\u5E73\u9762\u56FE\u6821\u9A8C\u3001capability \u6216\u751F\u547D\u5468\u671F\u6807\u8BB0\uFF1A${m}`))}let o=p.join(r,t.feature);if(await f(o)){let i=b(await w(o,"utf8"));for(let[h,E]of[[/plugin\.hooks\.on\(\s*['"]show['"]/,"show hook"],[/plugin\.hooks\.on\(\s*['"]hide['"]/,"hide hook"],[/plugin\.hooks\.on\(\s*['"]stateChange['"]/,"stateChange hook"]])h.test(i)||e.push(s("error","architecture.floorplan-lifecycle",`${t.feature} \u7F3A\u5C11 ${E}\u3002`));/autoShowEnable\s*:\s*true/.test(i)&&e.push(s("error","architecture.floorplan-lifecycle",`${t.feature} \u5FC5\u987B\u4FDD\u6301 autoShowEnable: false\u3002`));let v=i.indexOf("plugin.appendTo(wrapper)"),u=i.indexOf("plugin.load(serverData)");(v<0||u<v)&&e.push(s("error","architecture.floorplan-mount",`${t.feature} \u5FC5\u987B\u5148\u628A\u63D2\u4EF6 DOM appendTo Five \u5BBF\u4E3B\uFF0C\u518D\u52A0\u8F7D format_floorplan_url \u6570\u636E\u3002`));let d=i.indexOf("stopShow()"),m=i.indexOf("stopHide()"),S=i.indexOf("stopStateChange()"),y=i.indexOf("if (!loading) finalize()"),l=i.indexOf("const finalize ="),c=i.indexOf("restoreModelSurface()",l),g=i.indexOf("plugins.unregister(PLUGIN_NAME, plugin)",l);(d<0||m<d||S<m||y<S||l<0||c<l||g<c)&&e.push(s("error","architecture.floorplan-cleanup",`${t.feature} \u5FC5\u987B\u5728 hide\u3001\u5931\u8D25\u4E0E\u9500\u6BC1\u65F6\u6062\u590D Five model surface\uFF0C\u5E76\u5148\u79FB\u9664\u5168\u90E8 hooks\uFF0C\u518D\u6309 pending load \u72B6\u6001 identity-safe \u6E05\u7406\u3002`))}let n=p.join(r,t.modeSwitcher);if(await f(n)){let i=b(await w(n,"utf8"));for(let[u,d]of[[/type\s+ViewerTab\s*=\s*['"]tour['"]\s*\|\s*['"]model['"]\s*\|\s*['"]floorplan['"]/,"\u4E09\u4E2A ViewerTab"],[/value\s*:\s*['"]tour['"]\s*,\s*text\s*:\s*['"]漫游['"]/,"Panorama \u6F2B\u6E38 tab"],[/value\s*:\s*['"]model['"]\s*,\s*text\s*:\s*['"]三维模型['"]/,"Mapview \u4E09\u7EF4\u6A21\u578B tab"],[/\.\.\.\(\s*floorplan\s*\?\s*\[\{\s*value\s*:\s*['"]floorplan['"](?:\s+as\s+const)?\s*,\s*text\s*:\s*['"]平面图['"]\s*\}\]\s*:\s*\[\]\s*\)/,"\u6761\u4EF6\u5E73\u9762\u56FE tab"]])u.test(i)||e.push(s("error","architecture.floorplan-tabs",`${t.modeSwitcher} \u7F3A\u5C11 ${d}\u3002`));(i.match(/floorplan\?\.visible\s*\?\s*['"]floorplan['"]\s*:\s*tabFromFiveMode\(five\.currentMode\)/g)?.length??0)<2&&e.push(s("error","architecture.floorplan-tabs",`${t.modeSwitcher} \u5FC5\u987B\u5728\u521D\u59CB\u72B6\u6001\u548C\u5207\u6362\u5931\u8D25\u540E\u90FD\u6309\u63D2\u4EF6\u771F\u5B9E\u53EF\u89C1\u6027\u540C\u6B65 Tab\u3002`)),(i.includes("Five.Mode.Topview")||/text\s*:\s*['"]地图['"]/.test(i))&&e.push(s("error","architecture.floorplan-tabs",`${t.modeSwitcher} \u4E0D\u5F97\u628A Topview \u66B4\u9732\u4E3A\u5730\u56FE tab\u3002`))}for(let[i,v]of[[t.browserTsconfig,'"include": ["src", "shared", "vite.config.ts"]'],[t.serverTsconfig,'"include": ["server", "shared"]']]){let u=p.join(r,i);if(!await f(u))continue;(await w(u,"utf8")).includes(v)||e.push(s("error","tooling.floorplan-include",`${i} \u5FC5\u987B\u5305\u542B shared floorplan validator\uFF1A${v}`))}return e}function xr(r){let t=new Map;for(let e of r.split(/\r?\n/)){let a=e.trim();if(!a||a.startsWith("#"))continue;let o=/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(a);o&&t.set(o[1],o[2].trim())}return t}async function He(r,t){let e=[],a=p.join(r,".env.example");if(await f(a)){let u=await w(a,"utf8"),d=xr(u);for(let m of pe)d.has(m)||e.push(s("error","env.required-key",`.env.example \u7F3A\u5C11 ${m}\u3002`));for(let m of ue)u.includes(m)||e.push(s("error","env.optional-example",`.env.example \u7F3A\u5C11\u53EF\u9009\u914D\u7F6E\u793A\u4F8B\uFF1A${m}`));d.has("REALSEE_DEFAULT_RESOURCE_CODE")&&e.push(s("error","env.optional-resource",".env.example \u4E2D REALSEE_DEFAULT_RESOURCE_CODE \u5FC5\u987B\u4FDD\u6301\u4E3A\u6CE8\u91CA\u793A\u4F8B\uFF0C\u4E0D\u80FD\u8868\u73B0\u4E3A\u9ED8\u8BA4\u5FC5\u586B\u914D\u7F6E\u3002"));for(let[m,S]of d)m.startsWith("VITE_WORK_")&&e.push(s("error","env.legacy-work-url",`.env.example \u4E0D\u5F97\u58F0\u660E\u8DEF\u7531\u6A21\u5F0F\u4E0D\u9700\u8981\u7684 ${m}\u3002`)),m.startsWith("VITE_")&&je(m)&&e.push(s("error","env.client-secret",`.env.example \u7981\u6B62\u58F0\u660E\u6D4F\u89C8\u5668\u53EF\u89C1\u7684\u5BC6\u94A5\u53D8\u91CF ${m}\u3002`)),je(m)&&!de.includes(m)&&S&&S!=="*"&&e.push(s("error","env.secret",`.env.example \u7684 ${m} \u53EA\u80FD\u4E3A\u7A7A\u6216\u4F7F\u7528 * \u5360\u4F4D\uFF0C\u4E0D\u80FD\u5305\u542B\u771F\u5B9E\u5BC6\u94A5\u3002`));for(let m of fe){let S=d.get(m);S!==void 0&&S!==""&&S!=="*"&&e.push(s("error","env.credential",`.env.example \u7684 ${m} \u53EA\u80FD\u4E3A\u7A7A\u6216\u4F7F\u7528 * \u5360\u4F4D\u3002`))}}await f(p.join(r,".env.server.example"))&&e.push(s("error","env.legacy-file","\u73AF\u5883\u914D\u7F6E\u5DF2\u5408\u5E76\u5230 .env.example\uFF0C\u4E0D\u5F97\u4FDD\u7559 .env.server.example\u3002"));let o=p.join(r,"src");if(await f(o))for(let u of await C(o)){let d=b(await w(u,"utf8")),m=ae(d);m&&e.push(s("error","env.client-server-only-reference",`${p.relative(r,u)} \u4E0D\u5F97\u8BFB\u53D6\u6216\u5F15\u7528 server-only \u914D\u7F6E\u6216\u51ED\u636E\uFF1A${m}\u3002`))}let n=p.join(r,"src","vr","work","WorkSource.ts");if(await f(n)){let u=b(await w(n,"utf8"));for(let d of["resourceCodeFromPathname","window.location.pathname","segments.length === 0","segments.length !== 1",": '/api/work'","`/api/work/${encodeURIComponent(resourceCode)}`","asExplicitPanoTagList","hasCanonicalPanoTagData(value.contentType, value.data)","hasCanonicalPanoTagAnchor(value)","asRecord(envelope.presetData)","presetData: { panoTagList }","displayName: string","displayName: displayNameFrom(work.name) ?? workCode","new RouteWorkSource()"])u.includes(d)||e.push(s("error","architecture.resource-route",`WorkSource \u7F3A\u5C11\u9ED8\u8BA4/\u517C\u5BB9\u8DEF\u7531\u3001\u540C\u6E90 API \u6216\u663E\u5F0F preset \u6807\u8BB0\uFF1A${d}`));for(let d of["VITE_WORK_URL","/work.json","envelope.tags","envelope.tagList","envelope.tag_list","payload.tags"])u.includes(d)&&e.push(s("error","architecture.resource-route",`WorkSource \u4E0D\u5F97\u4F9D\u8D56\u65E7 Work URL\u3001\u672C\u5730 Work \u6587\u4EF6\u6216\u628A generic tags \u6620\u5C04\u5230 PanoTag\uFF1A${d}`))}for(let u of["vite.config.ts","vite.config.js","vite.config.mjs"]){let d=p.join(r,u);if(!await f(d))continue;let m=b(await w(d,"utf8")),S=ae(m);(S||/envPrefix\s*:\s*[^\n]*REALSEE_/.test(m))&&e.push(s("error","env.vite-secret",`${u} \u4E0D\u5F97\u628A server-only \u914D\u7F6E\u3001AK/SK/token \u6216 REALSEE_* \u524D\u7F00\u6CE8\u5165\u6D4F\u89C8\u5668${S?`\uFF1A${S}`:"\u3002"}`));let y=m.match(/\bhost\s*:\s*['"]127\.0\.0\.1['"]/g)?.length??0,l=m.match(/\bport\s*:\s*8087\b/g)?.length??0,c=m.match(/\bstrictPort\s*:\s*true\b/g)?.length??0,g=/target\s*:\s*['"]http:\/\/127\.0\.0\.1:8088['"]/.test(m);(y<2||l<2||c<2||!g)&&e.push(s("error","architecture.dev-route",`${u} \u5FC5\u987B\u8BA9 Vite server/preview \u4F7F\u7528 strict IPv4 127.0.0.1:8087\uFF0C\u5E76\u628A /api \u4EE3\u7406\u5230 Fastify 127.0.0.1:8088\u3002`))}let i=[["server/types.ts",["ServerConfig","WorkEnvelope","WorkDetailClient","WorkService","defaultResourceCode: string | undefined"]],["server/config.ts",["loadServerConfig","REALSEE_GATEWAY_APP_KEY","REALSEE_GATEWAY_APP_SECRET","REALSEE_DEFAULT_RESOURCE_CODE","optionalResourceCode","if (!nonEmptyString(value)) return undefined","^[A-Za-z0-9_-]{1,128}$","gatewayEndpoint.protocol !== 'https:'","): ServerConfig {"]],["server/check-config.ts",["loadServerConfig()","BFF server configuration is valid."]],["server/realsee-open-api.ts",["RealseeOpenApiClient","implements RealseeSpatialClient","ACCESS_TOKEN_PATH = '/auth/access_token'","'application/x-www-form-urlencoded'","WORK_INFO_PATH = '/open/v3/vr/info'","Authorization: accessToken","resource_code: resourceCode","UNAUTHORIZED_UPSTREAM_CODES","TOKEN_REFRESH_SKEW_MS"]],["server/work-service.ts",["createWorkService","): WorkService {","getWorkByResourceCode","openApiClient.getWorkDetail(resourceCode, { signal })"]],["server/work-route.ts",["'/api/work'","'/api/work/:resourceCode'","defaultResourceCode: string | undefined","error: 'RESOURCE_CODE_REQUIRED'","getWorkByResourceCode(resourceCode, {","signal: requestAbort.signal","WORK_UPSTREAM_FAILED"]],["server/app.ts",["Fastify({ logger })","'/api/health'","defaultResourceCode: string | undefined","fastifyStatic","serveFrontend","reply.sendFile('index.html')","Promise<FastifyInstance>"]],["server/index.ts",["loadServerConfig","RealseeOpenApiClient","createWorkService","createApp","app.listen({ host: config.host, port: config.port })"]]];for(let[u,d]of i){let m=p.join(r,u);if(!await f(m)){e.push(s("error","architecture.bff-missing",`\u7F3A\u5C11 Fastify BFF \u6587\u4EF6\uFF1A${u}`));continue}let S=b(await w(m,"utf8"));for(let y of d)S.includes(y)||e.push(s("error","architecture.bff-contract",`${u} \u7F3A\u5C11 BFF \u89E3\u8026\u6216 OpenAPI \u5951\u7EA6\uFF1A${y}`))}let v=p.join(r,t);if(await f(v)){let u=new Set((await w(v,"utf8")).split(/\r?\n/).map(d=>d.trim()).filter(Boolean));for(let d of[".env*","!.env.example"])u.has(d)||e.push(s("error","env.gitignore",`${t} \u5FC5\u987B\u5305\u542B ${d}\u3002`))}return e}async function $r(r,t){return t.name!==H?!1:Object.hasOwn(t.bin??{},"vr-cli")||await f(p.join(r,"bin","vr-cli.mjs"))||await f(p.join(r,"templates","base"))}async function Mr(r,t){let e=[];t.private!==!1&&e.push(s("error","tool.private","\u53EF\u53D1\u5E03\u5DE5\u5177\u5FC5\u987B\u663E\u5F0F\u58F0\u660E private: false\u3002"));let a=Object.entries(t.bin??{});(a.length!==1||a[0]?.[0]!=="vr-cli"||a[0]?.[1]!=="bin/vr-cli.mjs")&&e.push(s("error","tool.bin","\u5F00\u53D1\u5DE5\u5177\u5FC5\u987B\u53EA\u66B4\u9732 vr-cli -> bin/vr-cli.mjs\u3002")),((typeof t.repository=="string"?t.repository:t.repository?.url)??null)!==null&&e.push(s("error","tool.repository",null===null?"\u5F53\u524D\u53D1\u5E03 profile \u8981\u6C42 package.json \u7701\u7565 repository\u3002":`package.json repository \u5FC5\u987B\u4E3A ${null}\u3002`));let n=t.publishConfig?.registry??null,i=t.publishConfig?.access??null;(n!==D||i!==$)&&e.push(s("error","tool.publish-profile",`package.json publishConfig \u5FC5\u987B\u5339\u914D\u5F53\u524D\u53D1\u5E03 profile\uFF1Aregistry=${D}\uFF0Caccess=${$??"\u672A\u8BBE\u7F6E"}\u3002`)),e.push(...await Ge(r,t));let v=["README.md",...await f(p.join(r,".git"))?[".nvmrc",".node-version",".npmrc"]:[],"agent-assets/AGENTS.md","bin","bin/vr-cli.mjs",...$==="public"?["dist/cli.mjs"]:["src","src/core/spatial-policy.mjs"],"templates/base","templates/base/README.md","templates/base/.editorconfig","templates/base/.env.example","templates/base/.gitignore.template","templates/base/.nvmrc","templates/base/.node-version","templates/base/.npmrc.template","templates/base/.prettierignore","templates/base/eslint.config.mjs","templates/base/package-lock.json","templates/base/prettier.config.mjs","templates/base/tsconfig.server.json","templates/base/server/types.ts","templates/base/server/config.ts","templates/base/server/check-config.ts","templates/base/server/realsee-open-api.ts","templates/base/server/work-service.ts","templates/base/server/work-route.ts","templates/base/server/app.ts","templates/base/server/index.ts","templates/base/shared/model-floorplan.ts","templates/base/src/vr/five/api.ts","templates/base/src/vr/five/fiveConfig.ts","templates/base/src/vr/five/SpatialRuntimeProvider.tsx","templates/base/src/vr/five/initialState.ts","templates/base/src/vr/plugins/dnalogel.ts","templates/base/src/vr/plugins/SpatialPluginRegistry.ts","templates/base/src/vr/plugins/examples/FeatureOwnedMeshPlugin.ts","templates/base/src/vr/work/WorkSource.ts","templates/viewer/src/app/App.tsx","templates/viewer/src/features/floorplan/ModelFloorplanFeature.ts","templates/viewer/src/features/navigation/ModeSwitcher.tsx","templates/viewer/src/features/tags/PanoTagFeature.ts","templates/viewer/src/ui/ViewerOverlay.tsx","skills/realsee-vr-app","skills/realsee-vr-app/references/five-api-capabilities.md","skills/realsee-vr-app/references/dnalogel-api-capabilities.md","skills/realsee-vr-app/references/five-runtime.md","skills/realsee-vr-app/references/official-docs.md","skills/realsee-vr-app/references/plugin-lifecycle.md","skills/realsee-vr-app/references/ui-and-validation.md","skills/realsee-vr-app/references/vapor-dom-layer.md","skills/realsee-image-resources/SKILL.md","skills/realsee-image-resources/references/cors-and-three-textures.md","skills/realsee-image-resources/references/data-wanxiang.md","skills/realsee-image-resources/references/five-image-url-transform.md","skills/realsee-image-resources/references/source-and-domain-policy.md","skills/threejs-r117-five/SKILL.md","skills/threejs-r117-five/references/r117-capabilities.md","skills/threejs-r117-five/references/r117-version-guards.md","skills/threejs-r117-five/references/five-ownership.md"];for(let h of v)await f(p.join(r,h))||e.push(s("error","tool.missing",`\u7F3A\u5C11\u5DE5\u5177\u8DEF\u5F84\uFF1A${h}`));for(let h of["templates/base/README.md","agent-assets/AGENTS.md"]){let E=p.join(r,h);if(!await f(E))continue;let k=await w(E,"utf8");k.includes("{{TOOL_VERSION}}")||e.push(s("error","tool.token",`${h} \u5FC5\u987B\u4F7F\u7528 {{TOOL_VERSION}} \u7248\u672C token\u3002`)),k.includes("__TOOL_VERSION__")&&e.push(s("error","tool.token",`${h} \u4E0D\u5F97\u4F7F\u7528\u4E0E Markdown \u7C97\u4F53\u51B2\u7A81\u7684 __TOOL_VERSION__ token\u3002`))}let u=p.join(r,"templates","base");if(await f(u)){let h=new Set(["deploy.json","package-lock.json","package.json","tsconfig.json","tsconfig.server.json"]);for(let E of await W(u))E.endsWith(".json")&&!h.has(E)&&e.push(s("error","tool.data-asset",`\u6A21\u677F\u4E0D\u5F97\u5206\u53D1\u672A\u7ECF\u660E\u786E\u5BA1\u6838\u7684 JSON/Work \u6570\u636E\uFF1Atemplates/base/${E}`))}e.push(...await We(r,["skills/realsee-vr-app/SKILL.md","agent-assets/AGENTS.md"],"skills/realsee-vr-app/references/dnalogel-api-capabilities.md")),e.push(...await Ye(r,["skills/realsee-vr-app/SKILL.md","agent-assets/AGENTS.md"],"skills/realsee-vr-app")),e.push(...await Be(r,["agent-assets/AGENTS.md","skills/realsee-vr-app/SKILL.md"],"skills/realsee-vr-app")),e.push(...await qe(r,{validator:"templates/base/shared/model-floorplan.ts",types:"templates/base/server/types.ts",client:"templates/base/server/realsee-open-api.ts",service:"templates/base/server/work-service.ts",workSource:"templates/base/src/vr/work/WorkSource.ts",feature:"templates/viewer/src/features/floorplan/ModelFloorplanFeature.ts",app:"templates/viewer/src/app/App.tsx",modeSwitcher:"templates/viewer/src/features/navigation/ModeSwitcher.tsx",browserTsconfig:"templates/base/tsconfig.json",serverTsconfig:"templates/base/tsconfig.server.json"})),e.push(...await Ke(r,["agent-assets/AGENTS.md","skills/realsee-vr-app/SKILL.md","skills/realsee-image-resources/SKILL.md"])),e.push(...await ze(r,["agent-assets/AGENTS.md","skills/realsee-vr-app/SKILL.md","skills/realsee-vr-app/references/vapor-dom-layer.md"])),e.push(...await Ue(r,"skills/realsee-vr-app/references/vapor-dom-layer.md","skills/realsee-vr-app/references/ui-and-validation.md")),e.push(...await Ve(r,"templates/base/src/vr/plugins/examples/FeatureOwnedMeshPlugin.ts"));let d=p.join(r,"templates","base","src","main.tsx");if(await f(d)){let h=b(await w(d,"utf8"));(Pe(h)!==1||h.includes("@realsee/vapor/vapor.scss"))&&e.push(s("error","tool.vapor-css","\u9ED8\u8BA4\u6A21\u677F\u5FC5\u987B\u6070\u597D\u4E00\u6B21\u4EE5\u53EF\u6267\u884C side-effect import \u52A0\u8F7D @realsee/vapor/vapor.css\uFF0C\u4E14\u4E0D\u5F97\u4F7F\u7528 vapor.scss\u3002"))}let m=p.join(r,"templates","base","src","vr","work","WorkSource.ts");if(await f(m)){let h=b(await w(m,"utf8"));for(let E of["displayName: string","displayNameFrom(work.name) ?? workCode"])h.includes(E)||e.push(s("error","tool.work-name",`\u9ED8\u8BA4 WorkSource \u5FC5\u987B\u4ECE work.name \u5B89\u5168\u6D3E\u751F VR \u5C55\u793A\u540D\u79F0\uFF1A${E}`))}let S=p.join(r,"templates","viewer","src","ui","ViewerOverlay.tsx");if(await f(S)){let h=b(await w(S,"utf8")),E=Ne.filter(k=>!B(h,k)||!q(h,k));E.length>0&&e.push(s("error","tool.vapor-provider",`\u9ED8\u8BA4 ViewerOverlay \u5FC5\u987B\u4ECE @realsee/vapor \u5BFC\u5165\u5E76\u5B9E\u9645\u6E32\u67D3\u5B98\u65B9 Demo \u7EC4\u4EF6\uFF1A${E.join(", ")}`));for(let k of $e)h.includes(k)||e.push(s("error","tool.viewer-topbar",`\u9ED8\u8BA4 ViewerOverlay \u5FC5\u987B\u4F7F\u7528 Vapor TitleBar\u3001Layer \u4E0E\u54CD\u5E94\u5F0F Stack\uFF0C\u5E76\u6E32\u67D3\u5DE6\u4E0A\u6807\u9898\u3001\u5C45\u4E2D controls \u4E0E\u72B6\u6001\u8BED\u4E49\uFF1A${k}`));for(let k of Fe)h.includes(k)&&e.push(s("error","tool.viewer-native-vapor",`\u9ED8\u8BA4 ViewerOverlay \u4E0D\u5F97\u7528\u539F\u751F\u6807\u9898\u6216\u81EA\u5B9A\u4E49 glass \u6837\u5F0F\u66FF\u4EE3 Vapor \u7EC4\u4EF6\u89C6\u89C9\uFF1A${k}`))}let y=p.join(r,"templates","base","src","styles.css");if(await f(y)){let h=await w(y,"utf8");for(let E of Ce)h.includes(E)||e.push(s("error","tool.viewer-layout",`\u9ED8\u8BA4 Viewer \u5FC5\u987B\u4FDD\u6301 safe-area \u53CA\u684C\u9762/\u5E73\u677F/\u624B\u673A/\u77EE\u5C4F\u5916\u5C42\u54CD\u5E94\u5F0F\u5E03\u5C40\uFF1A${E}`));for(let E of De)h.includes(E)&&e.push(s("error","tool.viewer-native-vapor",`\u9ED8\u8BA4 Viewer \u6837\u5F0F\u4E0D\u5F97\u8986\u76D6 Vapor \u5185\u90E8\u7C7B\u6216\u91CD\u7ED8\u7EC4\u4EF6\u89C6\u89C9\uFF1A${E}`))}let l=p.join(r,"templates","viewer","src","features","navigation","ModeSwitcher.tsx");if(await f(l)){let h=b(await w(l,"utf8")),E=xe.filter(k=>!B(h,k)||!q(h,k));E.length>0&&e.push(s("error","tool.vapor-component",`\u9ED8\u8BA4 ModeSwitcher \u5FC5\u987B\u4ECE @realsee/vapor \u5BFC\u5165\u5E76\u5B9E\u9645\u6E32\u67D3\u5B98\u65B9 Demo \u7684\u6A21\u5F0F\u5207\u6362\u7EC4\u4EF6\uFF1A${E.join(", ")}`));for(let k of Me)h.includes(k)||e.push(s("error","tool.viewer-control-size",`\u9ED8\u8BA4 ModeSwitcher \u5FC5\u987B\u4F7F\u7528\u5B98\u65B9 Demo \u7684 Font/HStack/VStack \u4E0E shadow Button \u7EC4\u5408\uFF1A${k}`));for(let k of Ie)h.includes(k)&&e.push(s("error","tool.viewer-native-vapor",`\u9ED8\u8BA4 ModeSwitcher \u4E0D\u5F97\u9000\u56DE Segmented \u6216\u6DFB\u52A0\u65E7\u7684\u62C9\u4F38/glass \u6837\u5F0F\uFF1A${k}`))}let c=p.join(r,"templates","viewer","src");if(await f(c))for(let h of await C(c)){let E=b(await w(h,"utf8")),k=ae(E);k&&e.push(s("error","env.client-server-only-reference",`${p.relative(r,h)} \u4E0D\u5F97\u8BFB\u53D6\u6216\u5F15\u7528 server-only \u914D\u7F6E\u6216\u51ED\u636E\uFF1A${k}\u3002`))}let g=p.join(r,"templates","viewer","src","app","App.tsx");if(await f(g)){let h=b(await w(g,"utf8"));for(let E of["setWorkName(payload.displayName)","title={workName}"])h.includes(E)||e.push(s("error","tool.work-name",`\u9ED8\u8BA4 App \u5FC5\u987B\u628A WorkSource \u5C55\u793A\u540D\u79F0\u4F20\u7ED9 ViewerOverlay\uFF1A${E}`));for(let E of["FeatureOwnedMeshPlugin","registerFeatureOwnedMeshPlugin","vr/plugins/examples"])h.includes(E)&&e.push(s("error","tool.custom-plugin-enabled",`\u9ED8\u8BA4 Viewer \u4E0D\u5F97\u542F\u7528\u81EA\u5B9A\u4E49\u63D2\u4EF6\u793A\u4F8B\uFF1A${E}`))}return e.push(...await He(p.join(r,"templates","base"),".gitignore.template")),e}async function Cr(r,t,e){let a=new Set;for(let o of e)await f(p.join(r,o))&&a.add(o);for(let o of t){let n=p.join(r,o);if(!await f(n))continue;if(!(await mr(n)).isDirectory()){a.add(o);continue}for(let v of await W(n))a.add(p.posix.join(o,v))}return a}async function Fr(r){let t=[],e=p.join(r,".vr-cli.json");if(!await f(e)){let i=p.join(r,".agents"),v=await f(p.join(r,"AGENTS.md")),u=await f(i);if(!v&&!u)t.push(s("warning","agents.missing","\u672A\u5B89\u88C5\u8DE8 Agent \u6307\u4EE4\u8D44\u4EA7\u3002"));else for(let d of["skills/realsee-vr-app/SKILL.md","skills/realsee-image-resources/SKILL.md","skills/realsee-image-resources/references/cors-and-three-textures.md","skills/realsee-image-resources/references/data-wanxiang.md","skills/realsee-image-resources/references/five-image-url-transform.md","skills/realsee-image-resources/references/source-and-domain-policy.md","skills/threejs-r117-five/SKILL.md","skills/threejs-r117-five/references/r117-capabilities.md","skills/threejs-r117-five/references/r117-version-guards.md","skills/threejs-r117-five/references/five-ownership.md"])await f(p.join(i,d))||t.push(s("error","agents.skill-missing",`.agents/${d} \u7F3A\u5931\u3002`));return t}let a;try{a=await M(e)}catch(i){return[s("error","manifest.invalid",".vr-cli.json \u65E0\u6CD5\u89E3\u6790\u3002",String(i))]}if(!(a&&typeof a=="object"&&!Array.isArray(a)&&a.schemaVersion===1&&O.includes(a.agentAssets)))return[s("error","manifest.invalid",".vr-cli.json \u7F3A\u5C11\u6709\u6548\u7684 schemaVersion \u6216 agentAssets\u3002")];for(let i of Y(a.agentAssets))await f(p.join(r,i))||t.push(s("error","agents.asset-missing",`agentAssets=${a.agentAssets} \u7F3A\u5C11\uFF1A${i}`));let n=a.agentAssets==="portable"?{directoryRoots:_e,singleFiles:Ae}:a.agentAssets==="none"?{directoryRoots:ye,singleFiles:be}:null;if(n)for(let i of await Cr(r,n.directoryRoots,n.singleFiles))t.push(s("error","agents.unexpected",`agentAssets=${a.agentAssets} \u4E0D\u5E94\u5305\u542B\uFF1A${i}`));return t}async function Ir(r,t){let e=[];t.scripts?.["package:vibe-release"]!=="node scripts/package-vibe-release.mjs"&&e.push(s("error","release.script","package.json scripts.package:vibe-release \u5FC5\u987B\u8FD0\u884C node scripts/package-vibe-release.mjs\u3002")),t.devDependencies?.[P]!==L&&e.push(s("error","release.zip-dependency",`${P} \u5FC5\u987B\u4F5C\u4E3A\u5F00\u53D1\u4F9D\u8D56\u7CBE\u786E\u56FA\u5B9A\u4E3A ${L}\u3002`));try{let o=await M(p.join(r,"deploy.json")),n=d=>d!==null&&typeof d=="object"&&!Array.isArray(d),i=[],v=null,u=null;if(!n(o))i.push("\u5FC5\u987B\u662F JSON \u5BF9\u8C61");else{o.packageFormat!==Q&&i.push(`packageFormat \u5FC5\u987B\u662F ${Q}`);let d=new Set(["packageFormat","name","requiredEnv","resources","domain","healthCheck"]),m=Object.keys(o).find(y=>!d.has(y));if(m&&i.push(`\u4E0D\u652F\u6301\u5B57\u6BB5 ${m}`),!Array.isArray(o.requiredEnv)||o.requiredEnv.length>20)v="\u5FC5\u987B\u662F\u6700\u591A 20 \u9879\u7684\u6570\u7EC4";else{let y=new Set;for(let l of o.requiredEnv){let c=String(l??"").trim();if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(c)){v=`\u540D\u79F0\u4E0D\u5408\u6CD5\uFF1A${c||"(\u7A7A)"}`;break}if(y.has(c)){v=`\u5305\u542B\u91CD\u590D\u9879\uFF1A${c}`;break}y.add(c)}}let S=o.healthCheck;if(S!==void 0)if(!n(S))u="\u5FC5\u987B\u662F\u5BF9\u8C61";else{let y=new Set(["enabled","path","timeoutMs","retries"]),l=Object.keys(S).find(c=>!y.has(c));if(l)u=`\u4E0D\u652F\u6301\u5B57\u6BB5 ${l}`;else if(S.enabled!==void 0&&S.enabled!==!0)u="enabled \u5B58\u5728\u65F6\u5FC5\u987B\u4E3A true";else if(S.path!==void 0&&S.path!=="/api/health")u="path \u5B58\u5728\u65F6\u5FC5\u987B\u4E3A /api/health";else if(S.timeoutMs!==void 0){let c=Number(S.timeoutMs);(!Number.isFinite(c)||c<500||c>3e4)&&(u="timeoutMs \u5FC5\u987B\u5728 500-30000 \u4E4B\u95F4")}if(!u&&S.retries!==void 0){let c=Number(S.retries);(!Number.isInteger(c)||c<1||c>60)&&(u="retries \u5FC5\u987B\u662F 1-60 \u4E4B\u95F4\u7684\u6574\u6570")}}}i.length>0&&e.push(s("error","release.manifest",`deploy.json \u53D1\u5E03\u6E05\u5355\u65E0\u6548\uFF1A${i.join("\uFF1B")}\u3002`)),v&&e.push(s("error","release.required-env",`deploy.json.requiredEnv ${v}\uFF1B\u6A21\u677F\u9ED8\u8BA4\u503C\u4E3A ${le.join(", ")}\u3002`)),u&&e.push(s("error","release.health",`deploy.json.healthCheck ${u}\u3002`))}catch(o){e.push(s("error","release.manifest","deploy.json \u65E0\u6CD5\u89E3\u6790\u3002",String(o)))}try{let o=await M(p.join(r,"package-lock.json"));(o.packages?.[""]?.devDependencies?.[P]!==L||o.packages?.[`node_modules/${P}`]?.version!==L)&&e.push(s("error","release.zip-lock",`package-lock.json \u5FC5\u987B\u89E3\u6790\u5E76\u9501\u5B9A ${P}@${L}\u3002`))}catch(o){e.push(s("error","release.zip-lock","\u65E0\u6CD5\u68C0\u67E5\u53D1\u5E03\u6253\u5305\u4F9D\u8D56\u7684 lockfile\u3002",String(o)))}return e}async function Dr(r,t){let e=[];t.private!==!0&&e.push(s("error","project.public","\u751F\u6210\u9879\u76EE\u5FC5\u987B\u4FDD\u6301 private\u3002")),t.type!=="module"&&e.push(s("error","project.module","\u751F\u6210\u9879\u76EE\u5FC5\u987B\u4F7F\u7528 ESM\uFF1Atype=module\u3002")),e.push(...await Ge(r,t));for(let l of me)await f(p.join(r,l))||e.push(s("error","project.missing",`\u7F3A\u5C11\u751F\u6210\u6587\u4EF6\uFF1A${l}`));e.push(...await Ir(r,t));for(let[l,c,g]of[["dependency",t.dependencies,z],["dev-dependency",t.devDependencies,J]])for(let[h,E]of Object.entries(g)){let k=c?.[h];if(k===void 0){e.push(s("error",`${l}.missing`,`${h} \u5FC5\u987B\u58F0\u660E\uFF0C\u751F\u6210\u5668\u57FA\u7EBF\u4E3A ${E}`));continue}if(h in U){k!==E&&e.push(s("error",`${l}.locked`,`${h} \u5FC5\u987B\u9501\u5B9A\u4E3A ${E}\uFF0C\u5F53\u524D\u4E3A ${k}\uFF1BFive 6.8.9 \u4E0E Three \u80FD\u529B\u53C2\u8003\u5747\u9488\u5BF9\u8BE5\u7248\u672C\uFF0C\u5347\u7EA7\u9700\u6574\u4F53\u8BC4\u4F30\u5E76\u540C\u6B65\u751F\u6210\u5668\u3002`));continue}let I=X(k);if(!I){e.push(s("error",`${l}.range`,`${h} \u5FC5\u987B\u4F7F\u7528\u5F00\u653E\u6700\u4F4E\u7248\u672C\u8303\u56F4 >=x.y.z\uFF0C\u751F\u6210\u5668\u57FA\u7EBF\u4E3A >=${E}\uFF0C\u5F53\u524D\u4E3A ${k}\u3002`));continue}if(!N(I,E)){e.push(s("error",`${l}.minimum`,`${h} \u7684\u58F0\u660E\u4E0B\u9650 ${I} \u4F4E\u4E8E\u751F\u6210\u5668\u57FA\u7EBF ${E}\u3002`));continue}k!==ce(h,E)&&e.push(s("warning",`${l}.updated`,`${h} \u7684\u6700\u4F4E\u7248\u672C\u5DF2\u4ECE\u751F\u6210\u5668\u57FA\u7EBF ${E} \u63D0\u9AD8\u5230 ${I}\uFF1B\u8BF7\u786E\u8BA4 npm run check \u4E0E npm run build \u901A\u8FC7\uFF0C\u5E76\u5728\u5347\u7EA7 SDK \u65F6\u590D\u6838\u7248\u672C\u5316 fixture\u3002`))}for(let[l,c]of[["dev",["check:env","concurrently"]],["setup",["scripts/setup.mjs"]],["setup:complete",["scripts/setup.mjs","--complete"]],["test:setup",["--test","scripts/setup-config.test.mjs","scripts/project-identity.test.mjs","scripts/pairing.test.mjs"]],["dev:bff",["--env-file-if-exists=.env.development","server/index.ts"]],["dev:web",["vite"]],["check:env",["--env-file-if-exists=.env.development","server/check-config.ts"]],["typecheck",["npm run typecheck:web","npm run typecheck:bff"]],["typecheck:web",["tsc -p tsconfig.json"]],["typecheck:bff",["tsc -p tsconfig.server.json"]],["lint",["eslint . --max-warnings=0"]],["format",["prettier --write ."]],["format:check",["prettier --check ."]],["check",["npm run typecheck","npm run lint","npm run format:check"]],["check:bff",["typecheck:bff"]],["deps:relock",["npm install --package-lock-only"]],["deps:install",["npm ci"]],["start",["--env-file-if-exists=.env.development","server/index.ts","--serve-frontend"]]])for(let g of c)t.scripts?.[l]?.includes(g)||e.push(s("error","script.contract",`package.json scripts.${l} \u5FC5\u987B\u5305\u542B\uFF1A${g}`));e.push(...await Nr(r,t));for(let[l,c]of[["eslint.config.mjs",["from '@eslint/js'","from 'typescript-eslint'","from 'eslint-plugin-react-hooks'","recommendedTypeChecked","tsconfig.server.json","globals.browser","globals.node","'react-hooks/rules-of-hooks'","'react-hooks/exhaustive-deps'","no-floating-promises","disableTypeChecked"]],["prettier.config.mjs",["semi: false","singleQuote: true","trailingComma: 'all'"]],["tsconfig.json",['"strict": true','"noUncheckedIndexedAccess": true','"noEmit": true','"include": ["src", "shared", "vite.config.ts"]']],["tsconfig.server.json",['"strict": true','"erasableSyntaxOnly": true','"verbatimModuleSyntax": true','"exactOptionalPropertyTypes": true','"noUncheckedIndexedAccess": true','"types": ["node"]','"include": ["server", "shared"]']]]){let g=p.join(r,l);if(!await f(g))continue;let h=await w(g,"utf8");for(let E of c)h.includes(E)||e.push(s("error","tooling.config",`${l} \u7F3A\u5C11\u914D\u7F6E\u6807\u8BB0\uFF1A${E}`))}let a=p.join(r,"src","main.tsx");if(await f(a)){let l=await w(a,"utf8"),c=b(l);(Pe(c)!==1||c.includes("@realsee/vapor/vapor.scss"))&&e.push(s("error","vapor.css","src/main.tsx \u5FC5\u987B\u6070\u597D\u4E00\u6B21\u4EE5\u53EF\u6267\u884C side-effect import \u5BFC\u5165 @realsee/vapor/vapor.css\uFF0C\u4E14\u4E0D\u5F97\u4F7F\u7528 vapor.scss\u3002")),c.includes("./app/App")||e.push(s("error","architecture.app","src/main.tsx \u5FC5\u987B\u4ECE app \u5C42\u7EC4\u5408\u7A7A\u95F4\u8FD0\u884C\u65F6\u4E0E DOM UI\u3002"))}let o=p.join(r,"src","vr","five","api.ts");if(await f(o)){let l=await w(o,"utf8"),c=b(l);for(let g of["@realsee/five","@realsee/five/plugins","@realsee/five/line","@realsee/five/sticker","@realsee/five/vfx","@realsee/five/gltf-loader","@realsee/five/shader-lib","@realsee/five/work-downloader"])c.includes(`'${g}'`)||e.push(s("error","architecture.five-api",`Five \u80FD\u529B\u5165\u53E3\u7F3A\u5931\uFF1A${g}`))}let n=p.join(r,"src","vr","plugins","dnalogel.ts");await f(n)&&(b(await w(n,"utf8")).includes("from '@realsee/dnalogel'")||e.push(s("error","architecture.dnalogel-api","dnalogel \u9884\u8BBE\u63D2\u4EF6\u6839\u80FD\u529B\u5165\u53E3\u7F3A\u5931\u3002"))),e.push(...await Ve(r,"src/vr/plugins/examples/FeatureOwnedMeshPlugin.ts"));let i=p.join(r,"src");if(await f(i))for(let l of await C(i)){let c=b(await w(l,"utf8"));c.includes("@realsee/five/react")&&e.push(s("error","architecture.five-react",`${p.relative(r,l)} \u7981\u6B62\u4F7F\u7528\u66F4\u65B0\u6EDE\u540E\u7684 @realsee/five/react\uFF1B\u8BF7\u76F4\u63A5\u4F7F\u7528\u539F\u751F Five API\u3002`));for(let g of Er)g.pattern.test(c)&&e.push(s("error","compatibility.three-r117",`${p.relative(r,l)}\uFF1A${g.message}`))}let v=p.join(r,"src","vr","five","SpatialRuntimeProvider.tsx");if(await f(v)){let l=await w(v,"utf8"),c=b(l);(c.includes("unsafe__useFiveInstance")||c.includes("provider?.five"))&&e.push(s("error","architecture.five-instance","\u7A7A\u95F4\u8FD0\u884C\u65F6\u7981\u6B62\u901A\u8FC7 Five React wrapper \u83B7\u53D6\u5B9E\u4F8B\u3002")),/import\s*\{[^}]*\bFive\b[^}]*\}\s*from\s*['"]@realsee\/five['"]/.test(c)||e.push(s("error","architecture.five-native","SpatialRuntimeProvider \u5FC5\u987B\u4ECE @realsee/five \u5BFC\u5165\u539F\u751F Five\u3002"));for(let E of["new Five(",".appendTo(",".refresh(",".getElement()",".dispose()","new ResizeObserver","new SpatialRuntimeLifetime","runExclusive<T>(","ownedLifetime?.disposeFeatures()","ownedLifetime.afterPending(finalize)","width <= 0 || height <= 0",'className="five-surface"'])c.includes(E)||e.push(s("error","architecture.five-native",`\u539F\u751F Five \u751F\u547D\u5468\u671F\u7F3A\u5C11\uFF1A${E}`));let g=c.indexOf("ownedLifetime?.disposeFeatures()"),h=c.indexOf("ownedLifetime.afterPending(finalize)");(g<0||h<g)&&e.push(s("error","architecture.five-cleanup","\u5FC5\u987B\u5148\u540C\u6B65\u505C\u6B62 feature\uFF0C\u518D\u7B49\u5F85\u539F\u751F\u5F02\u6B65\u4EFB\u52A1\u5B8C\u6210\u540E\u6E05\u7406\u63D2\u4EF6\u548C Five\u3002"))}let u=p.join(r,"src","app","App.tsx");if(await f(u)){let l=await w(u,"utf8"),c=b(l);for(let g of["lifetime.own(","lifetime.runExclusive(","await five.load(","await waitForActiveModel(","const panoTagList = payload.presetData?.panoTagList","if (panoTagList?.length)","await currentTags.load(panoTagList)","setWorkName(payload.displayName)","title={workName}"])c.includes(g)||e.push(s("error","architecture.feature-lifetime",`App \u7684\u539F\u751F\u5F02\u6B65\u751F\u547D\u5468\u671F\u6216\u663E\u5F0F PanoTag preset \u6761\u4EF6\u7F3A\u5C11\uFF1A${g}`));c.includes("payload.tags")&&e.push(s("error","architecture.pano-tag-contract","App \u4E0D\u5F97\u628A generic payload.tags \u76F4\u63A5\u52A0\u8F7D\u4E3A PanoTag\u3002"))}let d=p.join(r,"src","ui"),m=p.join(d,"ViewerOverlay.tsx");if(await f(m)){let l=b(await w(m,"utf8")),c=Ne.filter(g=>!B(l,g)||!q(l,g));c.length>0&&e.push(s("error","architecture.vapor-provider",`ViewerOverlay \u5FC5\u987B\u4ECE @realsee/vapor \u5BFC\u5165\u5E76\u5B9E\u9645\u6E32\u67D3\u5B98\u65B9 Demo \u7EC4\u4EF6\uFF1A${c.join(", ")}`));for(let g of $e)l.includes(g)||e.push(s("error","architecture.viewer-topbar",`ViewerOverlay \u5FC5\u987B\u4F7F\u7528 Vapor TitleBar\u3001Layer \u4E0E\u54CD\u5E94\u5F0F Stack\uFF0C\u5E76\u6E32\u67D3\u5DE6\u4E0A\u6807\u9898\u3001\u5C45\u4E2D controls \u4E0E\u72B6\u6001\u8BED\u4E49\uFF1A${g}`));for(let g of Fe)l.includes(g)&&e.push(s("error","architecture.viewer-native-vapor",`ViewerOverlay \u4E0D\u5F97\u7528\u539F\u751F\u6807\u9898\u6216\u81EA\u5B9A\u4E49 glass \u6837\u5F0F\u66FF\u4EE3 Vapor \u7EC4\u4EF6\u89C6\u89C9\uFF1A${g}`))}let S=p.join(r,"src","features","navigation","ModeSwitcher.tsx");if(await f(S)){let l=b(await w(S,"utf8")),c=xe.filter(g=>!B(l,g)||!q(l,g));c.length>0&&e.push(s("error","architecture.vapor-component",`ModeSwitcher \u5FC5\u987B\u4ECE @realsee/vapor \u5BFC\u5165\u5E76\u5B9E\u9645\u6E32\u67D3\u5B98\u65B9 Demo \u7684\u6A21\u5F0F\u5207\u6362\u7EC4\u4EF6\uFF1A${c.join(", ")}`));for(let g of Me)l.includes(g)||e.push(s("error","architecture.viewer-control-size",`ModeSwitcher \u5FC5\u987B\u4F7F\u7528\u5B98\u65B9 Demo \u7684 Font/HStack/VStack \u4E0E shadow Button \u7EC4\u5408\uFF1A${g}`));for(let g of Ie)l.includes(g)&&e.push(s("error","architecture.viewer-native-vapor",`ModeSwitcher \u4E0D\u5F97\u9000\u56DE Segmented \u6216\u6DFB\u52A0\u65E7\u7684\u62C9\u4F38/glass \u6837\u5F0F\uFF1A${g}`))}if(await f(d))for(let l of await C(d)){let c=b(await w(l,"utf8")),g=/(?:from|import)\s*(?:\(\s*)?['"][^'"]*\/vr\//.test(c);(c.includes("@realsee/five")||c.includes("@realsee/dnalogel")||g)&&e.push(s("error","architecture.ui-boundary",`${p.relative(r,l)} \u4E0D\u5F97\u5BFC\u5165 src/vr\u3001Five \u6216 dnalogel \u7A7A\u95F4\u8FD0\u884C\u65F6\u3002`))}let y=p.join(r,"src","styles.css");if(await f(y)){let l=await w(y,"utf8");(!l.includes(".five-surface")||!l.includes(".dom-overlay")||!l.includes("pointer-events: none"))&&e.push(s("error","architecture.dom-overlay","\u539F\u751F Five surface \u4E0E DOM overlay \u5FC5\u987B\u4FDD\u6301\u72EC\u7ACB\u5206\u5C42\u53CA pointer-events \u9694\u79BB\u3002"));for(let c of Ce)l.includes(c)||e.push(s("error","architecture.viewer-layout",`Viewer \u5FC5\u987B\u4FDD\u6301 safe-area \u53CA\u684C\u9762/\u5E73\u677F/\u624B\u673A/\u77EE\u5C4F\u5916\u5C42\u54CD\u5E94\u5F0F\u5E03\u5C40\uFF1A${c}`));for(let c of De)l.includes(c)&&e.push(s("error","architecture.viewer-native-vapor",`Viewer \u6837\u5F0F\u4E0D\u5F97\u8986\u76D6 Vapor \u5185\u90E8\u7C7B\u6216\u91CD\u7ED8\u7EC4\u4EF6\u89C6\u89C9\uFF1A${c}`))}return e.push(...await He(r,".gitignore")),e.push(...await We(r,[".agents/skills/realsee-vr-app/SKILL.md","AGENTS.md"],".agents/skills/realsee-vr-app/references/dnalogel-api-capabilities.md")),e.push(...await Ye(r,[".agents/skills/realsee-vr-app/SKILL.md","AGENTS.md"],".agents/skills/realsee-vr-app")),e.push(...await Be(r,["AGENTS.md",".agents/skills/realsee-vr-app/SKILL.md"],".agents/skills/realsee-vr-app")),e.push(...await qe(r,{validator:"shared/model-floorplan.ts",types:"server/types.ts",client:"server/realsee-open-api.ts",service:"server/work-service.ts",workSource:"src/vr/work/WorkSource.ts",feature:"src/features/floorplan/ModelFloorplanFeature.ts",app:"src/app/App.tsx",modeSwitcher:"src/features/navigation/ModeSwitcher.tsx",browserTsconfig:"tsconfig.json",serverTsconfig:"tsconfig.server.json"})),e.push(...await Ke(r,["AGENTS.md",".agents/skills/realsee-vr-app/SKILL.md",".agents/skills/realsee-image-resources/SKILL.md"])),e.push(...await ze(r,["AGENTS.md",".agents/skills/realsee-vr-app/SKILL.md",".agents/skills/realsee-vr-app/references/vapor-dom-layer.md"])),e.push(...await Ue(r,".agents/skills/realsee-vr-app/references/vapor-dom-layer.md",".agents/skills/realsee-vr-app/references/ui-and-validation.md")),e.push(...await Fr(r)),e}async function Ze({directory:r=".",json:t=!1}){let e=p.resolve(r),a=p.join(e,"package.json"),o=[];N(process.versions.node,V)||o.push(s("error","node.version",`\u9700\u8981 Node >=${V}\uFF0C\u5F53\u524D ${process.versions.node}`));try{let i=await Lr();N(i,K)||o.push(s("error","npm.version",`\u9700\u8981 npm >=${K}\uFF0C\u5F53\u524D ${i}`))}catch(i){o.push(s("error","npm.unavailable","\u65E0\u6CD5\u6267\u884C npm --version\u3002",String(i)))}if(!await f(a))o.push(s("error","package.missing",`\u627E\u4E0D\u5230 package.json\uFF1A${a}`));else try{let i=await M(a);o.push(...await $r(e,i)?await Mr(e,i):await Dr(e,i))}catch(i){o.push(s("error","package.invalid","package.json \u65E0\u6CD5\u89E3\u6790\u3002",String(i)))}let n={ok:!o.some(i=>i.level==="error"),root:e,errors:o.filter(i=>i.level==="error").length,warnings:o.filter(i=>i.level==="warning").length,diagnostics:o};if(t)console.log(JSON.stringify(n,null,2));else{for(let i of o)console.log(`${i.level==="error"?"ERROR":"WARN "} [${i.code}] ${i.message}`);console.log(n.ok?`OK ${e}`:`FAILED ${e}`)}return n}var F=Object.freeze({version:1,capabilityOrder:["@realsee/dnalogel","@realsee/five","three"],intentRoutes:Object.freeze([Object.freeze({intent:"tag|hotspot|anchor",defaultCapability:"PanoTagPlugin",source:"GET /open/v3/hot/list"}),Object.freeze({intent:"tour",defaultCapability:"Five State snapshot",source:"GET /open/v3/observerpicture/search"}),Object.freeze({intent:"model-annotation",defaultCapability:"ModelMakerPlugin",source:"GET /open/v3/annotation/search"}),Object.freeze({intent:"floorplan|house-layout",defaultCapability:"ModelFloorplanPlugin",source:"GET /open/v3/floorplan/plugins -> data.format_floorplan_url"}),Object.freeze({intent:"route",defaultCapability:"GuideLinePlugin + CruisePlugin|MovePlugin",source:"GET /open/v3/route/search"})]),invariants:Object.freeze(["OpenAPI data is fetched by the BFF, validated, then adapted to installed SDK types.","SDK-shaped local data requires an explicit Open Platform opt-out.","A fallback records the missing preset capability before using Five or Three.","Only a completely validated { version, data: FloorplanServerData } wrapper from data.format_floorplan_url may expose the modelFloorplan capability.","Missing, empty, malformed or failed floorplan data is feature-local and leaves the base Viewer usable.","Each feature owns cancellation, registrations, hooks, media and Three resources.","Persistent Work or customer-data mutations require an authoritative write contract and confirmation."])});var Qe=`vr-cli ${j}
3
3
 
4
4
  Usage:
5
5
  vr-cli create <directory> [options]
@@ -18,7 +18,7 @@ Examples:
18
18
  vr-cli create ./my-vrapp --preset viewer --agent-assets all --no-install
19
19
  vr-cli doctor ./my-vrapp --json
20
20
  vr-cli policy --json
21
- `;function $r(r){return Mr({args:r,allowPositionals:!0,strict:!0,options:{preset:{type:"string"},name:{type:"string"},"agent-assets":{type:"string",default:"portable"},"no-install":{type:"boolean",default:!1},json:{type:"boolean",default:!1},help:{type:"boolean",short:"h",default:!1},version:{type:"boolean",short:"v",default:!1}}})}async function mt(r){try{let{values:t,positionals:e}=$r(r);if(t.version){console.log(j);return}let s=e[0]||(t.help?"help":void 0);if(!s||s==="help"){console.log(Ye);return}if(s==="create"){if(t.preset&&t.preset!=="viewer")throw new Error("--preset \u6682\u65F6\u53EA\u652F\u6301 viewer\u3002");if(!O.includes(t["agent-assets"]))throw new Error(`--agent-assets \u5FC5\u987B\u662F\uFF1A${O.join(", ")}`);let n=await be({directory:e[1],preset:t.preset,name:t.name,agentAssets:t["agent-assets"],install:!t["no-install"]});console.log(`
22
- Created viewer project at ${n.targetRoot}`),n.installed?console.log("Next: npm run setup"):console.log("Next: npm ci && npm run setup"),console.log("Configure: paste the single-line Portal setup payload into the hidden prompt, then run npm run dev"),console.log("Open: http://127.0.0.1:8087/");return}if(s==="doctor"){(await Ue({directory:e[1]||".",json:t.json})).ok||(process.exitCode=1);return}if(s==="policy"){if(t.json)console.log(JSON.stringify(M,null,2));else{console.log(`Spatial policy v${M.version}`),console.log(`Capability order: ${M.capabilityOrder.join(" -> ")}`);for(let n of M.intentRoutes)console.log(`${n.intent}: ${n.defaultCapability} (${n.source})`)}return}throw new Error(`\u672A\u77E5\u547D\u4EE4\uFF1A${s}
21
+ `;function Vr(r){return Gr({args:r,allowPositionals:!0,strict:!0,options:{preset:{type:"string"},name:{type:"string"},"agent-assets":{type:"string",default:"portable"},"no-install":{type:"boolean",default:!1},json:{type:"boolean",default:!1},help:{type:"boolean",short:"h",default:!1},version:{type:"boolean",short:"v",default:!1}}})}async function St(r){try{let{values:t,positionals:e}=Vr(r);if(t.version){console.log(j);return}let a=e[0]||(t.help?"help":void 0);if(!a||a==="help"){console.log(Qe);return}if(a==="create"){if(t.preset&&t.preset!=="viewer")throw new Error("--preset \u6682\u65F6\u53EA\u652F\u6301 viewer\u3002");if(!O.includes(t["agent-assets"]))throw new Error(`--agent-assets \u5FC5\u987B\u662F\uFF1A${O.join(", ")}`);let o=await Te({directory:e[1],preset:t.preset,name:t.name,agentAssets:t["agent-assets"],install:!t["no-install"]});console.log(`
22
+ Created viewer project at ${o.targetRoot}`),o.installed?console.log("Next: npm run setup"):console.log("Next: npm ci && npm run setup"),console.log("Configure: run npm run setup -- --no-wait --json, then npm run setup -- --complete after the user confirms the pairing request in the Portal"),console.log("Fallback: npm run setup -- --paste (single-line Portal payload in the hidden prompt)"),console.log("Open: http://127.0.0.1:8087/{resourceCode} (or / when setup includes the optional resource fallback)");return}if(a==="doctor"){(await Ze({directory:e[1]||".",json:t.json})).ok||(process.exitCode=1);return}if(a==="policy"){if(t.json)console.log(JSON.stringify(F,null,2));else{console.log(`Spatial policy v${F.version}`),console.log(`Capability order: ${F.capabilityOrder.join(" -> ")}`);for(let o of F.intentRoutes)console.log(`${o.intent}: ${o.defaultCapability} (${o.source})`)}return}throw new Error(`\u672A\u77E5\u547D\u4EE4\uFF1A${a}
23
23
 
24
- ${Ye}`)}catch(t){console.error(t instanceof Error?t.message:String(t)),process.exitCode=1}}export{mt as main};
24
+ ${Qe}`)}catch(t){console.error(t instanceof Error?t.message:String(t)),process.exitCode=1}}export{St as main};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realsee/vr-cli",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "description": "Create and validate TypeScript projects for Realsee spatial Viewer applications.",
6
6
  "keywords": [
@@ -117,7 +117,7 @@ For every image consumer, also apply these sibling image-resource rules:
117
117
  3. Construct `Five` directly from `@realsee/five`; never import `@realsee/five/react`, `createFiveProvider`, `FiveCanvas` or its hooks.
118
118
  4. Keep the native `Five` instance available through `useSpatialRuntime`; feature adapters must not narrow away SDK capabilities.
119
119
  5. Create and append Five only after its host has non-zero dimensions; call `five.refresh` for later positive resizes.
120
- 6. Resolve `/{resourceCode}` in the browser and call only same-origin `/api/work/{resourceCode}`. Parse Work with explicit `workCode` and optional `baseURL`; keep AK/SK, tokens and OpenAPI calls server-only. AK/SK authorize BFF upstream access; internet-facing caller or tenant authorization belongs before the Work route and must never expose AK/SK to the browser.
120
+ 6. Resolve `/{resourceCode}` in the browser and call only same-origin `/api/work/{resourceCode}`; this explicit route is always available. Treat `/` and `/api/work` as an optional `REALSEE_DEFAULT_RESOURCE_CODE` fallback: without it, return a controlled 4xx before any upstream request. Parse Work with explicit `workCode` and optional `baseURL`; keep AK/SK, tokens and OpenAPI calls server-only. The default `deploy.json.requiredEnv` contains only AK/SK. AK/SK authorize BFF upstream access; internet-facing caller or tenant authorization belongs before the Work route and must never expose AK/SK to the browser.
121
121
  7. Keep the generated TypeScript BFF replaceable: `server/types.ts` owns the contracts, the Realsee client owns token exchange/cache and upstream calls, the Work service owns the framework-neutral envelope, and Fastify only adapts HTTP. A Go or other backend must preserve the browser endpoint and response contract.
122
122
  8. Treat Work loaded, model refined and feature ready as separate gates.
123
123
  9. Use Five `State` as the authoritative spatial snapshot with Work identity.