gamedev 0.2.2 → 0.2.4
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/app-server/cliAuth.js +24 -224
- package/app-server/commands.js +7 -7
- package/bin/gamedev.mjs +11 -5
- package/build/index.js +15 -54
- package/build/index.js.map +2 -2
- package/build/public/admin.html +2 -2
- package/build/public/index.html +2 -2
- package/docs/App-server.md +3 -2
- package/package.json +1 -1
- package/src/server/admin.js +7 -3
- package/src/server/cliAuth.js +7 -46
- package/src/server/index.js +2 -6
package/app-server/cliAuth.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import crypto from 'crypto'
|
|
2
|
-
import http from 'http'
|
|
3
1
|
import { spawn } from 'child_process'
|
|
4
2
|
|
|
5
3
|
import { joinUrl, normalizeWorldAdminBaseUrl } from './helpers.js'
|
|
@@ -58,138 +56,6 @@ export async function fetchCliAuthStatus({ worldUrl, authToken }) {
|
|
|
58
56
|
return data
|
|
59
57
|
}
|
|
60
58
|
|
|
61
|
-
function createCallbackServer({ worldId, worldUrl, state, timeoutMs = 10 * 60 * 1000 } = {}) {
|
|
62
|
-
const expectedState = typeof state === 'string' && state.trim() ? state.trim() : crypto.randomUUID()
|
|
63
|
-
let settled = false
|
|
64
|
-
let timeoutId = null
|
|
65
|
-
let server = null
|
|
66
|
-
let startupResolve = null
|
|
67
|
-
let startupReject = null
|
|
68
|
-
let startupState = 'pending'
|
|
69
|
-
const startup = new Promise((resolve, reject) => {
|
|
70
|
-
startupResolve = resolve
|
|
71
|
-
startupReject = reject
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
const close = async () => {
|
|
75
|
-
if (!server) return
|
|
76
|
-
const target = server
|
|
77
|
-
server = null
|
|
78
|
-
await new Promise(resolve => target.close(() => resolve()))
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const result = new Promise((resolve, reject) => {
|
|
82
|
-
server = http.createServer(async (req, res) => {
|
|
83
|
-
res.setHeader('Access-Control-Allow-Origin', '*')
|
|
84
|
-
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS')
|
|
85
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
|
|
86
|
-
if (req.method === 'OPTIONS') {
|
|
87
|
-
res.statusCode = 204
|
|
88
|
-
res.end()
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (req.method !== 'POST' || req.url !== '/callback') {
|
|
93
|
-
res.statusCode = 404
|
|
94
|
-
res.end('Not found')
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const chunks = []
|
|
99
|
-
req.on('data', chunk => {
|
|
100
|
-
chunks.push(chunk)
|
|
101
|
-
})
|
|
102
|
-
req.on('error', async error => {
|
|
103
|
-
if (settled) return
|
|
104
|
-
settled = true
|
|
105
|
-
clearTimeout(timeoutId)
|
|
106
|
-
res.statusCode = 500
|
|
107
|
-
res.end(JSON.stringify({ error: 'callback_read_failed' }))
|
|
108
|
-
await close()
|
|
109
|
-
reject(error instanceof Error ? error : createError('callback_read_failed'))
|
|
110
|
-
})
|
|
111
|
-
req.on('end', async () => {
|
|
112
|
-
let payload
|
|
113
|
-
try {
|
|
114
|
-
payload = JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}')
|
|
115
|
-
} catch {
|
|
116
|
-
res.statusCode = 400
|
|
117
|
-
res.end(JSON.stringify({ error: 'invalid_payload' }))
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const receivedState = typeof payload?.state === 'string' ? payload.state.trim() : ''
|
|
122
|
-
const authToken = typeof payload?.authToken === 'string' ? payload.authToken.trim() : ''
|
|
123
|
-
const receivedWorldId = typeof payload?.worldId === 'string' ? payload.worldId.trim() : ''
|
|
124
|
-
const receivedWorldUrl = typeof payload?.worldUrl === 'string' ? payload.worldUrl.trim() : ''
|
|
125
|
-
|
|
126
|
-
if (!authToken || !receivedState || receivedState !== expectedState) {
|
|
127
|
-
res.statusCode = 400
|
|
128
|
-
res.end(JSON.stringify({ error: 'invalid_callback_state' }))
|
|
129
|
-
return
|
|
130
|
-
}
|
|
131
|
-
if (worldId && receivedWorldId && receivedWorldId !== worldId) {
|
|
132
|
-
res.statusCode = 409
|
|
133
|
-
res.end(JSON.stringify({ error: 'world_id_mismatch' }))
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
if (worldUrl && receivedWorldUrl && normalizeWorldAdminBaseUrl(receivedWorldUrl) !== normalizeWorldAdminBaseUrl(worldUrl)) {
|
|
137
|
-
res.statusCode = 409
|
|
138
|
-
res.end(JSON.stringify({ error: 'world_url_mismatch' }))
|
|
139
|
-
return
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
settled = true
|
|
143
|
-
clearTimeout(timeoutId)
|
|
144
|
-
res.statusCode = 200
|
|
145
|
-
res.setHeader('Content-Type', 'application/json')
|
|
146
|
-
res.end(JSON.stringify({ ok: true }))
|
|
147
|
-
await close()
|
|
148
|
-
resolve(payload)
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
server.listen(0, '127.0.0.1', () => {
|
|
153
|
-
if (startupState === 'pending') {
|
|
154
|
-
startupState = 'ready'
|
|
155
|
-
startupResolve()
|
|
156
|
-
}
|
|
157
|
-
timeoutId = setTimeout(async () => {
|
|
158
|
-
if (settled) return
|
|
159
|
-
settled = true
|
|
160
|
-
await close()
|
|
161
|
-
reject(createError('auth_timeout', 'Timed out waiting for browser authentication'))
|
|
162
|
-
}, timeoutMs)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
server.on('error', async error => {
|
|
166
|
-
if (startupState === 'pending') {
|
|
167
|
-
startupState = 'failed'
|
|
168
|
-
startupReject(error instanceof Error ? error : createError('callback_server_failed'))
|
|
169
|
-
}
|
|
170
|
-
if (settled) return
|
|
171
|
-
settled = true
|
|
172
|
-
clearTimeout(timeoutId)
|
|
173
|
-
await close()
|
|
174
|
-
reject(error instanceof Error ? error : createError('callback_server_failed'))
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
state: expectedState,
|
|
180
|
-
async getCallbackUrl() {
|
|
181
|
-
await startup
|
|
182
|
-
const address = server.address()
|
|
183
|
-
if (!address || typeof address === 'string') {
|
|
184
|
-
throw createError('callback_server_failed')
|
|
185
|
-
}
|
|
186
|
-
return `http://127.0.0.1:${address.port}/callback`
|
|
187
|
-
},
|
|
188
|
-
result,
|
|
189
|
-
close,
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
59
|
async function launchBrowser(url) {
|
|
194
60
|
const commands =
|
|
195
61
|
process.platform === 'darwin'
|
|
@@ -218,9 +84,7 @@ async function launchBrowser(url) {
|
|
|
218
84
|
export function buildCliAuthUrl({
|
|
219
85
|
worldUrl,
|
|
220
86
|
worldId,
|
|
221
|
-
callbackUrl,
|
|
222
87
|
sessionId,
|
|
223
|
-
state,
|
|
224
88
|
requiredCapability = 'builder',
|
|
225
89
|
} = {}) {
|
|
226
90
|
const normalizedWorldUrl = normalizeWorldAdminBaseUrl(worldUrl)
|
|
@@ -228,10 +92,8 @@ export function buildCliAuthUrl({
|
|
|
228
92
|
throw createError('invalid_world_url', 'Invalid world URL')
|
|
229
93
|
}
|
|
230
94
|
const url = new URL(joinUrl(normalizedWorldUrl, '/auth/cli'))
|
|
231
|
-
if (callbackUrl) url.searchParams.set('callback', callbackUrl)
|
|
232
95
|
if (sessionId) url.searchParams.set('session', sessionId)
|
|
233
96
|
if (worldId) url.searchParams.set('worldId', worldId)
|
|
234
|
-
if (state) url.searchParams.set('state', state)
|
|
235
97
|
url.searchParams.set('required', normalizeCapability(requiredCapability))
|
|
236
98
|
return url.toString()
|
|
237
99
|
}
|
|
@@ -254,12 +116,6 @@ async function createRemoteCliAuthSession({ worldUrl, worldId, requiredCapabilit
|
|
|
254
116
|
})
|
|
255
117
|
const data = await response.json().catch(() => null)
|
|
256
118
|
if (!response.ok) {
|
|
257
|
-
if (response.status === 404 || response.status === 405) {
|
|
258
|
-
throw createError('cli_auth_session_unsupported', 'World does not support server-mediated CLI auth sessions', {
|
|
259
|
-
status: response.status,
|
|
260
|
-
data,
|
|
261
|
-
})
|
|
262
|
-
}
|
|
263
119
|
throw createError(
|
|
264
120
|
data?.error || `session_create_failed:${response.status}`,
|
|
265
121
|
data?.message || 'Failed to create CLI auth session',
|
|
@@ -333,7 +189,7 @@ async function openCliAuthUrl(authUrl, { log = console } = {}) {
|
|
|
333
189
|
log?.log?.('Opening browser for world auth...')
|
|
334
190
|
}
|
|
335
191
|
|
|
336
|
-
async function
|
|
192
|
+
export async function runBrowserCliAuth({
|
|
337
193
|
rootDir = process.cwd(),
|
|
338
194
|
worldUrl,
|
|
339
195
|
worldId,
|
|
@@ -341,89 +197,33 @@ async function runLoopbackBrowserCliAuth({
|
|
|
341
197
|
timeoutMs,
|
|
342
198
|
log = console,
|
|
343
199
|
} = {}) {
|
|
344
|
-
const
|
|
200
|
+
const session = await createRemoteCliAuthSession({
|
|
201
|
+
worldUrl,
|
|
202
|
+
worldId,
|
|
203
|
+
requiredCapability,
|
|
204
|
+
})
|
|
205
|
+
const authUrl = buildCliAuthUrl({
|
|
206
|
+
worldUrl,
|
|
345
207
|
worldId,
|
|
208
|
+
sessionId: session.sessionId,
|
|
209
|
+
requiredCapability,
|
|
210
|
+
})
|
|
211
|
+
await openCliAuthUrl(authUrl, { log })
|
|
212
|
+
const payload = await waitForRemoteCliAuthSession({
|
|
346
213
|
worldUrl,
|
|
214
|
+
sessionId: session.sessionId,
|
|
347
215
|
timeoutMs,
|
|
348
216
|
})
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
await openCliAuthUrl(authUrl, { log })
|
|
360
|
-
|
|
361
|
-
const payload = await callback.result
|
|
362
|
-
const entry = writeProjectAuthEntry(rootDir, {
|
|
363
|
-
worldUrl: payload.worldUrl || worldUrl,
|
|
364
|
-
worldId: payload.worldId || worldId,
|
|
365
|
-
authToken: payload.authToken,
|
|
366
|
-
userId: payload?.user?.id || null,
|
|
367
|
-
userName: payload?.user?.name || null,
|
|
368
|
-
})
|
|
369
|
-
return {
|
|
370
|
-
entry,
|
|
371
|
-
capabilities: payload?.capabilities || null,
|
|
372
|
-
}
|
|
373
|
-
} finally {
|
|
374
|
-
await callback.close().catch(() => {})
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
export async function runBrowserCliAuth({
|
|
379
|
-
rootDir = process.cwd(),
|
|
380
|
-
worldUrl,
|
|
381
|
-
worldId,
|
|
382
|
-
requiredCapability = 'builder',
|
|
383
|
-
timeoutMs,
|
|
384
|
-
log = console,
|
|
385
|
-
} = {}) {
|
|
386
|
-
try {
|
|
387
|
-
const session = await createRemoteCliAuthSession({
|
|
388
|
-
worldUrl,
|
|
389
|
-
worldId,
|
|
390
|
-
requiredCapability,
|
|
391
|
-
})
|
|
392
|
-
const authUrl = buildCliAuthUrl({
|
|
393
|
-
worldUrl,
|
|
394
|
-
worldId,
|
|
395
|
-
sessionId: session.sessionId,
|
|
396
|
-
requiredCapability,
|
|
397
|
-
})
|
|
398
|
-
await openCliAuthUrl(authUrl, { log })
|
|
399
|
-
const payload = await waitForRemoteCliAuthSession({
|
|
400
|
-
worldUrl,
|
|
401
|
-
sessionId: session.sessionId,
|
|
402
|
-
timeoutMs,
|
|
403
|
-
})
|
|
404
|
-
const entry = writeProjectAuthEntry(rootDir, {
|
|
405
|
-
worldUrl: payload.worldUrl || worldUrl,
|
|
406
|
-
worldId: payload.worldId || worldId,
|
|
407
|
-
authToken: payload.authToken,
|
|
408
|
-
userId: payload?.user?.id || null,
|
|
409
|
-
userName: payload?.user?.name || null,
|
|
410
|
-
})
|
|
411
|
-
return {
|
|
412
|
-
entry,
|
|
413
|
-
capabilities: payload?.capabilities || null,
|
|
414
|
-
}
|
|
415
|
-
} catch (error) {
|
|
416
|
-
if (error?.code === 'cli_auth_session_unsupported') {
|
|
417
|
-
return runLoopbackBrowserCliAuth({
|
|
418
|
-
rootDir,
|
|
419
|
-
worldUrl,
|
|
420
|
-
worldId,
|
|
421
|
-
requiredCapability,
|
|
422
|
-
timeoutMs,
|
|
423
|
-
log,
|
|
424
|
-
})
|
|
425
|
-
}
|
|
426
|
-
throw error
|
|
217
|
+
const entry = writeProjectAuthEntry(rootDir, {
|
|
218
|
+
worldUrl: payload.worldUrl || worldUrl,
|
|
219
|
+
worldId: payload.worldId || worldId,
|
|
220
|
+
authToken: payload.authToken,
|
|
221
|
+
userId: payload?.user?.id || null,
|
|
222
|
+
userName: payload?.user?.name || null,
|
|
223
|
+
})
|
|
224
|
+
return {
|
|
225
|
+
entry,
|
|
226
|
+
capabilities: payload?.capabilities || null,
|
|
427
227
|
}
|
|
428
228
|
}
|
|
429
229
|
|
package/app-server/commands.js
CHANGED
|
@@ -260,14 +260,14 @@ export class HyperfyCLI {
|
|
|
260
260
|
return process.env.HYPERFY_TARGET_CONFIRM === 'true'
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
-
async _connectAdminClient() {
|
|
263
|
+
async _connectAdminClient({ requiredCapability = 'builder' } = {}) {
|
|
264
264
|
this._requireWorldUrl()
|
|
265
265
|
this._requireWorldId()
|
|
266
266
|
const auth = await ensureProjectAuth({
|
|
267
267
|
rootDir: this.rootDir,
|
|
268
268
|
worldUrl: this.worldUrl,
|
|
269
269
|
worldId: this.worldId,
|
|
270
|
-
requiredCapability
|
|
270
|
+
requiredCapability,
|
|
271
271
|
interactive: process.stdin.isTTY,
|
|
272
272
|
log: console,
|
|
273
273
|
})
|
|
@@ -411,7 +411,7 @@ export class HyperfyCLI {
|
|
|
411
411
|
|
|
412
412
|
console.log(`🚀 Deploying app: ${appName}`)
|
|
413
413
|
|
|
414
|
-
const server = await this._connectAdminClient()
|
|
414
|
+
const server = await this._connectAdminClient({ requiredCapability: 'deploy' })
|
|
415
415
|
try {
|
|
416
416
|
if (!options.dryRun && !options.yes && this._shouldConfirmDeployTarget()) {
|
|
417
417
|
const target = process.env.HYPERFY_TARGET ? ` "${process.env.HYPERFY_TARGET}"` : ''
|
|
@@ -452,7 +452,7 @@ export class HyperfyCLI {
|
|
|
452
452
|
|
|
453
453
|
async rollback(snapshotId) {
|
|
454
454
|
console.log(`⏪ Rolling back deploy snapshot...`)
|
|
455
|
-
const server = await this._connectAdminClient()
|
|
455
|
+
const server = await this._connectAdminClient({ requiredCapability: 'deploy' })
|
|
456
456
|
try {
|
|
457
457
|
const owner = `hyperfy-cli:${process.env.HYPERFY_TARGET || 'default'}:${process.pid}`
|
|
458
458
|
const lock = await server.client.acquireDeployLock({ owner })
|
|
@@ -486,7 +486,7 @@ export class HyperfyCLI {
|
|
|
486
486
|
|
|
487
487
|
async status() {
|
|
488
488
|
console.log(`📊 Admin Status`)
|
|
489
|
-
const server = await this._connectAdminClient()
|
|
489
|
+
const server = await this._connectAdminClient({ requiredCapability: 'builder' })
|
|
490
490
|
try {
|
|
491
491
|
const snapshot = await server.client.getSnapshot()
|
|
492
492
|
const blueprints = Array.isArray(snapshot?.blueprints) ? snapshot.blueprints.length : 0
|
|
@@ -572,7 +572,7 @@ export class HyperfyCLI {
|
|
|
572
572
|
return false
|
|
573
573
|
}
|
|
574
574
|
|
|
575
|
-
const server = await this._connectAdminClient()
|
|
575
|
+
const server = await this._connectAdminClient({ requiredCapability: 'deploy' })
|
|
576
576
|
try {
|
|
577
577
|
const result = await server.resolveSyncConflict(id, { use })
|
|
578
578
|
if (result?.alreadyResolved) {
|
|
@@ -592,7 +592,7 @@ export class HyperfyCLI {
|
|
|
592
592
|
}
|
|
593
593
|
|
|
594
594
|
async syncResolveInteractive() {
|
|
595
|
-
const server = await this._connectAdminClient()
|
|
595
|
+
const server = await this._connectAdminClient({ requiredCapability: 'deploy' })
|
|
596
596
|
try {
|
|
597
597
|
const summary = await server.promptAndResolveSyncConflicts()
|
|
598
598
|
if (!summary?.prompted && summary?.remaining > 0) {
|
package/bin/gamedev.mjs
CHANGED
|
@@ -486,7 +486,7 @@ async function startCommand(args = []) {
|
|
|
486
486
|
rootDir: projectDir,
|
|
487
487
|
worldUrl: env.WORLD_URL,
|
|
488
488
|
worldId: env.WORLD_ID,
|
|
489
|
-
requiredCapability: '
|
|
489
|
+
requiredCapability: 'deploy',
|
|
490
490
|
interactive: process.stdin.isTTY,
|
|
491
491
|
log: console,
|
|
492
492
|
})
|
|
@@ -584,7 +584,7 @@ async function appServerCommand(args = []) {
|
|
|
584
584
|
rootDir: projectDir,
|
|
585
585
|
worldUrl: env.WORLD_URL,
|
|
586
586
|
worldId: env.WORLD_ID,
|
|
587
|
-
requiredCapability: '
|
|
587
|
+
requiredCapability: 'deploy',
|
|
588
588
|
interactive: process.stdin.isTTY,
|
|
589
589
|
log: console,
|
|
590
590
|
})
|
|
@@ -848,12 +848,12 @@ async function syncCommand(args) {
|
|
|
848
848
|
return runSyncCommand({ command, args: commandArgs, rootDir: projectDir, helpPrefix: 'gamedev sync' })
|
|
849
849
|
}
|
|
850
850
|
|
|
851
|
-
async function connectAdminServer({ worldUrl, worldId, rootDir }) {
|
|
851
|
+
async function connectAdminServer({ worldUrl, worldId, rootDir, requiredCapability = 'builder' }) {
|
|
852
852
|
const auth = await ensureProjectAuth({
|
|
853
853
|
rootDir,
|
|
854
854
|
worldUrl,
|
|
855
855
|
worldId,
|
|
856
|
-
requiredCapability
|
|
856
|
+
requiredCapability,
|
|
857
857
|
interactive: process.stdin.isTTY,
|
|
858
858
|
log: console,
|
|
859
859
|
})
|
|
@@ -976,12 +976,18 @@ async function worldCommand(args) {
|
|
|
976
976
|
|
|
977
977
|
let server
|
|
978
978
|
try {
|
|
979
|
-
server = await connectAdminServer({ worldUrl, worldId, rootDir: projectDir })
|
|
980
979
|
if (action === 'export') {
|
|
980
|
+
server = await connectAdminServer({ worldUrl, worldId, rootDir: projectDir })
|
|
981
981
|
const includeBuiltScripts = actionArgs.includes('--include-built-scripts')
|
|
982
982
|
await server.exportWorldToDisk(undefined, { includeBuiltScripts })
|
|
983
983
|
console.log('✅ World export complete')
|
|
984
984
|
} else {
|
|
985
|
+
server = await connectAdminServer({
|
|
986
|
+
worldUrl,
|
|
987
|
+
worldId,
|
|
988
|
+
rootDir: projectDir,
|
|
989
|
+
requiredCapability: 'deploy',
|
|
990
|
+
})
|
|
985
991
|
await server.importWorldFromDisk()
|
|
986
992
|
console.log('✅ World import complete')
|
|
987
993
|
}
|
package/build/index.js
CHANGED
|
@@ -61788,6 +61788,9 @@ async function admin(fastify2, {
|
|
|
61788
61788
|
return { builder: true, deploy: true };
|
|
61789
61789
|
}
|
|
61790
61790
|
async function getCapabilitiesFromAuthToken(token) {
|
|
61791
|
+
if (allowsOpenAdminAccess(process.env)) {
|
|
61792
|
+
return { builder: true, deploy: true };
|
|
61793
|
+
}
|
|
61791
61794
|
if (!token || !db2) return { builder: false, deploy: false };
|
|
61792
61795
|
const worldId = world2?.network?.worldId || process.env.WORLD_ID;
|
|
61793
61796
|
const claims = await readJWT(token, { worldId });
|
|
@@ -62193,9 +62196,10 @@ async function admin(fastify2, {
|
|
|
62193
62196
|
ws2.close();
|
|
62194
62197
|
return;
|
|
62195
62198
|
}
|
|
62199
|
+
const openAdminAccess = allowsOpenAdminAccess(process.env);
|
|
62196
62200
|
const codeCapabilities = getCapabilitiesFromAdminCode(data?.code);
|
|
62197
|
-
let builderOk = codeCapabilities.builder;
|
|
62198
|
-
let deployOk = codeCapabilities.deploy;
|
|
62201
|
+
let builderOk = openAdminAccess || codeCapabilities.builder;
|
|
62202
|
+
let deployOk = openAdminAccess || codeCapabilities.deploy;
|
|
62199
62203
|
if (!builderOk || !deployOk) {
|
|
62200
62204
|
const payloadToken = typeof data?.authToken === "string" ? data.authToken.trim() : "";
|
|
62201
62205
|
const headerToken = getRuntimeAuthTokenFromRequest(req) || "";
|
|
@@ -63145,17 +63149,13 @@ async function createStandaloneGuestSession({
|
|
|
63145
63149
|
};
|
|
63146
63150
|
}
|
|
63147
63151
|
function buildCliAuthPage({
|
|
63148
|
-
callbackUrl,
|
|
63149
63152
|
sessionId,
|
|
63150
|
-
state,
|
|
63151
63153
|
worldId,
|
|
63152
63154
|
requiredCapability = "builder",
|
|
63153
63155
|
publicAuthUrl = null
|
|
63154
63156
|
} = {}) {
|
|
63155
63157
|
const config = JSON.stringify({
|
|
63156
|
-
callbackUrl: normalizeString2(callbackUrl),
|
|
63157
63158
|
sessionId: normalizeString2(sessionId),
|
|
63158
|
-
state: normalizeString2(state),
|
|
63159
63159
|
worldId: normalizeString2(worldId),
|
|
63160
63160
|
requiredCapability: normalizeString2(requiredCapability) || "builder",
|
|
63161
63161
|
publicAuthUrl: hasValue2(publicAuthUrl) ? publicAuthUrl.trim() : null
|
|
@@ -63544,18 +63544,6 @@ function buildCliAuthPage({
|
|
|
63544
63544
|
return !!capabilities?.builder
|
|
63545
63545
|
}
|
|
63546
63546
|
|
|
63547
|
-
function validateCallbackUrl(value) {
|
|
63548
|
-
try {
|
|
63549
|
-
const parsed = new URL(value)
|
|
63550
|
-
const hostname = parsed.hostname
|
|
63551
|
-
if (!hostname) return null
|
|
63552
|
-
if (hostname !== '127.0.0.1' && hostname !== 'localhost' && hostname !== '::1') return null
|
|
63553
|
-
return parsed.toString()
|
|
63554
|
-
} catch {
|
|
63555
|
-
return null
|
|
63556
|
-
}
|
|
63557
|
-
}
|
|
63558
|
-
|
|
63559
63547
|
async function fetchStatus(token) {
|
|
63560
63548
|
const response = await fetch(\`\${apiBaseUrl()}/auth/cli/status\`, {
|
|
63561
63549
|
headers: {
|
|
@@ -63613,47 +63601,24 @@ function buildCliAuthPage({
|
|
|
63613
63601
|
return token
|
|
63614
63602
|
}
|
|
63615
63603
|
|
|
63616
|
-
async function submitToken(token
|
|
63617
|
-
if (config.sessionId) {
|
|
63618
|
-
|
|
63619
|
-
method: 'POST',
|
|
63620
|
-
headers: {
|
|
63621
|
-
'content-type': 'application/json',
|
|
63622
|
-
accept: 'application/json',
|
|
63623
|
-
},
|
|
63624
|
-
body: JSON.stringify({
|
|
63625
|
-
worldUrl: worldRootUrl(),
|
|
63626
|
-
authToken: token,
|
|
63627
|
-
}),
|
|
63628
|
-
})
|
|
63629
|
-
if (!response.ok) {
|
|
63630
|
-
const payload = await response.json().catch(() => null)
|
|
63631
|
-
throw new Error(payload?.error || 'session_complete_failed')
|
|
63632
|
-
}
|
|
63633
|
-
return
|
|
63634
|
-
}
|
|
63635
|
-
|
|
63636
|
-
const callbackUrl = validateCallbackUrl(config.callbackUrl)
|
|
63637
|
-
if (!callbackUrl) {
|
|
63638
|
-
throw new Error('Invalid callback URL')
|
|
63604
|
+
async function submitToken(token) {
|
|
63605
|
+
if (!config.sessionId) {
|
|
63606
|
+
throw new Error('Invalid session id')
|
|
63639
63607
|
}
|
|
63640
|
-
const response = await fetch(
|
|
63608
|
+
const response = await fetch(\`\${apiBaseUrl()}/auth/cli/session/\${encodeURIComponent(config.sessionId)}\`, {
|
|
63641
63609
|
method: 'POST',
|
|
63642
63610
|
headers: {
|
|
63643
63611
|
'content-type': 'application/json',
|
|
63612
|
+
accept: 'application/json',
|
|
63644
63613
|
},
|
|
63645
63614
|
body: JSON.stringify({
|
|
63646
|
-
state: config.state,
|
|
63647
|
-
worldId: status?.worldId || config.worldId || '',
|
|
63648
63615
|
worldUrl: worldRootUrl(),
|
|
63649
63616
|
authToken: token,
|
|
63650
|
-
user: status?.user || null,
|
|
63651
|
-
capabilities: status?.capabilities || null,
|
|
63652
63617
|
}),
|
|
63653
63618
|
})
|
|
63654
63619
|
if (!response.ok) {
|
|
63655
63620
|
const payload = await response.json().catch(() => null)
|
|
63656
|
-
throw new Error(payload?.error || '
|
|
63621
|
+
throw new Error(payload?.error || 'session_complete_failed')
|
|
63657
63622
|
}
|
|
63658
63623
|
}
|
|
63659
63624
|
|
|
@@ -63695,7 +63660,7 @@ function buildCliAuthPage({
|
|
|
63695
63660
|
'success'
|
|
63696
63661
|
)
|
|
63697
63662
|
setHint('Permission confirmed. The CLI is storing this world token locally.', 'success')
|
|
63698
|
-
await submitToken(token
|
|
63663
|
+
await submitToken(token)
|
|
63699
63664
|
clearInterval(polling)
|
|
63700
63665
|
setTimeout(() => {
|
|
63701
63666
|
window.close()
|
|
@@ -65234,21 +65199,17 @@ async function handleCliAuthPage(req, reply) {
|
|
|
65234
65199
|
if (!isRuntimeReady(runtimeState)) {
|
|
65235
65200
|
return sendRuntimeNotReady2(reply, runtimeState, { html: true });
|
|
65236
65201
|
}
|
|
65237
|
-
const callbackUrl = typeof req.query?.callback === "string" ? req.query.callback.trim() : "";
|
|
65238
65202
|
const sessionId = typeof req.query?.session === "string" ? req.query.session.trim() : "";
|
|
65239
|
-
const state = typeof req.query?.state === "string" ? req.query.state.trim() : "";
|
|
65240
65203
|
const worldId = typeof req.query?.worldId === "string" ? req.query.worldId.trim() : resolveBoundWorldId() || "";
|
|
65241
65204
|
const requiredCapability = typeof req.query?.required === "string" ? req.query.required.trim() : "builder";
|
|
65242
|
-
if (!sessionId
|
|
65205
|
+
if (!sessionId) {
|
|
65243
65206
|
return reply.code(400).type("text/html").send(
|
|
65244
|
-
"<!doctype html><html><body>Missing session
|
|
65207
|
+
"<!doctype html><html><body>Missing session.</body></html>"
|
|
65245
65208
|
);
|
|
65246
65209
|
}
|
|
65247
65210
|
reply.type("text/html").send(
|
|
65248
65211
|
buildCliAuthPage({
|
|
65249
|
-
callbackUrl,
|
|
65250
65212
|
sessionId,
|
|
65251
|
-
state,
|
|
65252
65213
|
worldId,
|
|
65253
65214
|
requiredCapability,
|
|
65254
65215
|
publicAuthUrl: process.env.PUBLIC_AUTH_URL || null
|