@hiyve/cli 1.0.17 → 1.0.19

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/README.md CHANGED
@@ -27,7 +27,7 @@ Authenticate with Hiyve and configure npm for @hiyve packages.
27
27
  npx @hiyve/cli login
28
28
 
29
29
  # Non-interactive (CI/CD)
30
- npx @hiyve/cli login --key sk_live_your_secret_key_here
30
+ npx @hiyve/cli login --key pk_live_your_api_key_here
31
31
  ```
32
32
 
33
33
  ### `logout`
@@ -67,9 +67,11 @@ Templates: `basic`, `ai`, `full`
67
67
 
68
68
  ## Getting Your API Key
69
69
 
70
- 1. Log in to the [Hiyve Developer Console](https://api.hiyve.dev)
70
+ 1. Log in to the [Hiyve Developer Console](https://console.hiyve.dev)
71
71
  2. Navigate to **API Keys** in the sidebar
72
- 3. Copy your secret key (starts with `sk_test_` or `sk_live_`)
72
+ 3. Copy your **API key** (starts with `pk_test_` or `pk_live_`). The client
73
+ secret (`sk_*`) is for minting room tokens server-side and is **not** used
74
+ for registry login — the CLI rejects it.
73
75
 
74
76
  ## What Does Login Do?
75
77
 
@@ -78,8 +80,8 @@ The `login` command:
78
80
  1. Validates your API key with the Hiyve registry
79
81
  2. Adds two lines to your `~/.npmrc` file:
80
82
  ```
81
- @hiyve:registry=https://api.hiyve.dev/registry/
82
- //api.hiyve.dev/registry/:_authToken=sk_live_...
83
+ @hiyve:registry=https://registry.muziemedia.com/
84
+ //registry.muziemedia.com/:_authToken=pk_live_...
83
85
  ```
84
86
 
85
87
  This tells npm to fetch `@hiyve/*` packages from the private Hiyve registry instead of the public npm registry.
@@ -89,8 +91,8 @@ This tells npm to fetch `@hiyve/*` packages from the private Hiyve registry inst
89
91
  For automated deployments, add registry credentials as environment variables in your CI/CD pipeline. Your project `.npmrc` should use variable expansion:
90
92
 
91
93
  ```ini
92
- @hiyve:registry=https://api.hiyve.dev/registry/
93
- //api.hiyve.dev/registry/:_authToken=${HIYVE_API_KEY}
94
+ @hiyve:registry=https://registry.muziemedia.com/
95
+ //registry.muziemedia.com/:_authToken=${HIYVE_API_KEY}
94
96
  ```
95
97
 
96
98
  Set `HIYVE_API_KEY` in your CI environment (GitHub Actions secrets, AWS SSM, etc.). npm natively expands `${ENV_VAR}` in `.npmrc` files — no custom tooling needed.
@@ -99,7 +101,7 @@ Set `HIYVE_API_KEY` in your CI environment (GitHub Actions secrets, AWS SSM, etc
99
101
 
100
102
  ### "Invalid API key" error
101
103
 
102
- - Make sure your key starts with `sk_test_` or `sk_live_`
104
+ - Make sure your key starts with `pk_test_` or `pk_live_` (not `sk_` — that is the client secret)
103
105
  - Verify your account is active in the developer console
104
106
 
105
107
  ### "Connection failed" error
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hiyve/cli",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Hiyve SDK CLI - Configure npm for private @hiyve packages",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,8 +13,7 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "test": "vitest run --pool=forks --poolOptions.forks.singleFork",
16
- "test:watch": "vitest",
17
- "deploy": "./deploy.sh"
16
+ "test:watch": "vitest"
18
17
  },
19
18
  "keywords": [
20
19
  "hiyve",
@@ -14,7 +14,10 @@ const TEMPLATES = {
14
14
  basic: {
15
15
  name: 'Basic',
16
16
  description: 'Video conferencing with controls and participant list',
17
- packages: ['@hiyve/react', '@hiyve/react-ui', '@hiyve/core', '@hiyve/rtc-client', '@hiyve/utilities'],
17
+ packages: [
18
+ '@hiyve/react', '@hiyve/react-ui', '@hiyve/core', '@hiyve/rtc-client', '@hiyve/utilities',
19
+ '@hiyve/admin',
20
+ ],
18
21
  features: { intelligence: false, collaboration: false, capture: false },
19
22
  },
20
23
  ai: {
@@ -23,21 +26,65 @@ const TEMPLATES = {
23
26
  packages: [
24
27
  '@hiyve/react', '@hiyve/react-ui', '@hiyve/react-intelligence', '@hiyve/react-capture',
25
28
  '@hiyve/core', '@hiyve/cloud', '@hiyve/rtc-client', '@hiyve/utilities',
29
+ '@hiyve/admin',
26
30
  ],
27
31
  features: { intelligence: true, collaboration: false, capture: true },
28
32
  },
29
33
  full: {
30
34
  name: 'Full Suite',
31
35
  description: 'Video + AI + chat, polls, Q&A, file sharing, notes',
36
+ // react-room's REQUIRED peers are all here: react-intelligence and
37
+ // react-semantic-relay are imported statically, not optionally.
32
38
  packages: [
33
- '@hiyve/react', '@hiyve/react-ui', '@hiyve/react-intelligence', '@hiyve/react-capture',
34
- '@hiyve/react-collaboration', '@hiyve/react-notes', '@hiyve/react-room',
39
+ '@hiyve/react', '@hiyve/react-ui', '@hiyve/react-intelligence', '@hiyve/react-semantic-relay',
40
+ '@hiyve/react-capture', '@hiyve/react-collaboration', '@hiyve/react-notes', '@hiyve/react-room',
35
41
  '@hiyve/core', '@hiyve/cloud', '@hiyve/rtc-client', '@hiyve/utilities',
42
+ '@hiyve/admin',
36
43
  ],
37
44
  features: { intelligence: true, collaboration: true, capture: true },
38
45
  },
39
46
  };
40
47
 
48
+ /**
49
+ * The signaling region every generated project pins. It appears in BOTH
50
+ * `.env.example` (SERVER_REGION — what the token server mints for) and the
51
+ * client (`region` on HiyveProvider / HiyveRoom). They must agree: a token
52
+ * presented to a stack that did not sign it is rejected with a generic
53
+ * "Internal server error".
54
+ */
55
+ export const DEFAULT_REGION = 'us-east-2';
56
+
57
+ /** Port the generated token server listens on; vite proxies /api to it. */
58
+ const SERVER_PORT = 3001;
59
+
60
+ /**
61
+ * Pinned versions. Taken from what `npm create vite@latest -- --template
62
+ * react-ts` emits (2026-09-01) plus the peer ranges the published @hiyve
63
+ * packages declare: React 18 or 19, MUI ^9. Bumping MUI below 9 or React
64
+ * below 18 makes `npm install` fail with ERESOLVE against the registry.
65
+ */
66
+ export const PINS = {
67
+ react: '^19.2.8',
68
+ 'react-dom': '^19.2.8',
69
+ '@mui/material': '^9.4.0',
70
+ '@mui/icons-material': '^9.4.0',
71
+ '@emotion/react': '^11.14.0',
72
+ '@emotion/styled': '^11.14.1',
73
+ express: '^5.2.1',
74
+ cors: '^2.8.6',
75
+ dotenv: '^17.4.2',
76
+ '@types/react': '^19.2.18',
77
+ '@types/react-dom': '^19.2.4',
78
+ '@types/express': '^5.0.6',
79
+ '@types/cors': '^2.8.19',
80
+ '@types/node': '^24.13.3',
81
+ '@vitejs/plugin-react': '^6.1.0',
82
+ concurrently: '^10.0.5',
83
+ tsx: '^4.23.13',
84
+ typescript: '~6.0.2',
85
+ vite: '^8.2.2',
86
+ };
87
+
41
88
  export async function init(projectName, options) {
42
89
  console.log('');
43
90
  console.log(chalk.cyan(' Hiyve Project Scaffolding'));
@@ -102,8 +149,11 @@ export async function init(projectName, options) {
102
149
  writeFile(projectDir, 'vite.config.ts', generateViteConfig());
103
150
  writeFile(projectDir, '.env.example', generateEnvExample(template));
104
151
  writeFile(projectDir, '.gitignore', generateGitignore());
105
- writeFile(projectDir, 'src/main.tsx', generateMain());
152
+ writeFile(projectDir, 'src/main.tsx', generateMain(template));
106
153
  writeFile(projectDir, 'src/App.tsx', generateApp(template));
154
+ if (!usesPrebuiltRoom(template)) {
155
+ writeFile(projectDir, 'src/VideoRoom.tsx', generateVideoRoom());
156
+ }
107
157
  writeFile(projectDir, 'index.html', generateIndexHtml(projectName));
108
158
  writeFile(projectDir, 'server/index.ts', generateServer(template));
109
159
 
@@ -113,7 +163,7 @@ export async function init(projectName, options) {
113
163
  console.log(chalk.gray(' Next steps:'));
114
164
  console.log('');
115
165
  console.log(` ${chalk.cyan('cd')} ${projectName}`);
116
- console.log(` ${chalk.cyan('cp')} .env.example .env ${chalk.gray('# Add your API key')}`);
166
+ console.log(` ${chalk.cyan('cp')} .env.example .env ${chalk.gray('# Add your API key + client secret (console.hiyve.dev)')}`);
117
167
  console.log(` ${chalk.cyan('npm install')}`);
118
168
  console.log(` ${chalk.cyan('npm run dev')}`);
119
169
  console.log('');
@@ -136,17 +186,23 @@ function writeFile(dir, filePath, content) {
136
186
  writeFileSync(fullPath, content, 'utf-8');
137
187
  }
138
188
 
139
- function generatePackageJson(name, template) {
189
+ function usesPrebuiltRoom(template) {
190
+ return template.features.intelligence && template.features.collaboration;
191
+ }
192
+
193
+ export function generatePackageJson(name, template) {
140
194
  const deps = {};
141
195
  for (const pkg of template.packages) {
142
196
  deps[pkg] = 'latest';
143
197
  }
144
- deps['react'] = '^18.2.0';
145
- deps['react-dom'] = '^18.2.0';
146
- deps['@mui/material'] = '^5.15.0';
147
- deps['@mui/icons-material'] = '^5.15.0';
148
- deps['@emotion/react'] = '^11.11.0';
149
- deps['@emotion/styled'] = '^11.11.0';
198
+ for (const k of ['react', 'react-dom', '@mui/material', '@mui/icons-material', '@emotion/react', '@emotion/styled', 'express', 'cors', 'dotenv']) {
199
+ deps[k] = PINS[k];
200
+ }
201
+
202
+ const devDependencies = {};
203
+ for (const k of ['@types/react', '@types/react-dom', '@types/express', '@types/cors', '@types/node', '@vitejs/plugin-react', 'concurrently', 'tsx', 'typescript', 'vite']) {
204
+ devDependencies[k] = PINS[k];
205
+ }
150
206
 
151
207
  const pkg = {
152
208
  name,
@@ -154,39 +210,32 @@ function generatePackageJson(name, template) {
154
210
  private: true,
155
211
  type: 'module',
156
212
  scripts: {
157
- dev: 'concurrently "vite" "tsx watch server/index.ts"',
213
+ dev: 'concurrently "tsx watch server/index.ts" "vite"',
158
214
  build: 'vite build',
159
215
  preview: 'vite preview',
160
- 'server': 'tsx server/index.ts',
216
+ server: 'tsx server/index.ts',
161
217
  },
162
218
  dependencies: deps,
163
- devDependencies: {
164
- '@types/react': '^18.2.0',
165
- '@types/react-dom': '^18.2.0',
166
- '@vitejs/plugin-react': '^4.2.0',
167
- 'concurrently': '^8.2.0',
168
- 'dotenv': '^16.3.0',
169
- 'express': '^4.18.0',
170
- 'tsx': '^4.7.0',
171
- 'typescript': '^5.3.0',
172
- 'vite': '^5.0.0',
173
- },
219
+ devDependencies,
174
220
  };
175
221
 
176
222
  return JSON.stringify(pkg, null, 2) + '\n';
177
223
  }
178
224
 
179
- function generateTsConfig() {
225
+ export function generateTsConfig() {
226
+ // Mirrors the react-ts template `npm create vite@latest` emits, with one
227
+ // tsconfig covering both src/ (browser) and server/ (tsx).
180
228
  const config = {
181
229
  compilerOptions: {
182
- target: 'ES2020',
183
- useDefineForClassFields: true,
184
- lib: ['ES2020', 'DOM', 'DOM.Iterable'],
230
+ target: 'ES2023',
231
+ lib: ['ES2023', 'DOM', 'DOM.Iterable'],
185
232
  module: 'ESNext',
186
- skipLibCheck: true,
187
233
  moduleResolution: 'bundler',
234
+ types: ['vite/client', 'node'],
235
+ skipLibCheck: true,
188
236
  allowImportingTsExtensions: true,
189
- resolveJsonModule: true,
237
+ verbatimModuleSyntax: true,
238
+ moduleDetection: 'force',
190
239
  isolatedModules: true,
191
240
  noEmit: true,
192
241
  jsx: 'react-jsx',
@@ -195,46 +244,55 @@ function generateTsConfig() {
195
244
  noUnusedParameters: true,
196
245
  noFallthroughCasesInSwitch: true,
197
246
  },
198
- include: ['src'],
247
+ include: ['src', 'server', 'vite.config.ts'],
199
248
  };
200
249
  return JSON.stringify(config, null, 2) + '\n';
201
250
  }
202
251
 
203
- function generateViteConfig() {
252
+ export function generateViteConfig() {
204
253
  return `import { defineConfig } from 'vite';
205
254
  import react from '@vitejs/plugin-react';
206
255
 
207
256
  export default defineConfig({
208
257
  plugins: [react()],
209
258
  server: {
210
- port: 3000,
211
259
  proxy: {
212
- '/api': 'http://localhost:4000',
260
+ // The token server (server/index.ts). @hiyve/react fetches
261
+ // /api/generate-room-token and /api/generate-cloud-token from here.
262
+ '/api': 'http://localhost:${SERVER_PORT}',
213
263
  },
214
264
  },
215
265
  });
216
266
  `;
217
267
  }
218
268
 
219
- function generateEnvExample(template) {
220
- let env = `# Hiyve API Key (from cloud.hiyve.io)
221
- HIYVE_API_KEY=pk_live_your_api_key_here
269
+ export function generateEnvExample(template) {
270
+ let env = `# Both keys come from https://console.hiyve.dev (API Keys)
271
+
272
+ # API Key (required — identifies your app)
273
+ APIKEY=pk_live_your_api_key_here
274
+
275
+ # Client Secret (required for video conferencing — mints room tokens; server-side only)
276
+ CLIENT_SECRET=sk_live_your_client_secret_here
222
277
 
223
- # Room configuration
224
- HIYVE_SIGNALING_URL=https://signal.hiyve.dev
278
+ # Signaling region the token server mints for. MUST match the \`region\`
279
+ # passed to HiyveProvider / HiyveRoom in src/ (${DEFAULT_REGION} in both).
280
+ SERVER_REGION=${DEFAULT_REGION}
281
+ SERVER_REGION_URL=.rtc.muziemedia.com
282
+ ENVIRONMENT=development
225
283
  `;
226
284
 
227
285
  if (template.features.intelligence) {
228
286
  env += `
229
- # Cloud API (for AI features)
230
- HIYVE_CLOUD_URL=https://cloud.hiyve.io
287
+ # Cloud API for AI features. Defaults to https://cloud.hiyve.io — leave unset.
288
+ # HIYVE_CLOUD_API_URL=https://cloud.hiyve.io
231
289
  `;
232
290
  }
233
291
 
234
292
  return env;
235
293
  }
236
294
 
237
- function generateGitignore() {
295
+ export function generateGitignore() {
238
296
  return `node_modules
239
297
  dist
240
298
  .env
@@ -242,53 +300,70 @@ dist
242
300
  `;
243
301
  }
244
302
 
245
- function generateMain() {
246
- return `import React from 'react';
247
- import ReactDOM from 'react-dom/client';
303
+ export function generateMain(template) {
304
+ if (usesPrebuiltRoom(template)) {
305
+ // HiyveRoom (in App.tsx) provides the HiyveProvider, theme and region.
306
+ return `import ReactDOM from 'react-dom/client';
248
307
  import App from './App';
249
308
 
309
+ ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
310
+ `;
311
+ }
312
+
313
+ return `import ReactDOM from 'react-dom/client';
314
+ import { HiyveProvider } from '@hiyve/react';
315
+ import App from './App';
316
+
317
+ // region MUST match SERVER_REGION in .env — see .env.example.
250
318
  ReactDOM.createRoot(document.getElementById('root')!).render(
251
- <React.StrictMode>
319
+ <HiyveProvider region="${DEFAULT_REGION}">
252
320
  <App />
253
- </React.StrictMode>,
321
+ </HiyveProvider>,
254
322
  );
255
323
  `;
256
324
  }
257
325
 
258
- function generateApp(template) {
259
- if (template.features.intelligence && template.features.collaboration) {
260
- // Full template use @hiyve/react-room PrebuiltRoom
261
- return `import React, { useState, useCallback } from 'react';
326
+ export function generateApp(template) {
327
+ if (usesPrebuiltRoom(template)) {
328
+ return `import { useState, useCallback } from 'react';
262
329
  import { HiyveRoom, PrebuiltRoom, PrebuiltLobby } from '@hiyve/react-room';
263
330
 
331
+ // Must match SERVER_REGION in .env — see .env.example.
332
+ const REGION = '${DEFAULT_REGION}';
333
+
334
+ /** Fetch a room token from server/index.ts (mountHiyveRoutes). */
335
+ async function fetchRoomToken(): Promise<string> {
336
+ const res = await fetch('/api/generate-room-token', { method: 'POST' });
337
+ const data = await res.json().catch(() => ({}));
338
+ if (!res.ok) throw new Error(data.message || 'Failed to generate room token');
339
+ return data.roomToken;
340
+ }
341
+
342
+ /** Fetch a cloud token for AI features from the same server. */
343
+ async function fetchCloudToken(): Promise<string> {
344
+ const res = await fetch('/api/generate-cloud-token', { method: 'POST' });
345
+ const data = await res.json().catch(() => ({}));
346
+ if (!res.ok) throw new Error(data.message || 'Failed to generate cloud token');
347
+ return data.cloudToken;
348
+ }
349
+
264
350
  export default function App() {
265
351
  const [roomToken, setRoomToken] = useState<string | null>(null);
266
- const [cloudToken, setCloudToken] = useState<string | null>(null);
267
352
  const [displayName, setDisplayName] = useState('');
268
353
 
269
354
  const handleJoin = useCallback(async (name: string) => {
270
355
  setDisplayName(name);
271
- const res = await fetch('/api/token', {
272
- method: 'POST',
273
- headers: { 'Content-Type': 'application/json' },
274
- body: JSON.stringify({ roomName: 'my-room', userId: name, displayName: name }),
275
- });
276
- const data = await res.json();
277
- setRoomToken(data.roomToken);
278
- if (data.cloudToken) setCloudToken(data.cloudToken);
356
+ setRoomToken(await fetchRoomToken());
279
357
  }, []);
280
358
 
281
- const handleLeave = useCallback(() => {
282
- setRoomToken(null);
283
- setCloudToken(null);
284
- }, []);
359
+ const handleLeave = useCallback(() => setRoomToken(null), []);
285
360
 
286
361
  if (!roomToken) {
287
362
  return <PrebuiltLobby onJoin={handleJoin} />;
288
363
  }
289
364
 
290
365
  return (
291
- <HiyveRoom roomToken={roomToken} cloudToken={cloudToken} intelligence>
366
+ <HiyveRoom roomToken={roomToken} region={REGION} generateToken={fetchCloudToken} userId={displayName} intelligence>
292
367
  <PrebuiltRoom userId={displayName} onLeave={handleLeave} />
293
368
  </HiyveRoom>
294
369
  );
@@ -296,67 +371,62 @@ export default function App() {
296
371
  `;
297
372
  }
298
373
 
299
- // Basic / AI template compose directly
300
- const imports = [`import { HiyveProvider, useRoom, useConnection } from '@hiyve/react';`];
301
- imports.push(`import { VideoGrid, ControlBar } from '@hiyve/react-ui';`);
302
-
303
- let providerOpen = ' <HiyveProvider generateRoomToken={generateRoomToken}>';
304
- let providerClose = ' </HiyveProvider>';
305
- let extraComponents = '';
306
-
307
- if (template.features.intelligence) {
308
- extraComponents += `\n {/* AI features available via @hiyve/react-intelligence hooks */}`;
374
+ // Basic / AI — the getting-started guide's App, verbatim. HiyveProvider
375
+ // (src/main.tsx) fetches the room token itself; JoinForm drives the flow.
376
+ const aiNote = template.features.intelligence
377
+ ? `\n// AI: add hooks from @hiyve/react-intelligence inside VideoRoom (HiyveProvider\n// already fetches the cloud token from /api/generate-cloud-token).\n`
378
+ : '';
379
+ return `import { useRoomFlow } from '@hiyve/react';
380
+ import { JoinForm, ConnectingScreen } from '@hiyve/react-ui';
381
+ import VideoRoom from './VideoRoom';
382
+ ${aiNote}
383
+ function App() {
384
+ const { screen } = useRoomFlow();
385
+
386
+ switch (screen) {
387
+ case 'connecting':
388
+ return <ConnectingScreen />;
389
+ case 'in-room':
390
+ return <VideoRoom />;
391
+ default:
392
+ return <JoinForm autoConnect devicePreviewMode="inline" />;
309
393
  }
310
-
311
- return `import React, { useState, useCallback } from 'react';
312
- ${imports.join('\n')}
313
-
314
- function Room({ onLeave }: { onLeave: () => void }) {
315
- return (
316
- <div style={{ display: 'flex', flexDirection: 'column', height: '100vh', background: '#121212' }}>
317
- <div style={{ flex: 1, overflow: 'hidden' }}>
318
- <VideoGrid localVideoElementId="local-video" />
319
- </div>${extraComponents}
320
- <ControlBar onLeave={onLeave} />
321
- </div>
322
- );
323
394
  }
324
395
 
325
- export default function App() {
326
- const [roomToken, setRoomToken] = useState<string | null>(null);
327
-
328
- const handleJoin = useCallback(async () => {
329
- const res = await fetch('/api/token', {
330
- method: 'POST',
331
- headers: { 'Content-Type': 'application/json' },
332
- body: JSON.stringify({ roomName: 'my-room', userId: 'user-' + Date.now() }),
333
- });
334
- const data = await res.json();
335
- setRoomToken(data.roomToken);
336
- }, []);
396
+ export default App;
397
+ `;
398
+ }
337
399
 
338
- const generateRoomToken = useCallback(() => Promise.resolve(roomToken!), [roomToken]);
400
+ export function generateVideoRoom() {
401
+ return `import { useState } from 'react';
402
+ import { useRoom, useConnection } from '@hiyve/react';
403
+ import { VideoGrid, ControlBar } from '@hiyve/react-ui';
339
404
 
340
- if (!roomToken) {
341
- return (
342
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100vh', background: '#121212' }}>
343
- <button onClick={handleJoin} style={{ padding: '12px 32px', fontSize: 18, borderRadius: 8, border: 'none', background: '#6c63ff', color: '#fff', cursor: 'pointer' }}>
344
- Join Room
345
- </button>
346
- </div>
347
- );
348
- }
405
+ function VideoRoom() {
406
+ const { room } = useRoom();
407
+ const { leaveRoom } = useConnection();
408
+ const [layout, setLayout] = useState('grid');
349
409
 
350
410
  return (
351
- ${providerOpen}
352
- <Room onLeave={() => setRoomToken(null)} />
353
- ${providerClose}
411
+ <div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
412
+ {room?.name && <h2 style={{ margin: '8px 16px' }}>{room.name}</h2>}
413
+ <VideoGrid localVideoElementId="local-video" layout={layout} showNames showLocalFlip />
414
+ <ControlBar
415
+ onLeave={leaveRoom}
416
+ showScreenShare
417
+ showLayoutSelector
418
+ layout={layout}
419
+ onLayoutChange={setLayout}
420
+ />
421
+ </div>
354
422
  );
355
423
  }
424
+
425
+ export default VideoRoom;
356
426
  `;
357
427
  }
358
428
 
359
- function generateIndexHtml(name) {
429
+ export function generateIndexHtml(name) {
360
430
  return `<!DOCTYPE html>
361
431
  <html lang="en">
362
432
  <head>
@@ -372,71 +442,25 @@ function generateIndexHtml(name) {
372
442
  `;
373
443
  }
374
444
 
375
- function generateServer(template) {
376
- const hasCloud = template.features.intelligence;
377
-
378
- return `import express from 'express';
379
- import 'dotenv/config';
445
+ export function generateServer() {
446
+ // The getting-started guide's server. mountHiyveRoutes exposes
447
+ // /generate-room-token, /generate-cloud-token, /create-join-token and
448
+ // /health under /api; loadHiyveConfig reads APIKEY, CLIENT_SECRET and
449
+ // SERVER_REGION from .env. Nothing here talks to signaling directly.
450
+ return `import 'dotenv/config';
451
+ import express from 'express';
452
+ import cors from 'cors';
453
+ import { mountHiyveRoutes, loadHiyveConfig } from '@hiyve/admin';
380
454
 
381
455
  const app = express();
456
+ app.use(cors({ origin: ['http://localhost:5173'] }));
382
457
  app.use(express.json());
383
458
 
384
- const API_KEY = process.env.HIYVE_API_KEY;
385
- const SIGNALING_URL = process.env.HIYVE_SIGNALING_URL || 'https://signal.hiyve.dev';
386
- ${hasCloud ? `const CLOUD_URL = process.env.HIYVE_CLOUD_URL || 'https://cloud.hiyve.io';\n` : ''}
387
- /**
388
- * Generate a room token for the client.
389
- * In production, add your own authentication before issuing tokens.
390
- */
391
- app.post('/api/token', async (req, res) => {
392
- const { roomName, userId, displayName } = req.body;
393
-
394
- try {
395
- const response = await fetch(\`\${SIGNALING_URL}/api/rooms/token\`, {
396
- method: 'POST',
397
- headers: {
398
- 'Content-Type': 'application/json',
399
- 'Authorization': \`Bearer \${API_KEY}\`,
400
- },
401
- body: JSON.stringify({ roomName, userId, displayName: displayName || userId }),
402
- });
403
-
404
- if (!response.ok) {
405
- throw new Error(\`Token request failed: \${response.status}\`);
406
- }
407
-
408
- const data = await response.json();
409
- ${hasCloud ? `
410
- // Also generate a cloud token for AI features
411
- let cloudToken: string | undefined;
412
- try {
413
- const cloudRes = await fetch(\`\${CLOUD_URL}/auth/tokens/cloud\`, {
414
- method: 'POST',
415
- headers: {
416
- 'Content-Type': 'application/json',
417
- 'x-api-key': API_KEY!,
418
- },
419
- body: JSON.stringify({ userId }),
420
- });
421
- if (cloudRes.ok) {
422
- const cloudData = await cloudRes.json();
423
- cloudToken = cloudData.token;
424
- }
425
- } catch {
426
- // Cloud token optional — continue without it
427
- }
428
-
429
- res.json({ roomToken: data.token, cloudToken });
430
- ` : ` res.json({ roomToken: data.token });
431
- `} } catch (err: any) {
432
- console.error('Token error:', err.message);
433
- res.status(500).json({ error: 'Failed to generate token' });
434
- }
435
- });
459
+ const apiRouter = express.Router();
460
+ mountHiyveRoutes(apiRouter, loadHiyveConfig());
461
+ app.use('/api', apiRouter);
436
462
 
437
- const PORT = process.env.PORT || 4000;
438
- app.listen(PORT, () => {
439
- console.log(\`Server running on http://localhost:\${PORT}\`);
440
- });
463
+ const PORT = Number(process.env.PORT) || ${SERVER_PORT};
464
+ app.listen(PORT, () => console.log(\`Server running on http://localhost:\${PORT}\`));
441
465
  `;
442
466
  }