mirror of
https://github.com/hansjone/dsh-ops-cron.git
synced 2026-10-11 10:33:29 +08:00
Add sidebar-only agentAccess lock for scheduled jobs.
Default remains allow; humans can deny agent pause/resume/retrigger/delete in the panel while tools never expose or set the field. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4472a9c74b
commit
ad6e90f197
7 changed files with 130 additions and 11 deletions
51
lib/tools.js
51
lib/tools.js
|
|
@ -20,7 +20,7 @@ import {
|
|||
jobVisibleToIdentity,
|
||||
UNASSIGNED_OWNER,
|
||||
} from './ownership.js'
|
||||
import { splitProviderModel } from './store.js'
|
||||
import { agentMayMutateJob, splitProviderModel } from './store.js'
|
||||
|
||||
const JOB_SCHEMA = {
|
||||
type: 'object',
|
||||
|
|
@ -216,6 +216,20 @@ function jobLine(job) {
|
|||
return `${job.name} [${life}${stuck}] ${sched} tz=${tz} cwd=${job.cwd || '(recent workspace)'} model=${model} ${delivery}${labels} next=${when} id=${job.id}`
|
||||
}
|
||||
|
||||
/** Strip human-only fields so agents cannot see or game panel-only controls. */
|
||||
function forAgentView(job) {
|
||||
if (!job || typeof job !== 'object') return job
|
||||
const { agentAccess: _hidden, ...rest } = job
|
||||
return rest
|
||||
}
|
||||
|
||||
function assertAgentMayMutate(job) {
|
||||
if (agentMayMutateJob(job)) return
|
||||
const error = new Error('this job is human-managed; change “Agent 操作” in the 定时任务 panel (agents cannot toggle it)')
|
||||
error.code = 'AGENT_ACCESS_DENIED'
|
||||
throw error
|
||||
}
|
||||
|
||||
export function callerWorkingDirectory(exec) {
|
||||
const session = exec?.agent?.session
|
||||
return String(session?.header?.cwd || session?.cwd || exec?.agent?.cwd || '').trim()
|
||||
|
|
@ -458,7 +472,7 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
agentPreset,
|
||||
...monitorFieldsFromArgs(args),
|
||||
}, identity, { fromImPeer: !!peer?.botId })
|
||||
return { job }
|
||||
return { job: forAgentView(job) }
|
||||
} catch (error) {
|
||||
const tz = args.timezone || args.time_zone || 'Asia/Shanghai'
|
||||
const nowText = formatInZone(Date.now(), tz)
|
||||
|
|
@ -512,7 +526,8 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
let jobs = await service.listJobs(identity, Object.keys(query).length ? query : null)
|
||||
if (peer?.botId) jobs = jobs.filter((job) => jobVisibleToPeer(job, peer))
|
||||
if (args?.enabled_only === true) jobs = jobs.filter((job) => job.enabled !== false)
|
||||
return { jobs, count: jobs.length }
|
||||
const visible = jobs.map(forAgentView)
|
||||
return { jobs: visible, count: visible.length }
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -565,6 +580,12 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
result.items = (result.items || []).filter((item) => jobVisibleToPeer(item.job, peer))
|
||||
result.count = result.jobs.length
|
||||
}
|
||||
result.jobs = (result.jobs || []).map(forAgentView)
|
||||
result.items = (result.items || []).map((item) => (
|
||||
item && typeof item === 'object'
|
||||
? { ...item, job: forAgentView(item.job) }
|
||||
: item
|
||||
))
|
||||
return result
|
||||
},
|
||||
},
|
||||
|
|
@ -665,6 +686,11 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
result.snapshots = (result.snapshots || []).filter((row) => jobVisibleToPeer(row.job, peer))
|
||||
result.count = result.snapshots.length
|
||||
}
|
||||
result.snapshots = (result.snapshots || []).map((row) => (
|
||||
row && typeof row === 'object'
|
||||
? { ...row, job: forAgentView(row.job) }
|
||||
: row
|
||||
))
|
||||
return result
|
||||
},
|
||||
},
|
||||
|
|
@ -687,9 +713,10 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
const peer = await resolveCallerPeer(exec, getDshIm())
|
||||
const identity = resolveToolIdentity(exec, service)
|
||||
const id = requireId(args)
|
||||
await requireOwnedJob(service, id, peer, identity)
|
||||
const owned = await requireOwnedJob(service, id, peer, identity)
|
||||
assertAgentMayMutate(owned)
|
||||
const job = await service.pauseJob(id, false, identity)
|
||||
return { job }
|
||||
return { job: forAgentView(job) }
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -711,9 +738,10 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
const peer = await resolveCallerPeer(exec, getDshIm())
|
||||
const identity = resolveToolIdentity(exec, service)
|
||||
const id = requireId(args)
|
||||
await requireOwnedJob(service, id, peer, identity)
|
||||
const owned = await requireOwnedJob(service, id, peer, identity)
|
||||
assertAgentMayMutate(owned)
|
||||
const job = await service.pauseJob(id, true, identity)
|
||||
return { job }
|
||||
return { job: forAgentView(job) }
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -760,10 +788,12 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
const identity = resolveToolIdentity(exec, service)
|
||||
const id = String(args?.task_id || args?.id || '').trim()
|
||||
if (!id) throw new Error('task_id is required')
|
||||
await requireOwnedJob(service, id, peer, identity)
|
||||
return service.retriggerJob(id, {
|
||||
const owned = await requireOwnedJob(service, id, peer, identity)
|
||||
assertAgentMayMutate(owned)
|
||||
const result = await service.retriggerJob(id, {
|
||||
after_minutes: args?.after_minutes,
|
||||
}, identity)
|
||||
return { ...result, job: forAgentView(result.job) }
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -794,7 +824,8 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
const identity = resolveToolIdentity(exec, service)
|
||||
const id = requireId(args)
|
||||
try {
|
||||
await requireOwnedJob(service, id, peer, identity)
|
||||
const owned = await requireOwnedJob(service, id, peer, identity)
|
||||
assertAgentMayMutate(owned)
|
||||
await service.deleteJob(id)
|
||||
return { id, deleted: true }
|
||||
} catch (error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue