@realsee/vr-cli 0.1.4 → 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.
Files changed (31) hide show
  1. package/README.md +46 -29
  2. package/agent-assets/AGENTS.md +8 -5
  3. package/dist/cli.mjs +5 -5
  4. package/package.json +1 -1
  5. package/skills/realsee-vr-app/SKILL.md +1 -1
  6. package/skills/realsee-vr-app/references/dnalogel-openapi-recipes.md +90 -26
  7. package/skills/realsee-vr-app/references/five-runtime.md +2 -2
  8. package/skills/realsee-vr-app/scripts/verify-project.mjs +194 -13
  9. package/templates/base/.env.example +4 -2
  10. package/templates/base/.gitignore.template +2 -0
  11. package/templates/base/.prettierignore +2 -0
  12. package/templates/base/README.md +35 -8
  13. package/templates/base/deploy.json +11 -0
  14. package/templates/base/eslint.config.mjs +2 -0
  15. package/templates/base/package-lock.json +11 -0
  16. package/templates/base/package.json +6 -1
  17. package/templates/base/scripts/package-vibe-release.mjs +569 -0
  18. package/templates/base/scripts/pairing.mjs +564 -0
  19. package/templates/base/scripts/pairing.test.mjs +227 -0
  20. package/templates/base/scripts/project-identity.mjs +160 -0
  21. package/templates/base/scripts/project-identity.test.mjs +119 -0
  22. package/templates/base/scripts/setup-config.mjs +252 -0
  23. package/templates/base/scripts/setup-config.test.mjs +123 -0
  24. package/templates/base/scripts/setup.mjs +203 -0
  25. package/templates/base/server/app.ts +3 -1
  26. package/templates/base/server/config.ts +12 -1
  27. package/templates/base/server/index.ts +5 -1
  28. package/templates/base/server/types.ts +2 -1
  29. package/templates/base/server/work-route.ts +51 -32
  30. package/templates/base/server/work-service.ts +2 -2
  31. package/templates/base/src/vr/work/WorkSource.ts +13 -9
@@ -5,6 +5,7 @@ const RESOURCE_CODE_PATTERN = '^[A-Za-z0-9_-]{1,128}$'
5
5
 
6
6
  export interface WorkRoutesOptions {
7
7
  readonly workService: WorkService
8
+ readonly defaultResourceCode: string | undefined
8
9
  }
9
10
 
10
11
  interface WorkRouteParams {
@@ -23,9 +24,56 @@ const upstreamStatusCode = (error: unknown): number => {
23
24
 
24
25
  export function workRoutes(
25
26
  app: FastifyInstance,
26
- { workService }: WorkRoutesOptions,
27
+ { workService, defaultResourceCode }: WorkRoutesOptions,
27
28
  done: (error?: Error) => void,
28
29
  ): void {
30
+ const getWork = async (
31
+ request: FastifyRequest,
32
+ reply: FastifyReply,
33
+ resourceCode: string,
34
+ ): Promise<WorkEnvelope | FastifyReply> => {
35
+ const requestAbort = new AbortController()
36
+ const abortDisconnectedRequest = (): void => {
37
+ if (request.raw.aborted || !request.raw.complete) requestAbort.abort()
38
+ }
39
+ request.raw.once('close', abortDisconnectedRequest)
40
+
41
+ try {
42
+ return await workService.getWorkByResourceCode(resourceCode, {
43
+ signal: requestAbort.signal,
44
+ })
45
+ } catch (error) {
46
+ const statusCode = upstreamStatusCode(error)
47
+ request.log.error({ err: error }, 'Work BFF request failed')
48
+ return reply.code(statusCode).send({
49
+ error:
50
+ statusCode === 404
51
+ ? 'WORK_NOT_FOUND'
52
+ : statusCode === 504
53
+ ? 'WORK_UPSTREAM_TIMEOUT'
54
+ : 'WORK_UPSTREAM_FAILED',
55
+ requestId: request.id,
56
+ })
57
+ } finally {
58
+ request.raw.off('close', abortDisconnectedRequest)
59
+ }
60
+ }
61
+
62
+ app.get(
63
+ '/api/work',
64
+ async (request, reply): Promise<WorkEnvelope | FastifyReply> => {
65
+ if (!defaultResourceCode) {
66
+ return reply.code(400).send({
67
+ error: 'RESOURCE_CODE_REQUIRED',
68
+ message:
69
+ 'Provide resourceCode at /api/work/{resourceCode} or configure the optional REALSEE_DEFAULT_RESOURCE_CODE fallback.',
70
+ requestId: request.id,
71
+ })
72
+ }
73
+ return getWork(request, reply, defaultResourceCode)
74
+ },
75
+ )
76
+
29
77
  app.get(
30
78
  '/api/work/:resourceCode',
31
79
  {
@@ -43,37 +91,8 @@ export function workRoutes(
43
91
  async (
44
92
  request: FastifyRequest<{ Params: WorkRouteParams }>,
45
93
  reply: FastifyReply,
46
- ): Promise<WorkEnvelope | FastifyReply> => {
47
- const { resourceCode } = request.params
48
- const requestAbort = new AbortController()
49
- const abortDisconnectedRequest = (): void => {
50
- if (request.raw.aborted || !request.raw.complete) requestAbort.abort()
51
- }
52
- request.raw.once('close', abortDisconnectedRequest)
53
-
54
- try {
55
- return await workService.getWorkByResourceCode(resourceCode, {
56
- signal: requestAbort.signal,
57
- })
58
- } catch (error) {
59
- const statusCode = upstreamStatusCode(error)
60
- request.log.error(
61
- { err: error, resourceCode },
62
- 'Work BFF request failed',
63
- )
64
- return reply.code(statusCode).send({
65
- error:
66
- statusCode === 404
67
- ? 'WORK_NOT_FOUND'
68
- : statusCode === 504
69
- ? 'WORK_UPSTREAM_TIMEOUT'
70
- : 'WORK_UPSTREAM_FAILED',
71
- requestId: request.id,
72
- })
73
- } finally {
74
- request.raw.off('close', abortDisconnectedRequest)
75
- }
76
- },
94
+ ): Promise<WorkEnvelope | FastifyReply> =>
95
+ getWork(request, reply, request.params.resourceCode),
77
96
  )
78
97
 
79
98
  done()
@@ -27,8 +27,8 @@ export function createWorkService({
27
27
  .catch((error: unknown) => {
28
28
  if (!signal?.aborted) {
29
29
  console.warn(
30
- `资源 ${resourceCode} 的平面图 capability 不可用,继续返回基础 Work。`,
31
- error,
30
+ '所选空间的平面图 capability 不可用,继续返回基础 Work。',
31
+ error instanceof Error ? error.name : 'UnknownError',
32
32
  )
33
33
  }
34
34
  return undefined
@@ -177,7 +177,9 @@ const asExplicitPanoTagList = (value: unknown): Tags['tagList'] | undefined => {
177
177
 
178
178
  export function resourceCodeFromPathname(pathname: string) {
179
179
  const segments = pathname.split('/').filter(Boolean)
180
- if (segments.length !== 1) throw new Error('访问路径必须为 /{resourceCode}。')
180
+ if (segments.length === 0) return undefined
181
+ if (segments.length !== 1)
182
+ throw new Error('访问路径必须为 / 或 /{resourceCode}。')
181
183
  const [encodedResourceCode = ''] = segments
182
184
 
183
185
  let resourceCode: string
@@ -199,13 +201,13 @@ export class RouteWorkSource implements WorkSource {
199
201
  async load(signal?: AbortSignal): Promise<WorkPayload> {
200
202
  const resourceCode = resourceCodeFromPathname(window.location.pathname)
201
203
  // The generated Fastify BFF implements this contract; another language may replace it without changing the browser.
202
- const response = await fetch(
203
- `/api/work/${encodeURIComponent(resourceCode)}`,
204
- {
205
- signal,
206
- headers: { Accept: 'application/json' },
207
- },
208
- )
204
+ const workUrl = resourceCode
205
+ ? `/api/work/${encodeURIComponent(resourceCode)}`
206
+ : '/api/work'
207
+ const response = await fetch(workUrl, {
208
+ signal,
209
+ headers: { Accept: 'application/json' },
210
+ })
209
211
  if (!response.ok) throw new Error(`Work 请求失败:HTTP ${response.status}`)
210
212
 
211
213
  const raw = (await response.json()) as unknown
@@ -221,7 +223,9 @@ export class RouteWorkSource implements WorkSource {
221
223
  work.workCode,
222
224
  work.work_code,
223
225
  work.code,
224
- ) ?? resourceCode
226
+ ) ??
227
+ resourceCode ??
228
+ 'unknown-work'
225
229
  const presetData = asRecord(envelope.presetData)
226
230
  const panoTagList = asExplicitPanoTagList(presetData.panoTagList)
227
231
  const modelFloorplan = asModelFloorplanServerData(