# Web Task Editing, Scheduling, and AI-Optimize UI (2026-05) Session learning from extending BOSS's local `xianyu-hunter` fork at: ```text /Users/chick/.Hermes/workspace/research/xianyu-hunter ``` ## Feature shape implemented When BOSS asks for Web后台 task management instead of Hermes-only CLI control, keep the product shape focused on monitoring/analysis-only: - Web task modal supports both create and edit. - Existing tasks can be edited only when not running. - Task form supports: - search keywords (`queries`) - price min/max - region - personal seller toggle - max pages - `maxItems` / 单次最大商品寻找量 - custom requirements - coarse/fine prompts - schedule enabled + interval minutes - AI optimize/current-form generation - Save modes: - `autoStart:false` = 手动新增任务但不立即采集 - save + run = create and start immediately - edit + reset run = save then rerun with old products cleared - Keep seller chat disabled. Scheduled/background runs must still stop at collection + local prefilter/detail collection and never auto-contact sellers. ## Backend/API pattern Good API additions: ```text PUT /api/tasks/:id edit task config (reject while running) POST /api/tasks/optimize generate optimized config from current form POST /api/tasks accepts autoStart:false POST /api/tasks/:id/start accepts { reset:true } ``` Task config should normalize legacy and new schedule shapes: ```js const existingSchedule = config.schedule || {}; const scheduleEnabled = config.scheduleEnabled ?? existingSchedule.enabled ?? false; const scheduleIntervalMinutes = config.scheduleIntervalMinutes ?? existingSchedule.intervalMinutes ?? 0; ``` `maxItems` should be enforced after each query's dedupe and before fine detail fetch: ```js const remaining = Math.max(0, (task.config.maxItems || 60) - task.products.raw.length); const accepted = deduped.slice(0, remaining); task.products.raw.push(...accepted); if (task.products.raw.length >= (task.config.maxItems || 60)) break; await fineFilter(task.products.coarseFiltered.slice(0, task.config.maxItems || 60), ...) ``` Scheduler pattern: - `TaskManager` starts an interval timer in constructor after `_loadFromDisk()`. - Tick every ~30s. - For enabled schedule with due `nextRunAt`, non-running task: push a log and call `startTask(id, { reset: true, scheduled: true })`. - On scheduled run, set `lastRunAt` and normalize next run. ## AI optimize fallback `POST /api/tasks/optimize` can try `chatCompletion()`, but must tolerate missing AI config. If it fails, return `{ ok:true, source:'local-fallback', task }` so the UI still works. Local fallback should generate conservative monitoring defaults: - `maxPages`: 2 - `maxItems`: 40 - `personalSeller`: true - requirements explicitly include: no seller contact, exclude parts/repair/wanted/rental/recycling/commercial bulk/title spam. ## Frontend/UI notes - Reuse the create modal for editing by tracking `editingTaskId`. - Add buttons in pipeline controls: 编辑, 复制, 继续/启动, 重新采集, 删除. - `重新采集` should confirm because it clears current round products. - `fillTaskForm(taskOrConfig)` avoids duplicate create/edit/duplicate mapping bugs. - Add cache-busting version bump for `style.css` and `app.js`; also serve static files with `Cache-Control: no-store` to avoid stale dashboard assets. ## Verification recipe Before final response: ```bash cd /Users/chick/.Hermes/workspace/research/xianyu-hunter node --check server.mjs && node --check lib/task.mjs && node --check public/app.js ``` Restart the exact server on port 3000. If `node tools/xianyu_ops.mjs stop` says `no-recorded-server` but port is in use, kill both the listener and parent npm process: ```bash lsof -nP -iTCP:3000 -sTCP:LISTEN ps -p -o pid,ppid,command kill -TERM ``` Then start in background and verify API: ```bash PORT=3000 npm start curl -sS http://127.0.0.1:3000/api/tasks | python3 -m json.tool ``` Smoke-test the new APIs with a temporary task and delete it after: ```bash python3 - <<'PY' import json, urllib.request base='http://127.0.0.1:3000' def req(method,path,data=None): body=json.dumps(data).encode() if data is not None else None r=urllib.request.Request(base+path,data=body,method=method,headers={'Content-Type':'application/json'}) with urllib.request.urlopen(r,timeout=30) as resp: return json.loads(resp.read().decode()) opt=req('POST','/api/tasks/optimize',{'product':'e3-1245v3 cpu','budget':'150','currentRequirements':'只要CPU本体,排除主板套装'}) created=req('POST','/api/tasks',{**opt['task'],'name':'__api_verify_temp__','autoStart':False,'scheduleEnabled':True,'scheduleIntervalMinutes':120,'maxItems':25}) updated=req('PUT',f"/api/tasks/{created['id']}",{'name':'__api_verify_temp_updated__','queries':['e3 1245 v3 verify'],'maxItems':30,'scheduleEnabled':False,'scheduleIntervalMinutes':0}) req('DELETE',f"/api/tasks/{created['id']}") print('ok', opt.get('source'), created['id'], updated['config']['maxItems']) PY ``` ## Pitfall Do not leave a final user response empty after tools. If a tool result shows a port conflict or failed restart, process it immediately, continue with the fix, and give BOSS the final verified state.