mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-12 06:50:48 +08:00
Emergency (fallback / sealed-box) admins lost the workspace sidebar and saw "sign-in failed / add API key" on DSH Desktop because the remote.mux opens anonymously at boot and every early call was rejected terminally. - gateway: anonymous workspace/follow reaches dsh-acl's empty baseline; anonymous streams are parked until the carrier aborts instead of failing; anonymous workspace/initializeDefault answers "nothing created" - request-auth: a dead UDS_SESSION cookie no longer shadows a valid WS ticket or bridge (sync + async variants used by all callers) - dsh-acl: /api/* waits (bounded 15s) for the gateway ACL during Host startup instead of 403, so the Desktop welcome read no longer fails - desktop-bootstrap: key/account projection is best-effort per reference - client: emit connection/reset after login so boot-time caches (settings describe mirror -> Settings > Models) re-read under the real principal - sessions: absolute lifetime fixed at 7 days (was min(ttl, 7d) = 30 min); bridge expiry slides with its session; MemoryStore persisted to sessions.json (bearer hash only, userData AES-256-GCM sealed) - README: session lifetime/persistence and DSH compatibility notes Also includes previously uncommitted uds-auth 0.3.x work in this tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.7 KiB
JavaScript
60 lines
2.7 KiB
JavaScript
/** Host shellEnv adapter. Values are rebuilt after ordinary environment entries. */
|
|
export function createTaskEnvironment({ getOwner, rolesStore, capabilities, getWebServer }) {
|
|
const issued = new Map()
|
|
const release = execution => {
|
|
const key = execution?.token || execution
|
|
const row = issued.get(key)
|
|
if (!row) return
|
|
capabilities.revoke(row.id)
|
|
row.signal?.removeEventListener('abort', row.abort)
|
|
clearTimeout(row.timer)
|
|
row.off?.()
|
|
issued.delete(key)
|
|
}
|
|
const contributor = {
|
|
name: 'uds-auth',
|
|
variables: {
|
|
DSH_UDS_TASK_CAPABILITY: { description: 'Short-lived session-bound UDS outbound capability; never print or persist.' },
|
|
DSH_UDS_AUTH_BASE: { description: 'Owned loopback uds-auth endpoint of this Host.' },
|
|
},
|
|
resolve(execution) {
|
|
release(execution)
|
|
const empty = { DSH_UDS_TASK_CAPABILITY: '', DSH_UDS_AUTH_BASE: '' }
|
|
const sid = execution?.agent?.session?.header?.id
|
|
const owner = sid ? getOwner(String(sid)) : null
|
|
const server = getWebServer()
|
|
const port = Number(server?.port)
|
|
if (!owner || rolesStore.isDisabled(owner) || !Number.isInteger(port) || port <= 0 || port > 65535
|
|
|| execution?.signal?.aborted) return empty
|
|
const cap = capabilities.mint({
|
|
empNo: owner, dshSessionId: String(sid), audience: 'uds-auth-agent',
|
|
scopes: ['outbound'], ttlSeconds: 600, mintedBy: 'host:shellEnv',
|
|
})
|
|
const row = { id: cap.id, sessionId: String(sid), signal: execution.signal, abort: () => release(execution) }
|
|
issued.set(execution.token || execution, row)
|
|
row.signal?.addEventListener('abort', row.abort, { once: true })
|
|
row.timer = setTimeout(() => release(execution), cap.expiresAt - Date.now())
|
|
row.timer.unref?.()
|
|
return { DSH_UDS_TASK_CAPABILITY: cap.token, DSH_UDS_AUTH_BASE: `http://127.0.0.1:${port}/uds-auth` }
|
|
},
|
|
}
|
|
return { contributor, release, finish(execution, result, jobs) {
|
|
const row = issued.get(execution?.token || execution)
|
|
const value = result?.value
|
|
if (row && !result?.isError && ['background', 'promoted'].includes(value?.kind)
|
|
&& typeof value.jobId === 'string' && jobs?.events?.subscribe) {
|
|
const job = jobs.list(row.sessionId).find(job => job.id === value.jobId)
|
|
if (job && ['running', 'stopping'].includes(job.status)) {
|
|
row.signal?.removeEventListener('abort', row.abort)
|
|
row.signal = null
|
|
row.off = jobs.events.subscribe({ owner: row.sessionId }, event => {
|
|
if (event.job?.id === value.jobId && ['settled', 'stopping', 'removed'].includes(event.type)) release(execution)
|
|
})
|
|
return
|
|
}
|
|
}
|
|
release(execution)
|
|
}, dispose() {
|
|
for (const key of [...issued.keys()]) release({ token: key })
|
|
} }
|
|
}
|