vps-work 2.4.1 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.well-known/skills/index.json +11 -0
- package/README.md +10 -0
- package/dist/packages/vps-work/src/bin/vps-work.js +271 -0
- package/dist/packages/vps-work/src/commands/agent/index.js +264 -0
- package/dist/packages/vps-work/src/commands/approval/index.js +7 -0
- package/dist/packages/vps-work/src/commands/approval/shortcuts.js +259 -0
- package/dist/packages/vps-work/src/commands/attendance/applications.js +21 -0
- package/dist/packages/vps-work/src/commands/attendance/index.js +9 -0
- package/dist/packages/vps-work/src/commands/attendance/scheduling.js +183 -0
- package/dist/packages/vps-work/src/commands/attendance/shortcuts.js +131 -0
- package/dist/packages/vps-work/src/commands/auth/check.js +34 -0
- package/dist/packages/vps-work/src/commands/auth/index.js +21 -0
- package/dist/packages/vps-work/src/commands/auth/list.js +50 -0
- package/dist/packages/vps-work/src/commands/auth/scopes.js +17 -0
- package/dist/packages/vps-work/src/commands/auth/status.js +127 -0
- package/dist/packages/vps-work/src/commands/auth/tenants.js +35 -0
- package/dist/packages/vps-work/src/commands/auth/use.js +40 -0
- package/dist/packages/vps-work/src/commands/calendar/index.js +7 -0
- package/dist/packages/vps-work/src/commands/calendar/shortcuts.js +598 -0
- package/dist/packages/vps-work/src/commands/catalog.js +29 -0
- package/dist/packages/vps-work/src/commands/cli-install.js +70 -0
- package/dist/packages/vps-work/src/commands/collab-invoke.js +19 -0
- package/dist/packages/vps-work/src/commands/corporate/index.js +204 -0
- package/dist/packages/vps-work/src/commands/creative/index.js +369 -0
- package/dist/packages/vps-work/src/commands/documents/index.js +991 -0
- package/dist/packages/vps-work/src/commands/drive/index.js +435 -0
- package/dist/packages/vps-work/src/commands/exec.js +46 -0
- package/dist/packages/vps-work/src/commands/hr/business.js +22 -0
- package/dist/packages/vps-work/src/commands/hr/data-view-shortcuts.js +519 -0
- package/dist/packages/vps-work/src/commands/hr/index.js +15 -0
- package/dist/packages/vps-work/src/commands/hr/job-responsibilities.js +266 -0
- package/dist/packages/vps-work/src/commands/hr/payslip.js +45 -0
- package/dist/packages/vps-work/src/commands/hr/shortcuts.js +146 -0
- package/dist/packages/vps-work/src/commands/im/im-message-payload.js +150 -0
- package/dist/packages/vps-work/src/commands/im/index.js +7 -0
- package/dist/packages/vps-work/src/commands/im/shortcuts.js +856 -0
- package/dist/packages/vps-work/src/commands/knowledge/index.js +36 -0
- package/dist/packages/vps-work/src/commands/login.js +52 -0
- package/dist/packages/vps-work/src/commands/logout.js +12 -0
- package/dist/packages/vps-work/src/commands/mail/email.js +132 -0
- package/dist/packages/vps-work/src/commands/mail/index.js +9 -0
- package/dist/packages/vps-work/src/commands/mail/shortcuts.js +43 -0
- package/dist/packages/vps-work/src/commands/meeting/index.js +7 -0
- package/dist/packages/vps-work/src/commands/meeting/shortcuts.js +120 -0
- package/dist/packages/vps-work/src/commands/odoo-invoke.js +21 -0
- package/dist/packages/vps-work/src/commands/odoo-leaf-registry.js +13 -0
- package/dist/packages/vps-work/src/commands/office/index.js +59 -0
- package/dist/packages/vps-work/src/commands/okr/index.js +7 -0
- package/dist/packages/vps-work/src/commands/okr/shortcuts.js +75 -0
- package/dist/packages/vps-work/src/commands/org-structure.js +347 -0
- package/dist/packages/vps-work/src/commands/popup/index.js +147 -0
- package/dist/packages/vps-work/src/commands/product/abnormal-statistics.js +250 -0
- package/dist/packages/vps-work/src/commands/product/data-view-shortcuts.js +253 -0
- package/dist/packages/vps-work/src/commands/product/index.js +11 -0
- package/dist/packages/vps-work/src/commands/purchase/index.js +45 -0
- package/dist/packages/vps-work/src/commands/reauth.js +127 -0
- package/dist/packages/vps-work/src/commands/report/index.js +7 -0
- package/dist/packages/vps-work/src/commands/report/missing-daily-reports.js +9 -0
- package/dist/packages/vps-work/src/commands/report/shortcuts.js +251 -0
- package/dist/packages/vps-work/src/commands/sales/index.js +11 -0
- package/dist/packages/vps-work/src/commands/sales/period-params.js +75 -0
- package/dist/packages/vps-work/src/commands/sales/shortcuts-personnel.js +124 -0
- package/dist/packages/vps-work/src/commands/sales/shortcuts.js +800 -0
- package/dist/packages/vps-work/src/commands/shortcut-common.js +88 -0
- package/dist/packages/vps-work/src/commands/shortcut-params.js +22 -0
- package/dist/packages/vps-work/src/commands/skill/index.js +136 -0
- package/dist/packages/vps-work/src/commands/skill/personal.js +167 -0
- package/dist/packages/vps-work/src/commands/store/index.js +82 -0
- package/dist/packages/vps-work/src/commands/task/index.js +7 -0
- package/dist/packages/vps-work/src/commands/task/task-center.js +595 -0
- package/dist/packages/vps-work/src/commands/tools/index.js +127 -0
- package/dist/packages/vps-work/src/commands/vemory/index.js +7 -0
- package/dist/packages/vps-work/src/commands/vemory/shortcuts.js +53 -0
- package/dist/packages/vps-work/src/commands/vertu-aisales.js +30 -0
- package/dist/packages/vps-work/src/commands/vertu-erp.js +30 -0
- package/dist/packages/vps-work/src/core/aisales-mcp-client.js +35 -0
- package/dist/packages/vps-work/src/core/auth.js +240 -0
- package/dist/packages/vps-work/src/core/cli-bin.js +11 -0
- package/dist/packages/vps-work/src/core/cli-root-help.js +190 -0
- package/dist/packages/vps-work/src/core/client.js +158 -0
- package/dist/packages/vps-work/src/core/config.js +183 -0
- package/dist/packages/vps-work/src/core/corporate-client.js +52 -0
- package/dist/packages/vps-work/src/core/creative-attachment-download.js +48 -0
- package/dist/packages/vps-work/src/core/documents-client.js +57 -0
- package/dist/packages/vps-work/src/core/endpoints.js +46 -0
- package/dist/packages/vps-work/src/core/erp-mcp-client.js +11 -0
- package/dist/packages/vps-work/src/core/errors.js +107 -0
- package/dist/packages/vps-work/src/core/execute-catalog-client.js +42 -0
- package/dist/packages/vps-work/src/core/hermes-client.js +70 -0
- package/dist/packages/vps-work/src/core/im-attachment-download.js +109 -0
- package/dist/packages/vps-work/src/core/im-client.js +64 -0
- package/dist/packages/vps-work/src/core/keychain.js +58 -0
- package/dist/packages/vps-work/src/core/mail-client.js +82 -0
- package/dist/packages/vps-work/src/core/mcp-client.js +129 -0
- package/dist/packages/vps-work/src/core/office-client.js +25 -0
- package/dist/packages/vps-work/src/core/office-files.js +82 -0
- package/dist/packages/vps-work/src/core/paths.js +70 -0
- package/dist/packages/vps-work/src/core/public-result.js +43 -0
- package/dist/packages/vps-work/src/core/sales-invoice-attachments-client.js +75 -0
- package/dist/packages/vps-work/src/core/sales-query-client.js +452 -0
- package/dist/packages/vps-work/src/core/skill-catalog.js +158 -0
- package/dist/packages/vps-work/src/core/skill-key.js +19 -0
- package/dist/packages/vps-work/src/core/v-drive-store.js +392 -0
- package/dist/packages/vps-work/src/core/vertu-agent-env.js +32 -0
- package/dist/packages/vps-work/src/core/vps-auth-scopes.js +73 -0
- package/dist/packages/vps-work/src/core/vps-service-auth.js +382 -0
- package/dist/packages/vps-work/src/core/vps-service-config.js +83 -0
- package/dist/packages/vps-work/src/core/vps-service-http.js +187 -0
- package/dist/packages/vps-work/src/utils/at-file.js +34 -0
- package/dist/packages/vps-work/src/utils/cli-option-helpers.js +52 -0
- package/dist/packages/vps-work/src/utils/frontmatter.js +26 -0
- package/dist/packages/vps-work/src/utils/normalize-argv.js +51 -0
- package/dist/packages/vps-work/src/utils/parse-json-arg.js +85 -0
- package/dist/packages/vps-work/src/utils/prompt.js +46 -0
- package/dist/packages/vps-work/src/utils/read-text.js +44 -0
- package/dist/packages/vps-work/src/utils/schema-to-options.js +307 -0
- package/dist/packages/vps-work/src/utils/unwrap-envelope.js +31 -0
- package/dist/shared/im-org-tree/index.js +200 -0
- package/dist/vps-work.cjs +246 -44
- package/package.json +1 -1
- package/scripts/package-desktop-runtime.mjs +43 -0
- package/skills/README.md +1 -0
- package/skills/vertu-aisales/SKILL.md +18 -0
- package/skills/vertu-erp/SKILL.md +17 -0
- package/skills/vps-dealer-ops/SKILL.md +92 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
import { imRequest, printImResult } from '../../core/im-client.js';
|
|
2
|
+
import { VertuError } from '../../core/errors.js';
|
|
3
|
+
import { attachSharedShortcutOptions, mergeShortcutParams, parseOptionalInt, parseOptionalFloat, previewShortcutRequest, } from '../shortcut-common.js';
|
|
4
|
+
function applyListFilters(query, opts, kind) {
|
|
5
|
+
for (const [name, value] of [['start_time', opts.startTime], ['end_time', opts.endTime]]) {
|
|
6
|
+
if (value !== undefined) {
|
|
7
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || !Number.isFinite(Date.parse(value)) || new Date(value).toISOString().slice(0, 10) !== value)
|
|
8
|
+
throw new VertuError(`${name} must be a valid YYYY-MM-DD date`);
|
|
9
|
+
query[name] = value;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
if (opts.startTime && opts.endTime && opts.startTime > opts.endTime)
|
|
13
|
+
throw new VertuError('start_time must not exceed end_time');
|
|
14
|
+
if (opts.dateField !== undefined) {
|
|
15
|
+
const fields = ['created_at', 'updated_at', 'deadline', 'completed_at', ...(kind === 'tasks' ? ['start_date'] : [])];
|
|
16
|
+
if (!fields.includes(opts.dateField))
|
|
17
|
+
throw new VertuError(`date_field must be one of: ${fields.join(', ')}`);
|
|
18
|
+
query.date_field = opts.dateField;
|
|
19
|
+
}
|
|
20
|
+
if (opts.includeDepartment === false)
|
|
21
|
+
query.include_department = false;
|
|
22
|
+
const raw = kind === 'tasks' ? opts.offset : opts.page;
|
|
23
|
+
if (raw !== undefined) {
|
|
24
|
+
const n = Number(raw);
|
|
25
|
+
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(n) || n < (kind === 'tasks' ? 0 : 1))
|
|
26
|
+
throw new VertuError('Invalid pagination value');
|
|
27
|
+
query[kind === 'tasks' ? 'offset' : 'page'] = n;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function resolveBaseUrl(opts, cmd) {
|
|
31
|
+
const merged = cmd.optsWithGlobals();
|
|
32
|
+
return opts.baseUrl ?? merged.baseUrl;
|
|
33
|
+
}
|
|
34
|
+
function requireNonEmpty(raw, flag) {
|
|
35
|
+
const value = raw?.trim();
|
|
36
|
+
if (!value) {
|
|
37
|
+
throw new VertuError(`${flag} is required.`);
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
function requireId(raw, flag) {
|
|
42
|
+
const value = raw?.trim();
|
|
43
|
+
if (!value) {
|
|
44
|
+
throw new VertuError(`${flag} is required.`);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
function requireUpdateBody(body) {
|
|
49
|
+
const fields = ['name', 'project_type', 'description', 'goal', 'priority', 'progress',
|
|
50
|
+
'status', 'owner_user_id', 'start_date', 'deadline', 'milestones'];
|
|
51
|
+
if (!fields.some((field) => Object.hasOwn(body, field))) {
|
|
52
|
+
throw new VertuError('At least one project field is required for update.');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function rejectDeleteParams(opts) {
|
|
56
|
+
if (opts.params !== undefined || opts.paramsFile !== undefined) {
|
|
57
|
+
throw new VertuError('Delete commands have a fixed archive action; --params is not supported.');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function putString(target, key, raw) {
|
|
61
|
+
const value = raw?.trim();
|
|
62
|
+
if (value) {
|
|
63
|
+
target[key] = value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function putInt(target, key, raw, flag) {
|
|
67
|
+
const value = parseOptionalInt(raw, flag);
|
|
68
|
+
if (value !== undefined) {
|
|
69
|
+
target[key] = value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function putOkrContributionWeight(target, raw) {
|
|
73
|
+
const value = parseOptionalFloat(raw, '--okr-contribution-weight');
|
|
74
|
+
if (value === undefined)
|
|
75
|
+
return;
|
|
76
|
+
if (value <= 0 || value > 100) {
|
|
77
|
+
throw new VertuError('--okr-contribution-weight must be greater than 0 and at most 100.');
|
|
78
|
+
}
|
|
79
|
+
target.okr_contribution_weight = value;
|
|
80
|
+
}
|
|
81
|
+
function putPositiveInt(target, key, raw, flag) {
|
|
82
|
+
const value = parseOptionalInt(raw, flag);
|
|
83
|
+
if (value !== undefined) {
|
|
84
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
85
|
+
throw new VertuError(`Invalid ${flag}: expected a positive integer.`);
|
|
86
|
+
}
|
|
87
|
+
target[key] = value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function putMeetingTodoSource(body, opts) {
|
|
91
|
+
const meetingId = opts.meetingId?.trim();
|
|
92
|
+
const rawIndex = opts.meetingTodoIndex?.trim();
|
|
93
|
+
if (!meetingId && !rawIndex)
|
|
94
|
+
return;
|
|
95
|
+
if (!meetingId) {
|
|
96
|
+
throw new VertuError('--meeting-todo-index requires --meeting-id.');
|
|
97
|
+
}
|
|
98
|
+
if (!rawIndex) {
|
|
99
|
+
throw new VertuError('--meeting-id requires --meeting-todo-index.');
|
|
100
|
+
}
|
|
101
|
+
const index = parseOptionalInt(rawIndex, '--meeting-todo-index');
|
|
102
|
+
if (index === undefined || index <= 0) {
|
|
103
|
+
throw new VertuError('Invalid --meeting-todo-index: expected a positive integer.');
|
|
104
|
+
}
|
|
105
|
+
body.source = 'ai';
|
|
106
|
+
body.source_ref = `meeting-minutes:${meetingId}:todo:${index - 1}`;
|
|
107
|
+
}
|
|
108
|
+
function taskRelation(raw) {
|
|
109
|
+
const value = raw?.trim() || 'all';
|
|
110
|
+
if (value !== 'all' && value !== 'created' && value !== 'assisting') {
|
|
111
|
+
throw new VertuError('Invalid --relation: expected all, created or assisting.');
|
|
112
|
+
}
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
function todoRelation(raw) {
|
|
116
|
+
const value = raw?.trim() || 'all';
|
|
117
|
+
if (value !== 'all' &&
|
|
118
|
+
value !== 'owned' &&
|
|
119
|
+
value !== 'created' &&
|
|
120
|
+
value !== 'following' &&
|
|
121
|
+
value !== 'assisting' &&
|
|
122
|
+
value !== 'completed') {
|
|
123
|
+
throw new VertuError('Invalid --relation: expected all, owned, created, following, assisting or completed.');
|
|
124
|
+
}
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
function departmentSection(raw) {
|
|
128
|
+
const value = raw?.trim() || 'all';
|
|
129
|
+
if (value === 'all' || value === 'projects' || value === 'tasks' || value === 'todos' || value === 'files') {
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
throw new VertuError('Invalid --section: expected all, projects, tasks, todos or files.');
|
|
133
|
+
}
|
|
134
|
+
async function allDepartmentItems(baseUrl, departmentId, section, pageSize, userId) {
|
|
135
|
+
const items = [];
|
|
136
|
+
let offset = 0;
|
|
137
|
+
let first = null;
|
|
138
|
+
while (true) {
|
|
139
|
+
const page = await imRequest('GET', '/v1/task-center/department-overview', {
|
|
140
|
+
baseUrl,
|
|
141
|
+
query: { department_id: departmentId, section, limit: pageSize, offset, user_id: userId },
|
|
142
|
+
});
|
|
143
|
+
first ??= page;
|
|
144
|
+
const rows = Array.isArray(page.items) ? page.items : [];
|
|
145
|
+
items.push(...rows);
|
|
146
|
+
if (!page.page?.has_more || rows.length === 0)
|
|
147
|
+
break;
|
|
148
|
+
offset += rows.length;
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
...(first ?? {}),
|
|
152
|
+
items,
|
|
153
|
+
page: {
|
|
154
|
+
total: first?.page?.total ?? items.length,
|
|
155
|
+
returned: items.length,
|
|
156
|
+
has_more: false,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function attachTaskCenterList(parent) {
|
|
161
|
+
attachSharedShortcutOptions(parent
|
|
162
|
+
.command('+tc-department-overview')
|
|
163
|
+
.description('任务中心:按部门管理员授权读取部门及子部门的项目、任务和待办')
|
|
164
|
+
.option('--department-id <id>', '授权部门 ID;省略时列出当前账号的部门授权')
|
|
165
|
+
.option('--user-id <id>', '集成 API Key 查询时指定员工 res.users ID;账号会话始终使用本人')
|
|
166
|
+
.option('--section <name>', 'all|projects|tasks|todos|files', 'all')
|
|
167
|
+
.option('--page-size <n>', '每次请求条数,1-200', '200')).action(async (opts, cmd) => {
|
|
168
|
+
const baseUrl = resolveBaseUrl(opts, cmd);
|
|
169
|
+
const userId = parseOptionalInt(opts.userId, '--user-id');
|
|
170
|
+
if (userId !== undefined && (!Number.isSafeInteger(userId) || userId <= 0)) {
|
|
171
|
+
throw new VertuError('Invalid --user-id: expected a positive integer.');
|
|
172
|
+
}
|
|
173
|
+
if (!opts.departmentId?.trim()) {
|
|
174
|
+
if (opts.section !== 'all' || opts.pageSize !== '200') {
|
|
175
|
+
throw new VertuError('--section and --page-size require --department-id.');
|
|
176
|
+
}
|
|
177
|
+
const query = { user_id: userId };
|
|
178
|
+
if (previewShortcutRequest(opts, cmd, 'GET', '/v1/task-center/department-overview', { query }))
|
|
179
|
+
return;
|
|
180
|
+
const result = await imRequest('GET', '/v1/task-center/department-overview', { baseUrl, query });
|
|
181
|
+
printImResult(opts.json, result);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const departmentId = parseOptionalInt(opts.departmentId, '--department-id');
|
|
185
|
+
if (departmentId === undefined || departmentId <= 0) {
|
|
186
|
+
throw new VertuError('Invalid --department-id: expected a positive integer.');
|
|
187
|
+
}
|
|
188
|
+
const pageSize = parseOptionalInt(opts.pageSize, '--page-size') ?? 200;
|
|
189
|
+
if (pageSize < 1 || pageSize > 200) {
|
|
190
|
+
throw new VertuError('Invalid --page-size: expected an integer from 1 to 200.');
|
|
191
|
+
}
|
|
192
|
+
const selected = departmentSection(opts.section);
|
|
193
|
+
const sections = selected === 'all'
|
|
194
|
+
? ['projects', 'tasks', 'todos']
|
|
195
|
+
: [selected];
|
|
196
|
+
if (previewShortcutRequest(opts, cmd, 'GET', '/v1/task-center/department-overview', {
|
|
197
|
+
query: {
|
|
198
|
+
department_id: departmentId,
|
|
199
|
+
sections,
|
|
200
|
+
limit: pageSize,
|
|
201
|
+
offset: 0,
|
|
202
|
+
user_id: userId,
|
|
203
|
+
},
|
|
204
|
+
}))
|
|
205
|
+
return;
|
|
206
|
+
const pages = await Promise.all(sections.map((section) => allDepartmentItems(baseUrl, departmentId, section, pageSize, userId)));
|
|
207
|
+
if (selected !== 'all') {
|
|
208
|
+
printImResult(opts.json, pages[0]);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
printImResult(opts.json, {
|
|
212
|
+
ok: true,
|
|
213
|
+
department: pages[0]?.department ?? null,
|
|
214
|
+
sections: Object.fromEntries(pages.map((page, index) => [sections[index], {
|
|
215
|
+
total: page.page?.total ?? page.items?.length ?? 0,
|
|
216
|
+
items: page.items ?? [],
|
|
217
|
+
}])),
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
attachSharedShortcutOptions(parent
|
|
221
|
+
.command('+tc-tasks')
|
|
222
|
+
.description('任务中心:我的任务列表(新任务助手,直连库)')
|
|
223
|
+
.option('--limit <n>', '最多返回条数', '200')
|
|
224
|
+
.option('--start-time <date>', '起始日期 YYYY-MM-DD(业务时区)')
|
|
225
|
+
.option('--end-time <date>', '结束日期 YYYY-MM-DD(含当天)')
|
|
226
|
+
.option('--date-field <field>', '日期字段:deadline、created_at、updated_at、completed_at、start_date')
|
|
227
|
+
.option('--no-include-department', '不扩大到部门范围')
|
|
228
|
+
.option('--offset <n>', '翻页位置(从 0 开始)')
|
|
229
|
+
.option('--relation <name>', 'all|created|assisting', 'all')
|
|
230
|
+
.option('--principal-user-id <id>', '仅查看指定被协助人的全部相关事项;需 relation=assisting')
|
|
231
|
+
.option('--owner-user-id <id>', '按负责人 res.users id 过滤')
|
|
232
|
+
.option('--project-id <id>', '按项目过滤(task-center 项目 UUID)')
|
|
233
|
+
.option('--okr-objective-id <id>', '按 OKR 目标过滤')
|
|
234
|
+
.option('--isolated', '仅未关联项目/OKR 的临时任务', false)
|
|
235
|
+
.option('--include-parents', '包含用于汇总进度的父任务', false)
|
|
236
|
+
.option('--include-closed', '包含已完成/已取消', false)).action(async (opts, cmd) => {
|
|
237
|
+
const query = {
|
|
238
|
+
limit: parseOptionalInt(opts.limit, '--limit') ?? 200,
|
|
239
|
+
relation: taskRelation(opts.relation),
|
|
240
|
+
};
|
|
241
|
+
putPositiveInt(query, 'principal_user_id', opts.principalUserId, '--principal-user-id');
|
|
242
|
+
if (query.principal_user_id !== undefined && query.relation !== 'assisting') {
|
|
243
|
+
throw new VertuError('--principal-user-id requires --relation assisting.');
|
|
244
|
+
}
|
|
245
|
+
putPositiveInt(query, 'owner_user_id', opts.ownerUserId, '--owner-user-id');
|
|
246
|
+
putString(query, 'project_id', opts.projectId);
|
|
247
|
+
putString(query, 'okr_objective_id', opts.okrObjectiveId);
|
|
248
|
+
if (opts.isolated) {
|
|
249
|
+
query.isolated = true;
|
|
250
|
+
}
|
|
251
|
+
if (opts.includeClosed) {
|
|
252
|
+
query.open_only = false;
|
|
253
|
+
}
|
|
254
|
+
if (opts.includeParents) {
|
|
255
|
+
query.include_parents = true;
|
|
256
|
+
}
|
|
257
|
+
applyListFilters(query, opts, 'tasks');
|
|
258
|
+
const result = await imRequest('GET', '/v1/task-center/tasks', {
|
|
259
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
260
|
+
query,
|
|
261
|
+
});
|
|
262
|
+
printImResult(opts.json, result);
|
|
263
|
+
});
|
|
264
|
+
attachSharedShortcutOptions(parent
|
|
265
|
+
.command('+tc-todos')
|
|
266
|
+
.description('任务中心:我的待办列表(新任务助手,直连库)')
|
|
267
|
+
.option('--limit <n>', '最多返回条数', '200')
|
|
268
|
+
.option('--start-time <date>', '起始日期 YYYY-MM-DD(业务时区)')
|
|
269
|
+
.option('--end-time <date>', '结束日期 YYYY-MM-DD(含当天)')
|
|
270
|
+
.option('--date-field <field>', '日期字段:deadline、created_at、updated_at、completed_at')
|
|
271
|
+
.option('--no-include-department', '不扩大到部门范围')
|
|
272
|
+
.option('--page <n>', '翻页位置(从 1 开始)')
|
|
273
|
+
.option('--relation <name>', 'all|owned|created|following|assisting|completed', 'all')
|
|
274
|
+
.option('--principal-user-id <id>', '仅查看指定被协助人的全部相关事项;需 relation=assisting')
|
|
275
|
+
.option('--owner-user-id <id>', '按负责人 res.users id 过滤')
|
|
276
|
+
.option('--include-closed', '包含已完成/已取消', false)).action(async (opts, cmd) => {
|
|
277
|
+
const query = {
|
|
278
|
+
limit: parseOptionalInt(opts.limit, '--limit') ?? 200,
|
|
279
|
+
relation: todoRelation(opts.relation),
|
|
280
|
+
};
|
|
281
|
+
putPositiveInt(query, 'principal_user_id', opts.principalUserId, '--principal-user-id');
|
|
282
|
+
if (query.principal_user_id !== undefined && query.relation !== 'assisting') {
|
|
283
|
+
throw new VertuError('--principal-user-id requires --relation assisting.');
|
|
284
|
+
}
|
|
285
|
+
putPositiveInt(query, 'owner_user_id', opts.ownerUserId, '--owner-user-id');
|
|
286
|
+
if (opts.includeClosed) {
|
|
287
|
+
query.open_only = false;
|
|
288
|
+
}
|
|
289
|
+
applyListFilters(query, opts, 'todos');
|
|
290
|
+
const result = await imRequest('GET', '/v1/task-center/todos', {
|
|
291
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
292
|
+
query,
|
|
293
|
+
});
|
|
294
|
+
printImResult(opts.json, result);
|
|
295
|
+
});
|
|
296
|
+
attachSharedShortcutOptions(parent
|
|
297
|
+
.command('+tc-assisted-users')
|
|
298
|
+
.description('任务中心:当前登录用户可协助的 PDCA 用户(只读授权上下文)')).action(async (opts, cmd) => {
|
|
299
|
+
const result = await imRequest('GET', '/v1/workbench/pdca/assistant-principals', {
|
|
300
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
301
|
+
});
|
|
302
|
+
printImResult(opts.json, result);
|
|
303
|
+
});
|
|
304
|
+
attachSharedShortcutOptions(parent.command('+tc-projects').description('任务中心:我参与的项目列表')).action(async (opts, cmd) => {
|
|
305
|
+
const result = await imRequest('GET', '/v1/task-center/projects', {
|
|
306
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
307
|
+
});
|
|
308
|
+
printImResult(opts.json, result);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
function attachTaskCenterTaskWrite(parent) {
|
|
312
|
+
attachSharedShortcutOptions(parent
|
|
313
|
+
.command('+tc-task-create')
|
|
314
|
+
.description('任务中心:创建任务')
|
|
315
|
+
.requiredOption('--title <text>', '任务标题')
|
|
316
|
+
.option('--description <text>', '任务描述')
|
|
317
|
+
.option('--status <name>', 'not_started|in_progress|paused|done|cancelled')
|
|
318
|
+
.option('--priority <n>', '0-3')
|
|
319
|
+
.option('--progress <n>', '0-100')
|
|
320
|
+
.option('--owner-user-id <id>', '负责人 res.users id(默认自己)')
|
|
321
|
+
.option('--project-id <id>', '所属项目 UUID')
|
|
322
|
+
.option('--start-date <ymd>', 'YYYY-MM-DD')
|
|
323
|
+
.option('--deadline <ymd>', 'YYYY-MM-DD')
|
|
324
|
+
.option('--okr-kr-id <id>', '关联正式 KR ID')
|
|
325
|
+
.option('--okr-contribution-weight <n>', '该任务对 KR 的贡献权重,(0, 100]')).action(async (opts, cmd) => {
|
|
326
|
+
const body = { title: requireNonEmpty(opts.title, '--title') };
|
|
327
|
+
putString(body, 'description', opts.description);
|
|
328
|
+
putString(body, 'status', opts.status);
|
|
329
|
+
putInt(body, 'priority', opts.priority, '--priority');
|
|
330
|
+
putInt(body, 'progress', opts.progress, '--progress');
|
|
331
|
+
putInt(body, 'owner_user_id', opts.ownerUserId, '--owner-user-id');
|
|
332
|
+
putString(body, 'project_id', opts.projectId);
|
|
333
|
+
putString(body, 'start_date', opts.startDate);
|
|
334
|
+
putString(body, 'deadline', opts.deadline);
|
|
335
|
+
putString(body, 'okr_kr_id', opts.okrKrId);
|
|
336
|
+
putOkrContributionWeight(body, opts.okrContributionWeight);
|
|
337
|
+
const merged = await mergeShortcutParams(body, opts);
|
|
338
|
+
if (previewShortcutRequest(opts, cmd, 'POST', '/v1/task-center/tasks', { body: merged }))
|
|
339
|
+
return;
|
|
340
|
+
const result = await imRequest('POST', '/v1/task-center/tasks', {
|
|
341
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
342
|
+
body: merged,
|
|
343
|
+
});
|
|
344
|
+
printImResult(opts.json, result);
|
|
345
|
+
});
|
|
346
|
+
attachSharedShortcutOptions(parent
|
|
347
|
+
.command('+tc-task-update')
|
|
348
|
+
.description('任务中心:更新任务(PATCH)')
|
|
349
|
+
.requiredOption('--task-id <id>', '任务 UUID')
|
|
350
|
+
.option('--title <text>', '标题')
|
|
351
|
+
.option('--description <text>', '描述')
|
|
352
|
+
.option('--status <name>', 'not_started|in_progress|paused|done|cancelled')
|
|
353
|
+
.option('--progress <n>', '0-100')
|
|
354
|
+
.option('--priority <n>', '0-3')
|
|
355
|
+
.option('--owner-user-id <id>', '负责人 res.users id')
|
|
356
|
+
.option('--activity-content <text>', '本次进展内容;更新进度时写入任务活动记录')
|
|
357
|
+
.option('--okr-kr-id <id>', '绑定到正式 KR ID')
|
|
358
|
+
.option('--okr-contribution-weight <n>', '该任务对 KR 的贡献权重,(0, 100]')
|
|
359
|
+
.option('--clear-okr', '解除当前正式 OKR/KR 关联', false)).action(async (opts, cmd) => {
|
|
360
|
+
const taskId = requireId(opts.taskId, '--task-id');
|
|
361
|
+
const body = {};
|
|
362
|
+
putString(body, 'title', opts.title);
|
|
363
|
+
putString(body, 'description', opts.description);
|
|
364
|
+
putString(body, 'status', opts.status);
|
|
365
|
+
putInt(body, 'progress', opts.progress, '--progress');
|
|
366
|
+
putInt(body, 'priority', opts.priority, '--priority');
|
|
367
|
+
putInt(body, 'owner_user_id', opts.ownerUserId, '--owner-user-id');
|
|
368
|
+
putString(body, 'activity_content', opts.activityContent);
|
|
369
|
+
if (opts.clearOkr && opts.okrKrId?.trim()) {
|
|
370
|
+
throw new VertuError('--clear-okr and --okr-kr-id cannot be used together.');
|
|
371
|
+
}
|
|
372
|
+
if (opts.clearOkr) {
|
|
373
|
+
body.okr_objective_id = null;
|
|
374
|
+
body.okr_kr_id = null;
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
putString(body, 'okr_kr_id', opts.okrKrId);
|
|
378
|
+
}
|
|
379
|
+
putOkrContributionWeight(body, opts.okrContributionWeight);
|
|
380
|
+
const merged = await mergeShortcutParams(body, opts);
|
|
381
|
+
const path = `/v1/task-center/tasks/${encodeURIComponent(taskId)}`;
|
|
382
|
+
if (previewShortcutRequest(opts, cmd, 'PATCH', path, { body: merged }))
|
|
383
|
+
return;
|
|
384
|
+
const result = await imRequest('PATCH', path, {
|
|
385
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
386
|
+
body: merged,
|
|
387
|
+
});
|
|
388
|
+
printImResult(opts.json, result);
|
|
389
|
+
});
|
|
390
|
+
attachSharedShortcutOptions(parent.command('+tc-task-delete')
|
|
391
|
+
.description('任务中心:归档任务(可恢复,不物理删除)')
|
|
392
|
+
.requiredOption('--task-id <id>', '任务 UUID')).action(async (opts, cmd) => {
|
|
393
|
+
rejectDeleteParams(opts);
|
|
394
|
+
const path = `/v1/task-center/tasks/${encodeURIComponent(requireId(opts.taskId, '--task-id'))}`;
|
|
395
|
+
const body = { status: 'archived' };
|
|
396
|
+
if (previewShortcutRequest(opts, cmd, 'PATCH', path, { body }))
|
|
397
|
+
return;
|
|
398
|
+
printImResult(opts.json, await imRequest('PATCH', path, {
|
|
399
|
+
baseUrl: resolveBaseUrl(opts, cmd), body,
|
|
400
|
+
}));
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
function attachTaskCenterTodoWrite(parent) {
|
|
404
|
+
attachSharedShortcutOptions(parent
|
|
405
|
+
.command('+tc-todo-create')
|
|
406
|
+
.description('任务中心:创建待办')
|
|
407
|
+
.requiredOption('--title <text>', '待办标题')
|
|
408
|
+
.option('--remark <text>', '备注')
|
|
409
|
+
.option('--priority <n>', '0-3')
|
|
410
|
+
.option('--owner-user-id <id>', '负责人 res.users id(默认自己)')
|
|
411
|
+
.option('--project-id <id>', '所属项目 UUID')
|
|
412
|
+
.option('--deadline <ymd>', 'YYYY-MM-DD')
|
|
413
|
+
.option('--meeting-id <id>', '关联会议/录音 ID(meeting +detail 的 id 或 minutes_id)')
|
|
414
|
+
.option('--meeting-todo-index <n>', '会议待办序号(从 1 开始;与 --meeting-id 同时使用)')).action(async (opts, cmd) => {
|
|
415
|
+
const body = { title: requireNonEmpty(opts.title, '--title') };
|
|
416
|
+
putString(body, 'remark', opts.remark);
|
|
417
|
+
putInt(body, 'priority', opts.priority, '--priority');
|
|
418
|
+
putInt(body, 'owner_user_id', opts.ownerUserId, '--owner-user-id');
|
|
419
|
+
putString(body, 'project_id', opts.projectId);
|
|
420
|
+
putString(body, 'deadline', opts.deadline);
|
|
421
|
+
putMeetingTodoSource(body, opts);
|
|
422
|
+
const merged = await mergeShortcutParams(body, opts);
|
|
423
|
+
const result = await imRequest('POST', '/v1/task-center/todos', {
|
|
424
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
425
|
+
body: merged,
|
|
426
|
+
});
|
|
427
|
+
printImResult(opts.json, result);
|
|
428
|
+
});
|
|
429
|
+
attachSharedShortcutOptions(parent
|
|
430
|
+
.command('+tc-todo-update')
|
|
431
|
+
.description('任务中心:更新待办(PATCH;完成用 --status done)')
|
|
432
|
+
.requiredOption('--todo-id <id>', '待办 UUID')
|
|
433
|
+
.option('--title <text>', '标题')
|
|
434
|
+
.option('--remark <text>', '备注')
|
|
435
|
+
.option('--status <name>', 'pending|in_progress|done|cancelled')
|
|
436
|
+
.option('--progress <n>', '0-100')
|
|
437
|
+
.option('--priority <n>', '0-3')).action(async (opts, cmd) => {
|
|
438
|
+
const todoId = requireId(opts.todoId, '--todo-id');
|
|
439
|
+
const body = {};
|
|
440
|
+
putString(body, 'title', opts.title);
|
|
441
|
+
putString(body, 'remark', opts.remark);
|
|
442
|
+
putString(body, 'status', opts.status);
|
|
443
|
+
putInt(body, 'progress', opts.progress, '--progress');
|
|
444
|
+
putInt(body, 'priority', opts.priority, '--priority');
|
|
445
|
+
const merged = await mergeShortcutParams(body, opts);
|
|
446
|
+
const result = await imRequest('PATCH', `/v1/task-center/todos/${encodeURIComponent(todoId)}`, {
|
|
447
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
448
|
+
body: merged,
|
|
449
|
+
});
|
|
450
|
+
printImResult(opts.json, result);
|
|
451
|
+
});
|
|
452
|
+
attachSharedShortcutOptions(parent.command('+tc-todo-delete')
|
|
453
|
+
.description('任务中心:归档待办(可恢复,不物理删除)')
|
|
454
|
+
.requiredOption('--todo-id <id>', '待办 UUID')).action(async (opts, cmd) => {
|
|
455
|
+
rejectDeleteParams(opts);
|
|
456
|
+
const path = `/v1/task-center/todos/${encodeURIComponent(requireId(opts.todoId, '--todo-id'))}`;
|
|
457
|
+
const body = { status: 'archived' };
|
|
458
|
+
if (previewShortcutRequest(opts, cmd, 'PATCH', path, { body }))
|
|
459
|
+
return;
|
|
460
|
+
printImResult(opts.json, await imRequest('PATCH', path, {
|
|
461
|
+
baseUrl: resolveBaseUrl(opts, cmd), body,
|
|
462
|
+
}));
|
|
463
|
+
});
|
|
464
|
+
attachSharedShortcutOptions(parent
|
|
465
|
+
.command('+tc-todo-convert')
|
|
466
|
+
.description('任务中心:待办转任务')
|
|
467
|
+
.requiredOption('--todo-id <id>', '待办 UUID')
|
|
468
|
+
.option('--title <text>', '覆盖任务标题')
|
|
469
|
+
.option('--project-id <id>', '关联项目 UUID')
|
|
470
|
+
.option('--deadline <ymd>', 'YYYY-MM-DD')
|
|
471
|
+
.option('--priority <n>', '0-3')).action(async (opts, cmd) => {
|
|
472
|
+
const todoId = requireId(opts.todoId, '--todo-id');
|
|
473
|
+
const body = {};
|
|
474
|
+
putString(body, 'title', opts.title);
|
|
475
|
+
putString(body, 'project_id', opts.projectId);
|
|
476
|
+
putString(body, 'deadline', opts.deadline);
|
|
477
|
+
putInt(body, 'priority', opts.priority, '--priority');
|
|
478
|
+
const merged = await mergeShortcutParams(body, opts);
|
|
479
|
+
const result = await imRequest('POST', `/v1/task-center/todos/${encodeURIComponent(todoId)}`, {
|
|
480
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
481
|
+
query: { action: 'convert' },
|
|
482
|
+
body: merged,
|
|
483
|
+
});
|
|
484
|
+
printImResult(opts.json, result);
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
function attachTaskCenterProjectAndOkr(parent) {
|
|
488
|
+
attachSharedShortcutOptions(parent
|
|
489
|
+
.command('+tc-okr-options')
|
|
490
|
+
.description('任务中心:列出当前用户可绑定的正式 OKR/KR')
|
|
491
|
+
.option('--period <ym>', 'YYYY-MM,省略=当前周期')
|
|
492
|
+
.option('--owner-type <person|department>', '限定个人或部门 OKR')
|
|
493
|
+
.option('--owner-id <id>', '与 --owner-type 配套的负责人或部门 ID')).action(async (opts, cmd) => {
|
|
494
|
+
const query = {};
|
|
495
|
+
putString(query, 'period', opts.period);
|
|
496
|
+
putString(query, 'owner_type', opts.ownerType);
|
|
497
|
+
putString(query, 'owner_id', opts.ownerId);
|
|
498
|
+
if (previewShortcutRequest(opts, cmd, 'GET', '/v1/task-center/okr-options', { query }))
|
|
499
|
+
return;
|
|
500
|
+
const result = await imRequest('GET', '/v1/task-center/okr-options', {
|
|
501
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
502
|
+
query,
|
|
503
|
+
});
|
|
504
|
+
printImResult(opts.json, result);
|
|
505
|
+
});
|
|
506
|
+
attachSharedShortcutOptions(parent
|
|
507
|
+
.command('+tc-project-create')
|
|
508
|
+
.description('任务中心:创建项目(创建人自动成为管理员)')
|
|
509
|
+
.requiredOption('--name <text>', '项目名称')
|
|
510
|
+
.option('--description <text>', '项目描述')
|
|
511
|
+
.option('--goal <text>', '项目目标')
|
|
512
|
+
.option('--priority <n>', '0-3')
|
|
513
|
+
.option('--owner-user-id <id>', '负责人 res.users id(默认自己)')
|
|
514
|
+
.option('--start-date <ymd>', 'YYYY-MM-DD')
|
|
515
|
+
.option('--deadline <ymd>', 'YYYY-MM-DD')).action(async (opts, cmd) => {
|
|
516
|
+
const body = { name: requireNonEmpty(opts.name, '--name') };
|
|
517
|
+
putString(body, 'description', opts.description);
|
|
518
|
+
putString(body, 'goal', opts.goal);
|
|
519
|
+
putInt(body, 'priority', opts.priority, '--priority');
|
|
520
|
+
putInt(body, 'owner_user_id', opts.ownerUserId, '--owner-user-id');
|
|
521
|
+
putString(body, 'start_date', opts.startDate);
|
|
522
|
+
putString(body, 'deadline', opts.deadline);
|
|
523
|
+
const merged = await mergeShortcutParams(body, opts);
|
|
524
|
+
const result = await imRequest('POST', '/v1/task-center/projects', {
|
|
525
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
526
|
+
body: merged,
|
|
527
|
+
});
|
|
528
|
+
printImResult(opts.json, result);
|
|
529
|
+
});
|
|
530
|
+
attachSharedShortcutOptions(parent.command('+tc-project-update')
|
|
531
|
+
.description('任务中心:更新项目(PATCH)')
|
|
532
|
+
.requiredOption('--project-id <id>', '项目 UUID')
|
|
533
|
+
.option('--name <text>', '项目名称')
|
|
534
|
+
.option('--project-type <text>', '项目类型')
|
|
535
|
+
.option('--description <text>', '项目描述')
|
|
536
|
+
.option('--goal <text>', '项目目标')
|
|
537
|
+
.option('--priority <n>', '0-3')
|
|
538
|
+
.option('--progress <n>', '0-100')
|
|
539
|
+
.option('--status <name>', 'not_started|in_progress|paused|completed|cancelled|archived')
|
|
540
|
+
.option('--owner-user-id <id>', '负责人 res.users id')
|
|
541
|
+
.option('--start-date <ymd>', 'YYYY-MM-DD')
|
|
542
|
+
.option('--deadline <ymd>', 'YYYY-MM-DD')).action(async (opts, cmd) => {
|
|
543
|
+
const path = `/v1/task-center/projects/${encodeURIComponent(requireId(opts.projectId, '--project-id'))}`;
|
|
544
|
+
const body = {};
|
|
545
|
+
putString(body, 'name', opts.name);
|
|
546
|
+
putString(body, 'project_type', opts.projectType);
|
|
547
|
+
putString(body, 'description', opts.description);
|
|
548
|
+
putString(body, 'goal', opts.goal);
|
|
549
|
+
putInt(body, 'priority', opts.priority, '--priority');
|
|
550
|
+
putInt(body, 'progress', opts.progress, '--progress');
|
|
551
|
+
putString(body, 'status', opts.status);
|
|
552
|
+
putPositiveInt(body, 'owner_user_id', opts.ownerUserId, '--owner-user-id');
|
|
553
|
+
putString(body, 'start_date', opts.startDate);
|
|
554
|
+
putString(body, 'deadline', opts.deadline);
|
|
555
|
+
const merged = await mergeShortcutParams(body, opts);
|
|
556
|
+
requireUpdateBody(merged);
|
|
557
|
+
if (previewShortcutRequest(opts, cmd, 'PATCH', path, { body: merged }))
|
|
558
|
+
return;
|
|
559
|
+
printImResult(opts.json, await imRequest('PATCH', path, {
|
|
560
|
+
baseUrl: resolveBaseUrl(opts, cmd), body: merged,
|
|
561
|
+
}));
|
|
562
|
+
});
|
|
563
|
+
attachSharedShortcutOptions(parent.command('+tc-project-delete')
|
|
564
|
+
.description('任务中心:归档项目(可恢复,不物理删除)')
|
|
565
|
+
.requiredOption('--project-id <id>', '项目 UUID')).action(async (opts, cmd) => {
|
|
566
|
+
rejectDeleteParams(opts);
|
|
567
|
+
const path = `/v1/task-center/projects/${encodeURIComponent(requireId(opts.projectId, '--project-id'))}`;
|
|
568
|
+
if (previewShortcutRequest(opts, cmd, 'DELETE', path))
|
|
569
|
+
return;
|
|
570
|
+
printImResult(opts.json, await imRequest('DELETE', path, {
|
|
571
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
572
|
+
}));
|
|
573
|
+
});
|
|
574
|
+
attachSharedShortcutOptions(parent
|
|
575
|
+
.command('+my-okr')
|
|
576
|
+
.description('任务助手:我的/团队 OKR(task-assistant,直连库)')
|
|
577
|
+
.option('--type <mine|team>', '默认 mine', 'mine')
|
|
578
|
+
.option('--period <ym>', 'YYYY-MM,省略=当前周期')).action(async (opts, cmd) => {
|
|
579
|
+
const type = opts.type?.trim() === 'team' ? 'team' : 'mine';
|
|
580
|
+
const query = { type };
|
|
581
|
+
putString(query, 'period', opts.period);
|
|
582
|
+
const result = await imRequest('GET', '/v1/task-assistant/okr', {
|
|
583
|
+
baseUrl: resolveBaseUrl(opts, cmd),
|
|
584
|
+
query,
|
|
585
|
+
});
|
|
586
|
+
printImResult(opts.json, result);
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
/** 新任务中心(task-center)+ 任务助手 OKR 子命令,统一挂在 `task` 命令下。 */
|
|
590
|
+
export function attachTaskCenterShortcuts(parent) {
|
|
591
|
+
attachTaskCenterList(parent);
|
|
592
|
+
attachTaskCenterTaskWrite(parent);
|
|
593
|
+
attachTaskCenterTodoWrite(parent);
|
|
594
|
+
attachTaskCenterProjectAndOkr(parent);
|
|
595
|
+
}
|