Wait for workspaceRegistry before provisioning user workspaces.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-08 15:03:06 +08:00
parent e9040224fb
commit f98d283118
3 changed files with 72 additions and 8 deletions

View file

@ -699,13 +699,50 @@ function installSettingsSection(ctx, entry, hooks) {
let _workspaceRegistry = null
let _workspaceRegistryWaiters = []
function notifyWorkspaceRegistryReady(registry) {
_workspaceRegistry = registry || null
const waiters = _workspaceRegistryWaiters.splice(0)
for (const w of waiters) {
try { clearTimeout(w.timer) } catch { /* ignore */ }
try { w.resolve(_workspaceRegistry) } catch { /* ignore */ }
}
}
/** Wait until workspaceRegistry inject fires (or timeout). */
function whenWorkspaceRegistry(timeoutMs = 30000) {
if (_workspaceRegistry) return Promise.resolve(_workspaceRegistry)
return new Promise((resolve) => {
const entry = {
resolve,
timer: setTimeout(() => {
const i = _workspaceRegistryWaiters.indexOf(entry)
if (i >= 0) _workspaceRegistryWaiters.splice(i, 1)
resolve(_workspaceRegistry || null)
}, timeoutMs),
}
_workspaceRegistryWaiters.push(entry)
})
}
async function ensureUserWorkspace(empNo) {
if (!_userWorkspaces) return null
try {
let registry = _workspaceRegistry
if (!registry) {
registry = await whenWorkspaceRegistry(30000)
}
const root = _currentConfig?.workspaceRoot
if (!registry) {
console.warn(
'[uds-auth] workspaceRegistry still unavailable after wait; mkdir only under',
root || '(default $DSH_HOME/user-workspaces)',
)
}
return await _userWorkspaces.ensureUserWorkspace(
{ workspaceRegistryHandle: _workspaceRegistry },
_currentConfig?.workspaceRoot,
{ workspaceRegistryHandle: registry },
root,
empNo,
)
} catch (err) {
@ -714,6 +751,21 @@ async function ensureUserWorkspace(empNo) {
}
}
async function rebindPendingUserWorkspaces() {
if (!_userWorkspaces || !_workspaceRegistry) return
try {
const users = _userWorkspaces._map
if (!users || typeof users.keys !== 'function') return
for (const empNo of users.keys()) {
const row = users.get(empNo)
if (row && row.workspaceId) continue
try { await ensureUserWorkspace(empNo) } catch { /* ignore */ }
}
} catch (err) {
console.warn('[uds-auth] rebindPendingUserWorkspaces failed:', err.message)
}
}
export async function apply(ctx, config = {}) {
let source = () => mergeConfig(config)
_currentConfig = source()
@ -809,8 +861,9 @@ async function initServices(ctx, config) {
})
ctx.inject(['workspaceRegistry'], (wctx) => {
_workspaceRegistry = wctx.workspaceRegistry
notifyWorkspaceRegistryReady(wctx.workspaceRegistry)
ctx.logger?.info?.('[uds-auth] workspaceRegistry ready')
void rebindPendingUserWorkspaces()
})
installDshAcl(ctx, {

View file

@ -2,7 +2,7 @@
* Per-user workspace provisioning under workspaceRoot/<empNo>.
*/
import { mkdir } from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { join, resolve, sep } from 'node:path'
import { homedir } from 'node:os'
import { readFile, writeFile } from 'node:fs/promises'
import { dirname } from 'node:path'
@ -100,16 +100,27 @@ export class UserWorkspaceStore {
if (!registry && ctx && typeof ctx.get === 'function') {
try { registry = ctx.get('workspaceRegistry') } catch { registry = undefined }
}
const cached = this.get(empNo)
if (registry && cached?.workspaceId && typeof registry.get === 'function') {
try {
const existing = registry.get(cached.workspaceId)
if (existing) {
return { path: cached.path || userPath, workspaceId: String(cached.workspaceId), workspace: existing }
}
} catch { /* fall through and recreate */ }
}
if (!registry || typeof registry.create !== 'function') {
console.warn('[uds-auth] workspaceRegistry unavailable; mkdir only:', userPath)
this.set(empNo, { path: userPath, workspaceId: null })
return { path: userPath, workspaceId: null, workspace: null }
this.set(empNo, { path: userPath, workspaceId: cached?.workspaceId || null })
return { path: userPath, workspaceId: cached?.workspaceId || null, workspace: null }
}
const prev = getUserContext()
const workspace = await withUserContext(
{ ...(prev || {}), empNo, _internalProvision: true },
() => registry.create(userPath),
() => registry.create(userPath, String(empNo)),
)
const workspaceId = workspace?.id != null ? String(workspace.id) : null
this.set(empNo, { path: userPath, workspaceId })

View file

@ -1,6 +1,6 @@
{
"name": "uds-auth",
"version": "0.2.1",
"version": "0.2.2",
"description": "UDS authentication plugin for DeepSeek Harness with multi-tenant session isolation",
"type": "module",
"main": "lib/index.js",