diff --git a/uds-auth/lib/index.js b/uds-auth/lib/index.js index 65e6dd73..839e6e6d 100644 --- a/uds-auth/lib/index.js +++ b/uds-auth/lib/index.js @@ -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, { diff --git a/uds-auth/lib/workspace-provision.js b/uds-auth/lib/workspace-provision.js index d911ab87..0381710c 100644 --- a/uds-auth/lib/workspace-provision.js +++ b/uds-auth/lib/workspace-provision.js @@ -2,7 +2,7 @@ * Per-user workspace provisioning under workspaceRoot/. */ 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 }) diff --git a/uds-auth/package.json b/uds-auth/package.json index 6e29f2bb..2f2d99c9 100644 --- a/uds-auth/package.json +++ b/uds-auth/package.json @@ -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",