tasks.js
1 import { cli, Strategy } from '@jackwener/opencli/registry'; 2 import { CliError } from '@jackwener/opencli/errors'; 3 import { gotoOnesHome, onesFetchInPage } from './common.js'; 4 import { enrichPeekEntriesWithDetails } from './enrich-tasks.js'; 5 import { resolveTaskListLabels } from './resolve-labels.js'; 6 import { defaultPeekBody, flattenPeekGroups, mapTaskEntry, parsePeekLimit } from './task-helpers.js'; 7 function buildQuery(project, assign) { 8 const must = []; 9 if (project?.trim()) { 10 must.push({ in: { 'field_values.field006': [project.trim()] } }); 11 } 12 if (assign?.trim()) { 13 must.push({ equal: { assign: assign.trim() } }); 14 } 15 if (must.length === 0) { 16 return { must: [] }; 17 } 18 return { must }; 19 } 20 cli({ 21 site: 'ones', 22 name: 'tasks', 23 description: 'ONES Project API — list work items (POST team/:team/filters/peek); use token-info -f json for team uuid', 24 domain: 'ones.cn', 25 strategy: Strategy.COOKIE, 26 browser: true, 27 navigateBefore: false, 28 args: [ 29 { 30 name: 'team', 31 type: 'str', 32 required: false, 33 positional: true, 34 help: 'Team UUID (8 chars), or set ONES_TEAM_UUID', 35 }, 36 { 37 name: 'project', 38 type: 'str', 39 required: false, 40 help: 'Filter by project UUID (field006 / 所属项目)', 41 }, 42 { 43 name: 'assign', 44 type: 'str', 45 required: false, 46 help: 'Filter by assignee user UUID (负责人 assign)', 47 }, 48 { 49 name: 'limit', 50 type: 'int', 51 default: 30, 52 help: 'Max rows after flattening groups (default 30)', 53 }, 54 ], 55 columns: ['title', 'status', 'project', 'uuid', 'updated', '工时'], 56 func: async (page, kwargs) => { 57 const team = kwargs.team?.trim() || 58 process.env.ONES_TEAM_UUID?.trim() || 59 process.env.ONES_TEAM_ID?.trim(); 60 if (!team) { 61 throw new CliError('CONFIG', 'team UUID required', 'Pass team as first argument or set ONES_TEAM_UUID (see `opencli ones token-info -f json` → teams[].uuid).'); 62 } 63 const project = kwargs.project?.trim(); 64 const assign = kwargs.assign?.trim(); 65 const limit = parsePeekLimit(kwargs.limit, 30); 66 await gotoOnesHome(page); 67 const body = defaultPeekBody(buildQuery(project, assign)); 68 const path = `team/${team}/filters/peek`; 69 const parsed = (await onesFetchInPage(page, path, { 70 method: 'POST', 71 body: JSON.stringify(body), 72 skipGoto: true, 73 })); 74 const entries = flattenPeekGroups(parsed, limit); 75 const enriched = await enrichPeekEntriesWithDetails(page, team, entries, true); 76 const labels = await resolveTaskListLabels(page, team, enriched, true); 77 return enriched.map((e) => mapTaskEntry(e, labels)); 78 }, 79 });