mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-17 07:23:30 +08:00
Wait for workspaceRegistry before provisioning user workspaces.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
e9040224fb
commit
f98d283118
3 changed files with 72 additions and 8 deletions
|
|
@ -699,13 +699,50 @@ function installSettingsSection(ctx, entry, hooks) {
|
||||||
|
|
||||||
|
|
||||||
let _workspaceRegistry = null
|
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) {
|
async function ensureUserWorkspace(empNo) {
|
||||||
if (!_userWorkspaces) return null
|
if (!_userWorkspaces) return null
|
||||||
try {
|
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(
|
return await _userWorkspaces.ensureUserWorkspace(
|
||||||
{ workspaceRegistryHandle: _workspaceRegistry },
|
{ workspaceRegistryHandle: registry },
|
||||||
_currentConfig?.workspaceRoot,
|
root,
|
||||||
empNo,
|
empNo,
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} 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 = {}) {
|
export async function apply(ctx, config = {}) {
|
||||||
let source = () => mergeConfig(config)
|
let source = () => mergeConfig(config)
|
||||||
_currentConfig = source()
|
_currentConfig = source()
|
||||||
|
|
@ -809,8 +861,9 @@ async function initServices(ctx, config) {
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.inject(['workspaceRegistry'], (wctx) => {
|
ctx.inject(['workspaceRegistry'], (wctx) => {
|
||||||
_workspaceRegistry = wctx.workspaceRegistry
|
notifyWorkspaceRegistryReady(wctx.workspaceRegistry)
|
||||||
ctx.logger?.info?.('[uds-auth] workspaceRegistry ready')
|
ctx.logger?.info?.('[uds-auth] workspaceRegistry ready')
|
||||||
|
void rebindPendingUserWorkspaces()
|
||||||
})
|
})
|
||||||
|
|
||||||
installDshAcl(ctx, {
|
installDshAcl(ctx, {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
* Per-user workspace provisioning under workspaceRoot/<empNo>.
|
* Per-user workspace provisioning under workspaceRoot/<empNo>.
|
||||||
*/
|
*/
|
||||||
import { mkdir } from 'node:fs/promises'
|
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 { homedir } from 'node:os'
|
||||||
import { readFile, writeFile } from 'node:fs/promises'
|
import { readFile, writeFile } from 'node:fs/promises'
|
||||||
import { dirname } from 'node:path'
|
import { dirname } from 'node:path'
|
||||||
|
|
@ -100,16 +100,27 @@ export class UserWorkspaceStore {
|
||||||
if (!registry && ctx && typeof ctx.get === 'function') {
|
if (!registry && ctx && typeof ctx.get === 'function') {
|
||||||
try { registry = ctx.get('workspaceRegistry') } catch { registry = undefined }
|
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') {
|
if (!registry || typeof registry.create !== 'function') {
|
||||||
console.warn('[uds-auth] workspaceRegistry unavailable; mkdir only:', userPath)
|
console.warn('[uds-auth] workspaceRegistry unavailable; mkdir only:', userPath)
|
||||||
this.set(empNo, { path: userPath, workspaceId: null })
|
this.set(empNo, { path: userPath, workspaceId: cached?.workspaceId || null })
|
||||||
return { path: userPath, workspaceId: null, workspace: null }
|
return { path: userPath, workspaceId: cached?.workspaceId || null, workspace: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
const prev = getUserContext()
|
const prev = getUserContext()
|
||||||
const workspace = await withUserContext(
|
const workspace = await withUserContext(
|
||||||
{ ...(prev || {}), empNo, _internalProvision: true },
|
{ ...(prev || {}), empNo, _internalProvision: true },
|
||||||
() => registry.create(userPath),
|
() => registry.create(userPath, String(empNo)),
|
||||||
)
|
)
|
||||||
const workspaceId = workspace?.id != null ? String(workspace.id) : null
|
const workspaceId = workspace?.id != null ? String(workspace.id) : null
|
||||||
this.set(empNo, { path: userPath, workspaceId })
|
this.set(empNo, { path: userPath, workspaceId })
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "uds-auth",
|
"name": "uds-auth",
|
||||||
"version": "0.2.1",
|
"version": "0.2.2",
|
||||||
"description": "UDS authentication plugin for DeepSeek Harness with multi-tenant session isolation",
|
"description": "UDS authentication plugin for DeepSeek Harness with multi-tenant session isolation",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue