feat(tasks): 总览页支持任务测试范围的查看/增删
- db: 新增 DeleteTaskScope;AddAgentScope 加 source 参数区分 agent/manual
- server: 新增 POST/DELETE /api/tasks/{id}/scope 端点
- web: 总览页新增「测试范围」卡片,可按类型手动增删,继承范围只读
Claude-Session: https://claude.ai/code/session_014TfZWeVGhjXqZD3vcj44mz
This commit is contained in:
1 parent
56101b46e8
commit
6b79043d82
8 files changed
+488
-193
No files matched your search
@@ -356,13 +356,13 @@ func TestTaskAssetContextUsesDirectSourceScopeAndAnchors(t *testing.T) {
|
||||
_ = d.DeleteTask(grand.ID)
|
||||
_, _ = assets.DeleteByIDs([]int64{sourceTestedID, sourceUntestedID, sourceAnchoredOnlyID, grandID})
|
||||
})
|
||||
if _, err := assets.AddAgentScope(source.ID, "root_domain", sourceTestedDomain, "test"); err != nil {
|
||||
if _, err := assets.AddAgentScope(source.ID, "root_domain", sourceTestedDomain, "test", "agent"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := assets.AddAgentScope(source.ID, "root_domain", sourceUntestedDomain, "test"); err != nil {
|
||||
if _, err := assets.AddAgentScope(source.ID, "root_domain", sourceUntestedDomain, "test", "agent"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := assets.AddAgentScope(grand.ID, "root_domain", grandDomain, "test"); err != nil {
|
||||
if _, err := assets.AddAgentScope(grand.ID, "root_domain", grandDomain, "test", "agent"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := d.Exploration(source.ExplorationID).AddNode(KindFact, map[string]any{"summary": "tested source asset"}, 0, "confirmed", "worker", []int64{sourceTestedID}); err != nil {
|
||||
|
||||
+18
-3
@@ -113,11 +113,15 @@ func (s *AssetStore) AddAutoScope(taskID int64, assetType, domain, rawURL, ip st
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddAgentScope parses an agent-provided (kind,value) and records it (source='agent').
|
||||
// AddAgentScope parses a (kind,value) pair and records it as task scope.
|
||||
// source is "agent" when called from an LLM tool, "manual" from the UI.
|
||||
// company: value = company name or id (must already exist). root_domain/subdomain:
|
||||
// value = a domain. ip/cidr: value = an IP or CIDR (bare IP → /32,/128).
|
||||
func (s *AssetStore) AddAgentScope(taskID int64, kind, value, reason string) (TaskScope, error) {
|
||||
ts := TaskScope{TaskID: taskID, Kind: kind, Source: "agent", Reason: reason}
|
||||
func (s *AssetStore) AddAgentScope(taskID int64, kind, value, reason, source string) (TaskScope, error) {
|
||||
if source == "" {
|
||||
source = "agent"
|
||||
}
|
||||
ts := TaskScope{TaskID: taskID, Kind: kind, Source: source, Reason: reason}
|
||||
if taskID <= 0 {
|
||||
return ts, fmt.Errorf("需要 task_id")
|
||||
}
|
||||
@@ -184,6 +188,17 @@ func (s *AssetStore) AddAgentScope(taskID int64, kind, value, reason string) (Ta
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// DeleteTaskScope removes a single scope row by id, scoped to the given task.
|
||||
// Returns whether a row was actually deleted.
|
||||
func (s *AssetStore) DeleteTaskScope(taskID, scopeID int64) (bool, error) {
|
||||
res, err := s.db.Exec(`DELETE FROM task_scope WHERE id=$1 AND task_id=$2`, scopeID, taskID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
n, _ := res.RowsAffected()
|
||||
return n > 0, nil
|
||||
}
|
||||
|
||||
// ListTaskScope returns all scope rows for a task.
|
||||
func (s *AssetStore) ListTaskScope(taskID int64) ([]TaskScope, error) {
|
||||
rows, err := s.db.Query(`
|
||||
|
||||
Reference in new issue
Block a user