mirror of
https://github.com/hansjone/dsh-ops-cron.git
synced 2026-10-09 00:43:22 +08:00
Keep foreign cron cwd for super_admin via live role resolve and session default.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
9b8f937a65
commit
7a2a673ac2
9 changed files with 189 additions and 18 deletions
|
|
@ -978,12 +978,16 @@ body>.dsh-ct-main{position:fixed;top:0;right:0;bottom:0;left:var(--dsh-ct-sideba
|
||||||
return String(row?.cwd || '').trim()
|
return String(row?.cwd || '').trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Prefer provisioned viewer workspace over the currently open project. */
|
/** Prefer open session cwd for elevated viewers; else provisioned path. */
|
||||||
function preferredNewJobCwd(viewer, sessionCwd) {
|
function preferredNewJobCwd(viewer, sessionCwd) {
|
||||||
|
const session = String(sessionCwd || '').trim()
|
||||||
const provisioned = String(viewer?.workspacePath || '').trim()
|
const provisioned = String(viewer?.workspacePath || '').trim()
|
||||||
|
if (viewer?.canViewAll && session) return session
|
||||||
|
const role = String(viewer?.role || '').trim()
|
||||||
|
if ((role === 'super_admin' || role === 'fallback_admin') && session) return session
|
||||||
if (viewer?.mode === 'multi' && provisioned) return provisioned
|
if (viewer?.mode === 'multi' && provisioned) return provisioned
|
||||||
if (provisioned) return provisioned
|
if (provisioned) return provisioned
|
||||||
return String(sessionCwd || '').trim()
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
function cwdSelectValue(cwd, workspaces) {
|
function cwdSelectValue(cwd, workspaces) {
|
||||||
|
|
|
||||||
84
lib/host.js
84
lib/host.js
|
|
@ -30,6 +30,7 @@ import {
|
||||||
claimUnassignedForViewer,
|
claimUnassignedForViewer,
|
||||||
filterJobsForIdentity,
|
filterJobsForIdentity,
|
||||||
filterRunsForJobs,
|
filterRunsForJobs,
|
||||||
|
isElevatedCronRole,
|
||||||
isMultiUserIdentity,
|
isMultiUserIdentity,
|
||||||
localIdentity,
|
localIdentity,
|
||||||
migrateJobOwners,
|
migrateJobOwners,
|
||||||
|
|
@ -175,6 +176,67 @@ async function requireIdentity(request, write, getUdsAuth) {
|
||||||
return { ...identity, mode: 'multi', lang: identity.lang || locale }
|
return { ...identity, mode: 'multi', lang: identity.lang || locale }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this identity / job owner may keep an explicit cwd outside the
|
||||||
|
* provisioned tree. Re-resolves via uds-auth so stale tool identities (role
|
||||||
|
* missing permissions, or Host with an older caller path) still work.
|
||||||
|
*/
|
||||||
|
function allowsForeignJobCwd(identity, getUdsAuth, ownerEmpNo) {
|
||||||
|
if (canViewAllJobs(identity)) return true
|
||||||
|
const uds = typeof getUdsAuth === 'function' ? getUdsAuth() : null
|
||||||
|
const empNo = String(ownerEmpNo || identity?.empNo || '').trim()
|
||||||
|
if (!empNo || empNo.startsWith('__') || !uds) return false
|
||||||
|
if (typeof uds.resolveIdentityForEmpNo === 'function') {
|
||||||
|
const live = uds.resolveIdentityForEmpNo(empNo)
|
||||||
|
if (live && canViewAllJobs(live)) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Merge live role/permissions onto a caller identity when uds-auth can look them up. */
|
||||||
|
function enrichIdentity(identity, getUdsAuth) {
|
||||||
|
if (!isMultiUserIdentity(identity)) return identity
|
||||||
|
const uds = typeof getUdsAuth === 'function' ? getUdsAuth() : null
|
||||||
|
if (typeof uds?.resolveIdentityForEmpNo === 'function') {
|
||||||
|
const live = uds.resolveIdentityForEmpNo(identity.empNo)
|
||||||
|
if (live?.empNo) {
|
||||||
|
const canView = !!(
|
||||||
|
live.permissions?.canViewAllSessions
|
||||||
|
|| identity.permissions?.canViewAllSessions
|
||||||
|
|| isElevatedCronRole(live.role)
|
||||||
|
|| isElevatedCronRole(identity.role)
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...identity,
|
||||||
|
role: live.role || identity.role,
|
||||||
|
displayName: live.displayName || identity.displayName || identity.empNo,
|
||||||
|
permissions: {
|
||||||
|
...(identity.permissions || {}),
|
||||||
|
...(live.permissions || {}),
|
||||||
|
canViewAllSessions: canView,
|
||||||
|
canCreateWorkspace: !!(
|
||||||
|
live.permissions?.canCreateWorkspace
|
||||||
|
|| identity.permissions?.canCreateWorkspace
|
||||||
|
|| canView
|
||||||
|
),
|
||||||
|
},
|
||||||
|
workspacePath: live.workspacePath || identity.workspacePath || null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isElevatedCronRole(identity.role)) {
|
||||||
|
return {
|
||||||
|
...identity,
|
||||||
|
permissions: {
|
||||||
|
...(identity.permissions || {}),
|
||||||
|
canViewAllSessions: true,
|
||||||
|
canCreateWorkspace: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return identity
|
||||||
|
}
|
||||||
|
|
||||||
function sanitizeJobCwd(cwd, identity, getUdsAuth, { forOwnerEmpNo } = {}) {
|
function sanitizeJobCwd(cwd, identity, getUdsAuth, { forOwnerEmpNo } = {}) {
|
||||||
const raw = typeof cwd === 'string' ? cwd.trim() : ''
|
const raw = typeof cwd === 'string' ? cwd.trim() : ''
|
||||||
const uds = typeof getUdsAuth === 'function' ? getUdsAuth() : null
|
const uds = typeof getUdsAuth === 'function' ? getUdsAuth() : null
|
||||||
|
|
@ -183,7 +245,7 @@ function sanitizeJobCwd(cwd, identity, getUdsAuth, { forOwnerEmpNo } = {}) {
|
||||||
? uds.getProvisionedWorkspacePath(owner)
|
? uds.getProvisionedWorkspacePath(owner)
|
||||||
: null
|
: null
|
||||||
if (!raw) return ownerPath || ''
|
if (!raw) return ownerPath || ''
|
||||||
if (canViewAllJobs(identity)) return raw
|
if (allowsForeignJobCwd(identity, getUdsAuth, owner)) return raw
|
||||||
// Without uds-auth, keep the caller cwd (tools may stamp owner from path only).
|
// Without uds-auth, keep the caller cwd (tools may stamp owner from path only).
|
||||||
if (!uds) return raw
|
if (!uds) return raw
|
||||||
if (owner && uds?.isUserPath?.(owner, raw)) return raw
|
if (owner && uds?.isUserPath?.(owner, raw)) return raw
|
||||||
|
|
@ -350,9 +412,12 @@ export function createHostService(options = {}) {
|
||||||
safeInput.ownerDisplayName = ''
|
safeInput.ownerDisplayName = ''
|
||||||
// Keep cwd as the bot workspace path; do not sanitize via empNo.
|
// Keep cwd as the bot workspace path; do not sanitize via empNo.
|
||||||
} else {
|
} else {
|
||||||
const ownerIdentity = isMultiUserIdentity(identity)
|
const ownerIdentity = enrichIdentity(
|
||||||
? identity
|
isMultiUserIdentity(identity)
|
||||||
: inferIdentityFromJobInput(safeInput, getUdsAuth)
|
? identity
|
||||||
|
: inferIdentityFromJobInput(safeInput, getUdsAuth),
|
||||||
|
getUdsAuth,
|
||||||
|
)
|
||||||
if (isMultiUserIdentity(ownerIdentity)) {
|
if (isMultiUserIdentity(ownerIdentity)) {
|
||||||
const delivery = normalizeDelivery(safeInput.delivery || { kind: 'dsh' })
|
const delivery = normalizeDelivery(safeInput.delivery || { kind: 'dsh' })
|
||||||
assertDeliveryAllowedForIdentity(delivery, ownerIdentity)
|
assertDeliveryAllowedForIdentity(delivery, ownerIdentity)
|
||||||
|
|
@ -412,18 +477,19 @@ export function createHostService(options = {}) {
|
||||||
ownerDisplayName: job.ownerDisplayName || '',
|
ownerDisplayName: job.ownerDisplayName || '',
|
||||||
}
|
}
|
||||||
if (patch._identity) {
|
if (patch._identity) {
|
||||||
|
const enriched = enrichIdentity(patch._identity, getUdsAuth)
|
||||||
if (patch.delivery !== undefined) {
|
if (patch.delivery !== undefined) {
|
||||||
nextInput.delivery = mergeDeliveryMention(job.delivery, patch.delivery)
|
nextInput.delivery = mergeDeliveryMention(job.delivery, patch.delivery)
|
||||||
assertDeliveryAllowedForIdentity(nextInput.delivery, patch._identity)
|
assertDeliveryAllowedForIdentity(nextInput.delivery, enriched)
|
||||||
}
|
}
|
||||||
nextInput.cwd = sanitizeJobCwd(
|
nextInput.cwd = sanitizeJobCwd(
|
||||||
nextInput.cwd,
|
nextInput.cwd,
|
||||||
patch._identity,
|
enriched,
|
||||||
getUdsAuth,
|
getUdsAuth,
|
||||||
{ forOwnerEmpNo: nextInput.ownerEmpNo },
|
{ forOwnerEmpNo: nextInput.ownerEmpNo },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (patch.ownerEmpNo !== undefined && canViewAllJobs(patch._identity)) {
|
if (patch.ownerEmpNo !== undefined && canViewAllJobs(enrichIdentity(patch._identity, getUdsAuth))) {
|
||||||
nextInput.ownerEmpNo = normalizeOwnerEmpNo(patch.ownerEmpNo)
|
nextInput.ownerEmpNo = normalizeOwnerEmpNo(patch.ownerEmpNo)
|
||||||
if (patch.ownerDisplayName !== undefined) {
|
if (patch.ownerDisplayName !== undefined) {
|
||||||
nextInput.ownerDisplayName = String(patch.ownerDisplayName || '').trim().slice(0, 80)
|
nextInput.ownerDisplayName = String(patch.ownerDisplayName || '').trim().slice(0, 80)
|
||||||
|
|
@ -1004,16 +1070,18 @@ function sessionCwdOf(ctx, sessionId) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True when the job owner may keep an explicit cwd outside their provisioned tree
|
* True when the job owner may keep an explicit cwd outside their provisioned tree
|
||||||
* (super_admin / fallback_admin via canViewAllSessions or canCreateWorkspace).
|
* (super_admin / fallback_admin via canViewAllSessions, canCreateWorkspace, or role).
|
||||||
*/
|
*/
|
||||||
export function ownerAllowsForeignCwd(ownerEmpNo, uds) {
|
export function ownerAllowsForeignCwd(ownerEmpNo, uds) {
|
||||||
const empNo = String(ownerEmpNo || '').trim()
|
const empNo = String(ownerEmpNo || '').trim()
|
||||||
if (!empNo || empNo.startsWith('__') || !uds) return false
|
if (!empNo || empNo.startsWith('__') || !uds) return false
|
||||||
if (typeof uds.resolveIdentityForEmpNo === 'function') {
|
if (typeof uds.resolveIdentityForEmpNo === 'function') {
|
||||||
const identity = uds.resolveIdentityForEmpNo(empNo)
|
const identity = uds.resolveIdentityForEmpNo(empNo)
|
||||||
|
if (canViewAllJobs(identity)) return true
|
||||||
return !!(
|
return !!(
|
||||||
identity?.permissions?.canViewAllSessions
|
identity?.permissions?.canViewAllSessions
|
||||||
|| identity?.permissions?.canCreateWorkspace
|
|| identity?.permissions?.canCreateWorkspace
|
||||||
|
|| isElevatedCronRole(identity?.role)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
1
lib/index.d.ts
vendored
1
lib/index.d.ts
vendored
|
|
@ -24,6 +24,7 @@ export function localIdentity(overrides?: object): object
|
||||||
export function claimUnassignedForViewer(state: object, identity: object, deps?: object): { state: object, changed: boolean }
|
export function claimUnassignedForViewer(state: object, identity: object, deps?: object): { state: object, changed: boolean }
|
||||||
export function jobVisibleToIdentity(job: object, identity: object): boolean
|
export function jobVisibleToIdentity(job: object, identity: object): boolean
|
||||||
export function canViewAllJobs(identity: object): boolean
|
export function canViewAllJobs(identity: object): boolean
|
||||||
|
export function isElevatedCronRole(role?: string | null): boolean
|
||||||
export function assertCanAccessJob(job: object, identity: object): object
|
export function assertCanAccessJob(job: object, identity: object): object
|
||||||
export function filterJobsForIdentity(jobs: object[], identity: object): object[]
|
export function filterJobsForIdentity(jobs: object[], identity: object): object[]
|
||||||
export function migrateJobOwners(state: object, deps?: object): { state: object, changed: boolean }
|
export function migrateJobOwners(state: object, deps?: object): { state: object, changed: boolean }
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ export {
|
||||||
canViewAllJobs,
|
canViewAllJobs,
|
||||||
claimUnassignedForViewer,
|
claimUnassignedForViewer,
|
||||||
filterJobsForIdentity,
|
filterJobsForIdentity,
|
||||||
|
isElevatedCronRole,
|
||||||
isMultiUserIdentity,
|
isMultiUserIdentity,
|
||||||
jobVisibleToIdentity,
|
jobVisibleToIdentity,
|
||||||
LOCAL_EMP_NO,
|
LOCAL_EMP_NO,
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,17 @@ export function localIdentity(overrides = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Roles that may keep foreign cwd and see every job (aligns with uds-auth). */
|
||||||
|
export function isElevatedCronRole(role) {
|
||||||
|
const value = String(role || '').trim()
|
||||||
|
return value === 'super_admin' || value === 'fallback_admin'
|
||||||
|
}
|
||||||
|
|
||||||
export function canViewAllJobs(identity) {
|
export function canViewAllJobs(identity) {
|
||||||
if (!isMultiUserIdentity(identity)) return true
|
if (!isMultiUserIdentity(identity)) return true
|
||||||
return !!identity?.permissions?.canViewAllSessions
|
if (identity?.permissions?.canViewAllSessions) return true
|
||||||
|
// Defense: some callers stamp role without copying permissions.
|
||||||
|
return isElevatedCronRole(identity?.role)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobVisibleToIdentity(job, identity) {
|
export function jobVisibleToIdentity(job, identity) {
|
||||||
|
|
@ -141,14 +149,18 @@ export function viewerPayload(identity) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default cwd when creating a job in the sidebar.
|
* Default cwd when creating a job in the sidebar.
|
||||||
* Prefer the viewer's provisioned workspace over whatever project the session
|
* - super_admin / fallback_admin: prefer the currently open session workspace
|
||||||
* happens to have open (admins often sit in an unrelated clone).
|
* (they often work in a shared tree like D:\\code\\gpt, not their empty provisioned dir).
|
||||||
|
* - ordinary multi-user: prefer provisioned path so a random open clone is not stamped.
|
||||||
*/
|
*/
|
||||||
export function preferredNewJobCwd({ viewer = null, sessionCwd = '' } = {}) {
|
export function preferredNewJobCwd({ viewer = null, sessionCwd = '' } = {}) {
|
||||||
|
const session = String(sessionCwd || '').trim()
|
||||||
const provisioned = String(viewer?.workspacePath || '').trim()
|
const provisioned = String(viewer?.workspacePath || '').trim()
|
||||||
|
if (viewer?.canViewAll && session) return session
|
||||||
|
if (isElevatedCronRole(viewer?.role) && session) return session
|
||||||
if (viewer?.mode === 'multi' && provisioned) return provisioned
|
if (viewer?.mode === 'multi' && provisioned) return provisioned
|
||||||
if (provisioned) return provisioned
|
if (provisioned) return provisioned
|
||||||
return String(sessionCwd || '').trim()
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -174,13 +174,14 @@ export function resolveToolIdentity(exec, service) {
|
||||||
if (typeof uds?.resolveIdentityForEmpNo === 'function') {
|
if (typeof uds?.resolveIdentityForEmpNo === 'function') {
|
||||||
const resolved = uds.resolveIdentityForEmpNo(id)
|
const resolved = uds.resolveIdentityForEmpNo(id)
|
||||||
if (resolved?.empNo) {
|
if (resolved?.empNo) {
|
||||||
|
const elevated = resolved.role === 'super_admin' || resolved.role === 'fallback_admin'
|
||||||
return {
|
return {
|
||||||
empNo: String(resolved.empNo),
|
empNo: String(resolved.empNo),
|
||||||
displayName: String(resolved.displayName || resolved.empNo),
|
displayName: String(resolved.displayName || resolved.empNo),
|
||||||
role: resolved.role || 'user',
|
role: resolved.role || 'user',
|
||||||
permissions: {
|
permissions: {
|
||||||
canViewAllSessions: !!resolved.permissions?.canViewAllSessions,
|
canViewAllSessions: !!(resolved.permissions?.canViewAllSessions || elevated),
|
||||||
canCreateWorkspace: !!resolved.permissions?.canCreateWorkspace,
|
canCreateWorkspace: !!(resolved.permissions?.canCreateWorkspace || elevated),
|
||||||
},
|
},
|
||||||
workspacePath: resolved.workspacePath || uds.getProvisionedWorkspacePath?.(id) || null,
|
workspacePath: resolved.workspacePath || uds.getProvisionedWorkspacePath?.(id) || null,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "dsh-ops-cron",
|
"name": "dsh-ops-cron",
|
||||||
"description": "Scheduled tasks for DeepSeek Harness: Agent cron_* tools + sidebar 定时任务, with DSH or WhatsApp/IM delivery per job.",
|
"description": "Scheduled tasks for DeepSeek Harness: Agent cron_* tools + sidebar 定时任务, with DSH or WhatsApp/IM delivery per job.",
|
||||||
"version": "0.1.13",
|
"version": "0.1.14",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
||||||
|
|
@ -671,6 +671,75 @@ test('super_admin createJob keeps explicit foreign cwd; normal user is clamped',
|
||||||
assert.equal(userJob.cwd, '/tmp/user-workspaces/tester')
|
assert.equal(userJob.cwd, '/tmp/user-workspaces/tester')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('createJob elevates via live resolveIdentityForEmpNo when caller permissions are empty', async (t) => {
|
||||||
|
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||||
|
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||||
|
const service = createTestHost({
|
||||||
|
filePath: join(dir, 'store.json'),
|
||||||
|
now: () => Date.parse('2026-08-24T01:00:00.000Z'),
|
||||||
|
udsAuthOptions: { strictPath: true },
|
||||||
|
sessionPort: {
|
||||||
|
async createAndPrompt() {
|
||||||
|
return { sessionId: 's1', status: 'succeeded', summary: 'ok' }
|
||||||
|
},
|
||||||
|
async archiveSession() {},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// Stale tool identity: empNo only, wrong role flag — must re-resolve from uds-auth.
|
||||||
|
const stale = {
|
||||||
|
empNo: '10329667',
|
||||||
|
role: 'user',
|
||||||
|
displayName: '10329667',
|
||||||
|
permissions: { canViewAllSessions: false },
|
||||||
|
workspacePath: '/tmp/deepseek-harness/10329667',
|
||||||
|
}
|
||||||
|
const job = await service.createJob({
|
||||||
|
name: 'gpt-via-live',
|
||||||
|
prompt: 'work in gpt',
|
||||||
|
cwd: 'D:/code/gpt',
|
||||||
|
schedule: { kind: 'at', at: '2026-08-24T02:00:00.000Z', timezone: 'UTC' },
|
||||||
|
}, stale)
|
||||||
|
assert.equal(job.cwd, 'D:/code/gpt')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('createJob keeps foreign cwd when only role is stamped super_admin', async (t) => {
|
||||||
|
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||||
|
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||||
|
const service = createTestHost({
|
||||||
|
filePath: join(dir, 'store.json'),
|
||||||
|
now: () => Date.parse('2026-08-24T01:00:00.000Z'),
|
||||||
|
udsAuth: {
|
||||||
|
getProvisionedWorkspacePath(empNo) {
|
||||||
|
return empNo === '10329667'
|
||||||
|
? '/tmp/deepseek-harness/10329667'
|
||||||
|
: `/tmp/user-workspaces/${empNo}`
|
||||||
|
},
|
||||||
|
isUserPath(empNo, candidatePath) {
|
||||||
|
const root = this.getProvisionedWorkspacePath(empNo)
|
||||||
|
const cand = String(candidatePath || '').replace(/\\/g, '/')
|
||||||
|
const normRoot = String(root).replace(/\\/g, '/')
|
||||||
|
return cand === normRoot || cand.startsWith(`${normRoot}/`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sessionPort: {
|
||||||
|
async createAndPrompt() {
|
||||||
|
return { sessionId: 's1', status: 'succeeded', summary: 'ok' }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const job = await service.createJob({
|
||||||
|
name: 'role-only',
|
||||||
|
prompt: 'p',
|
||||||
|
cwd: 'D:/code/gpt',
|
||||||
|
schedule: { kind: 'at', at: '2026-08-24T02:00:00.000Z', timezone: 'UTC' },
|
||||||
|
}, {
|
||||||
|
empNo: '10329667',
|
||||||
|
role: 'super_admin',
|
||||||
|
permissions: {},
|
||||||
|
})
|
||||||
|
assert.equal(job.cwd, 'D:/code/gpt')
|
||||||
|
})
|
||||||
|
|
||||||
test('live fire uses super_admin job cwd outside provisioned tree', async (t) => {
|
test('live fire uses super_admin job cwd outside provisioned tree', async (t) => {
|
||||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||||
|
|
|
||||||
|
|
@ -125,14 +125,29 @@ test('claimUnassignedForViewer claims by session owner or user-workspaces cwd',
|
||||||
assert.equal(next.jobs[5].ownerEmpNo, UNASSIGNED_OWNER)
|
assert.equal(next.jobs[5].ownerEmpNo, UNASSIGNED_OWNER)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('preferredNewJobCwd prefers provisioned viewer workspace over session cwd', () => {
|
test('preferredNewJobCwd: users prefer provisioned; elevated prefer session', () => {
|
||||||
assert.equal(preferredNewJobCwd({
|
assert.equal(preferredNewJobCwd({
|
||||||
viewer: { mode: 'multi', workspacePath: '/tmp/user-workspaces/10329667' },
|
viewer: { mode: 'multi', workspacePath: '/tmp/user-workspaces/10329667' },
|
||||||
sessionCwd: 'D:/code/deepseek-harness/clone',
|
sessionCwd: 'D:/code/deepseek-harness/clone',
|
||||||
}), '/tmp/user-workspaces/10329667')
|
}), '/tmp/user-workspaces/10329667')
|
||||||
|
assert.equal(preferredNewJobCwd({
|
||||||
|
viewer: {
|
||||||
|
mode: 'multi',
|
||||||
|
canViewAll: true,
|
||||||
|
role: 'super_admin',
|
||||||
|
workspacePath: '/tmp/deepseek-harness/10329667',
|
||||||
|
},
|
||||||
|
sessionCwd: 'D:/code/gpt',
|
||||||
|
}), 'D:/code/gpt')
|
||||||
assert.equal(preferredNewJobCwd({
|
assert.equal(preferredNewJobCwd({
|
||||||
viewer: { mode: 'local', workspacePath: null },
|
viewer: { mode: 'local', workspacePath: null },
|
||||||
sessionCwd: '/tmp/ws-app',
|
sessionCwd: '/tmp/ws-app',
|
||||||
}), '/tmp/ws-app')
|
}), '/tmp/ws-app')
|
||||||
assert.equal(preferredNewJobCwd({}), '')
|
assert.equal(preferredNewJobCwd({}), '')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('canViewAllJobs treats super_admin role even without permissions flags', () => {
|
||||||
|
assert.equal(canViewAllJobs({ empNo: '10329667', role: 'super_admin', permissions: {} }), true)
|
||||||
|
assert.equal(canViewAllJobs({ empNo: 'tester', role: 'user', permissions: {} }), false)
|
||||||
|
assert.equal(canViewAllJobs({ empNo: 'a', role: 'fallback_admin' }), true)
|
||||||
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue