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,991 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { documentsRequest, printDocumentsResult } from '../../core/documents-client.js';
|
|
4
|
+
import { VertuError } from '../../core/errors.js';
|
|
5
|
+
import { imRequest } from '../../core/im-client.js';
|
|
6
|
+
import { parseJsonArg } from '../../utils/parse-json-arg.js';
|
|
7
|
+
import { readStdinText, readUtf8File } from '../../utils/read-text.js';
|
|
8
|
+
const DOCUMENT_BLOCK_TYPES = [
|
|
9
|
+
'paragraph',
|
|
10
|
+
'heading',
|
|
11
|
+
'numberedListItem',
|
|
12
|
+
'bulletListItem',
|
|
13
|
+
'checkListItem',
|
|
14
|
+
'codeBlock',
|
|
15
|
+
'quote',
|
|
16
|
+
];
|
|
17
|
+
const DOCUMENT_BLOCK_ALIGNMENTS = ['left', 'center', 'right'];
|
|
18
|
+
const DOCUMENT_BLOCK_BACKGROUNDS = ['default', 'yellow'];
|
|
19
|
+
function required(value, flag) {
|
|
20
|
+
const normalized = value?.trim();
|
|
21
|
+
if (!normalized) {
|
|
22
|
+
throw new VertuError(`${flag} 为必填。`);
|
|
23
|
+
}
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
/** 解析 --locked true/false。 */
|
|
27
|
+
function parseLockedFlag(value) {
|
|
28
|
+
const normalized = required(value, '--locked').toLowerCase();
|
|
29
|
+
if (normalized === 'true' || normalized === '1')
|
|
30
|
+
return true;
|
|
31
|
+
if (normalized === 'false' || normalized === '0')
|
|
32
|
+
return false;
|
|
33
|
+
throw new VertuError('--locked 必须是 true 或 false。');
|
|
34
|
+
}
|
|
35
|
+
/** 组装新建字段的 options;人员字段用 --multiple,单选/多选用 --choices。 */
|
|
36
|
+
function fieldOptions(opts) {
|
|
37
|
+
if (opts.type === 'member') {
|
|
38
|
+
return { isMultiple: opts.multiple === true };
|
|
39
|
+
}
|
|
40
|
+
if (opts.type === 'rating') {
|
|
41
|
+
return { icon: 'star', max: 5 };
|
|
42
|
+
}
|
|
43
|
+
if (opts.type === 'formula' || opts.type === 'rollup' || opts.type === 'conditionalRollup') {
|
|
44
|
+
return {
|
|
45
|
+
expression: required(opts.expression, '--expression'),
|
|
46
|
+
foreignTableId: opts.foreignTable,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (opts.type === 'link') {
|
|
50
|
+
return {
|
|
51
|
+
foreignTableId: required(opts.foreignTable, '--foreign-table'),
|
|
52
|
+
isOneWay: opts.oneWay === true,
|
|
53
|
+
relationship: opts.relationship ?? 'manyMany',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (opts.choices) {
|
|
57
|
+
return { choices: parseJsonArg(opts.choices, '--choices') };
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
function documentPath(docId, suffix = '') {
|
|
62
|
+
return `/v1/documents/${encodeURIComponent(docId)}${suffix}`;
|
|
63
|
+
}
|
|
64
|
+
/** 历史查询参数;字段和修改人用逗号分隔。 */
|
|
65
|
+
function historyQuery(opts) {
|
|
66
|
+
return {
|
|
67
|
+
createdByIds: opts.createdByIds,
|
|
68
|
+
cursor: opts.cursor,
|
|
69
|
+
endDate: opts.endDate,
|
|
70
|
+
fieldIds: opts.fieldIds,
|
|
71
|
+
mirrorId: opts.mirrorId,
|
|
72
|
+
startDate: opts.startDate,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function attachBaseOptions(command) {
|
|
76
|
+
return command
|
|
77
|
+
.option('--base-url <url>', 'Override Vertu service URL')
|
|
78
|
+
.option('--no-json', 'Print compact output instead of formatted JSON');
|
|
79
|
+
}
|
|
80
|
+
async function resolveContent(opts) {
|
|
81
|
+
if (opts.content !== undefined && opts.contentFile !== undefined) {
|
|
82
|
+
throw new VertuError('--content 和 --content-file 不能同时使用。');
|
|
83
|
+
}
|
|
84
|
+
if (opts.contentFile === '-') {
|
|
85
|
+
return readStdinText();
|
|
86
|
+
}
|
|
87
|
+
if (opts.contentFile !== undefined) {
|
|
88
|
+
return readUtf8File(opts.contentFile);
|
|
89
|
+
}
|
|
90
|
+
return opts.content;
|
|
91
|
+
}
|
|
92
|
+
async function resolveBlockPatch(opts) {
|
|
93
|
+
const hasJsonPatch = opts.patch !== undefined || opts.patchFile !== undefined;
|
|
94
|
+
const hasStructuredPatch = [opts.type, opts.level, opts.align, opts.background]
|
|
95
|
+
.some((value) => value !== undefined);
|
|
96
|
+
if (opts.patch !== undefined && opts.patchFile !== undefined) {
|
|
97
|
+
throw new VertuError('--patch 和 --patch-file 不能同时使用。');
|
|
98
|
+
}
|
|
99
|
+
if (hasJsonPatch && hasStructuredPatch) {
|
|
100
|
+
throw new VertuError('--patch/--patch-file 不能与 --type/--level/--align/--background 混用。');
|
|
101
|
+
}
|
|
102
|
+
if (hasJsonPatch) {
|
|
103
|
+
const raw = await resolveContent({ content: opts.patch, contentFile: opts.patchFile });
|
|
104
|
+
return parseJsonArg(raw ?? '', '--patch', {
|
|
105
|
+
fileFlagSuggestion: '--patch-file',
|
|
106
|
+
requireObject: true,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (!hasStructuredPatch) {
|
|
110
|
+
throw new VertuError('请提供结构化格式参数(--type/--level/--align/--background)或 --patch/--patch-file。');
|
|
111
|
+
}
|
|
112
|
+
const patch = {};
|
|
113
|
+
if (opts.type !== undefined) {
|
|
114
|
+
if (!DOCUMENT_BLOCK_TYPES.includes(opts.type)) {
|
|
115
|
+
throw new VertuError(`--type 需为 ${DOCUMENT_BLOCK_TYPES.join('、')}。`);
|
|
116
|
+
}
|
|
117
|
+
patch.type = opts.type;
|
|
118
|
+
}
|
|
119
|
+
const props = {};
|
|
120
|
+
if (opts.level !== undefined) {
|
|
121
|
+
if (!/^[1-5]$/.test(opts.level)) {
|
|
122
|
+
throw new VertuError('--level 需为 1 到 5 的整数。');
|
|
123
|
+
}
|
|
124
|
+
if (opts.type !== undefined && opts.type !== 'heading') {
|
|
125
|
+
throw new VertuError('--level 仅适用于 --type heading。');
|
|
126
|
+
}
|
|
127
|
+
props.level = Number(opts.level);
|
|
128
|
+
}
|
|
129
|
+
if (opts.align !== undefined) {
|
|
130
|
+
if (!DOCUMENT_BLOCK_ALIGNMENTS.includes(opts.align)) {
|
|
131
|
+
throw new VertuError(`--align 需为 ${DOCUMENT_BLOCK_ALIGNMENTS.join('、')}。`);
|
|
132
|
+
}
|
|
133
|
+
if (opts.type === 'codeBlock' || opts.type === 'quote') {
|
|
134
|
+
throw new VertuError(`--type ${opts.type} 不支持 --align。`);
|
|
135
|
+
}
|
|
136
|
+
props.textAlignment = opts.align;
|
|
137
|
+
}
|
|
138
|
+
if (opts.background !== undefined) {
|
|
139
|
+
if (!DOCUMENT_BLOCK_BACKGROUNDS.includes(opts.background)) {
|
|
140
|
+
throw new VertuError(`--background 需为 ${DOCUMENT_BLOCK_BACKGROUNDS.join('、')}。`);
|
|
141
|
+
}
|
|
142
|
+
if (opts.type === 'codeBlock') {
|
|
143
|
+
throw new VertuError('--type codeBlock 不支持 --background。');
|
|
144
|
+
}
|
|
145
|
+
props.backgroundColor = opts.background;
|
|
146
|
+
}
|
|
147
|
+
if (Object.keys(props).length > 0)
|
|
148
|
+
patch.props = props;
|
|
149
|
+
return patch;
|
|
150
|
+
}
|
|
151
|
+
function parseDocType(value) {
|
|
152
|
+
const normalized = value?.trim() || 'block';
|
|
153
|
+
if (normalized === 'block' || normalized === 'sheet' || normalized === 'folder' || normalized === 'base') {
|
|
154
|
+
return normalized;
|
|
155
|
+
}
|
|
156
|
+
throw new VertuError('--type 需为 block、sheet、folder 或 base。');
|
|
157
|
+
}
|
|
158
|
+
function parseScope(value) {
|
|
159
|
+
const normalized = value?.trim() || 'mine';
|
|
160
|
+
if (normalized === 'mine' || normalized === 'team' || normalized === 'trash') {
|
|
161
|
+
return normalized;
|
|
162
|
+
}
|
|
163
|
+
throw new VertuError('--scope 需为 mine、team 或 trash。');
|
|
164
|
+
}
|
|
165
|
+
function parseShareMode(value) {
|
|
166
|
+
const normalized = value?.trim();
|
|
167
|
+
if (normalized === 'private'
|
|
168
|
+
|| normalized === 'public-view'
|
|
169
|
+
|| normalized === 'public-edit'
|
|
170
|
+
|| normalized === 'login-view'
|
|
171
|
+
|| normalized === 'login-edit'
|
|
172
|
+
|| normalized === 'team-view'
|
|
173
|
+
|| normalized === 'team-edit') {
|
|
174
|
+
return normalized;
|
|
175
|
+
}
|
|
176
|
+
throw new VertuError('--mode 需为 private、public-view、public-edit、login-view、login-edit、team-view 或 team-edit。');
|
|
177
|
+
}
|
|
178
|
+
function parseSharePermission(value) {
|
|
179
|
+
const normalized = value?.trim() || 'request';
|
|
180
|
+
if (normalized === 'request' || normalized === 'view' || normalized === 'edit') {
|
|
181
|
+
return normalized;
|
|
182
|
+
}
|
|
183
|
+
throw new VertuError('--permission 需为 request、view 或 edit。');
|
|
184
|
+
}
|
|
185
|
+
function record(value, message) {
|
|
186
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
187
|
+
throw new VertuError(message);
|
|
188
|
+
}
|
|
189
|
+
return value;
|
|
190
|
+
}
|
|
191
|
+
function text(recordValue, ...keys) {
|
|
192
|
+
for (const key of keys) {
|
|
193
|
+
const value = recordValue[key];
|
|
194
|
+
if (typeof value === 'string' && value.trim()) {
|
|
195
|
+
return value.trim();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return '';
|
|
199
|
+
}
|
|
200
|
+
function documentType(value) {
|
|
201
|
+
return value === 'sheet' || value === 'folder' || value === 'html' || value === 'base' ? value : 'block';
|
|
202
|
+
}
|
|
203
|
+
function documentExcerpt(value) {
|
|
204
|
+
return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim().slice(0, 800) : '';
|
|
205
|
+
}
|
|
206
|
+
async function resolveSheetUpdates(opts) {
|
|
207
|
+
if (opts.updates !== undefined && opts.updatesFile !== undefined) {
|
|
208
|
+
throw new VertuError('--updates 和 --updates-file 不能同时使用。');
|
|
209
|
+
}
|
|
210
|
+
const raw = await resolveContent({
|
|
211
|
+
content: opts.updates,
|
|
212
|
+
contentFile: opts.updatesFile,
|
|
213
|
+
});
|
|
214
|
+
if (raw === undefined) {
|
|
215
|
+
throw new VertuError('请提供 --updates 或 --updates-file。');
|
|
216
|
+
}
|
|
217
|
+
let parsed;
|
|
218
|
+
try {
|
|
219
|
+
parsed = JSON.parse(raw);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
throw new VertuError('单元格更新必须是有效 JSON。');
|
|
223
|
+
}
|
|
224
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
225
|
+
throw new VertuError('单元格更新必须是非空 JSON 数组。');
|
|
226
|
+
}
|
|
227
|
+
return parsed;
|
|
228
|
+
}
|
|
229
|
+
/** 读取自动化 / Routine 快照 JSON。 */
|
|
230
|
+
async function resolveAutomationDraft(opts) {
|
|
231
|
+
const raw = await resolveContent({ content: opts.draft, contentFile: opts.draftFile });
|
|
232
|
+
if (raw === undefined) {
|
|
233
|
+
throw new VertuError('请提供 --draft 或 --draft-file。');
|
|
234
|
+
}
|
|
235
|
+
return parseJsonArg(raw, '--draft', {
|
|
236
|
+
fileFlagSuggestion: '--draft-file',
|
|
237
|
+
requireObject: true,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
async function resolveOptionalDraft(opts) {
|
|
241
|
+
if (opts.draft === undefined && opts.draftFile === undefined)
|
|
242
|
+
return undefined;
|
|
243
|
+
return resolveAutomationDraft(opts);
|
|
244
|
+
}
|
|
245
|
+
async function resolveJsonRecords(opts) {
|
|
246
|
+
if (opts.records !== undefined && opts.recordsFile !== undefined) {
|
|
247
|
+
throw new VertuError('--records 和 --records-file 不能同时使用。');
|
|
248
|
+
}
|
|
249
|
+
const raw = await resolveContent({ content: opts.records, contentFile: opts.recordsFile });
|
|
250
|
+
if (raw === undefined) {
|
|
251
|
+
throw new VertuError('请提供 --records 或 --records-file。');
|
|
252
|
+
}
|
|
253
|
+
const parsed = parseJsonArg(raw, '--records', { fileFlagSuggestion: '--records-file' });
|
|
254
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
255
|
+
throw new VertuError('记录必须是非空 JSON 数组。');
|
|
256
|
+
}
|
|
257
|
+
return parsed;
|
|
258
|
+
}
|
|
259
|
+
/** 解析逗号分隔的记录 ID;空值直接抛错。 */
|
|
260
|
+
function parseRecordIds(value) {
|
|
261
|
+
const ids = (value ?? '').split(',').map((item) => item.trim()).filter(Boolean);
|
|
262
|
+
if (ids.length === 0)
|
|
263
|
+
throw new VertuError('--ids 必须是非空的记录 ID 列表。');
|
|
264
|
+
return ids;
|
|
265
|
+
}
|
|
266
|
+
/** 组装 Teable 风格的视图锚点插入参数。 */
|
|
267
|
+
function recordOrder(opts) {
|
|
268
|
+
const position = required(opts.position, '--position');
|
|
269
|
+
if (position !== 'before' && position !== 'after') {
|
|
270
|
+
throw new VertuError('--position 必须是 before 或 after。');
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
anchorId: required(opts.anchorId, '--anchor-id'),
|
|
274
|
+
position,
|
|
275
|
+
viewId: required(opts.viewId, '--view-id'),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function positiveIntegerEnv(...keys) {
|
|
279
|
+
for (const key of keys) {
|
|
280
|
+
const raw = process.env[key]?.trim();
|
|
281
|
+
if (raw && /^\d+$/.test(raw)) {
|
|
282
|
+
const value = Number(raw);
|
|
283
|
+
if (Number.isSafeInteger(value) && value > 0) {
|
|
284
|
+
return value;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
function firstEnv(...keys) {
|
|
291
|
+
for (const key of keys) {
|
|
292
|
+
const value = process.env[key]?.trim();
|
|
293
|
+
if (value) {
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return '';
|
|
298
|
+
}
|
|
299
|
+
export function buildDocumentsCommand() {
|
|
300
|
+
const documents = new Command('docs').description('企业云文档:查询、读取、新建、更新、共享、复制与导出。');
|
|
301
|
+
attachBaseOptions(documents
|
|
302
|
+
.command('+list')
|
|
303
|
+
.description('列出当前用户可访问的云文档')
|
|
304
|
+
.option('--query <text>', '按标题搜索')
|
|
305
|
+
.option('--scope <scope>', 'mine / team / trash', 'mine')
|
|
306
|
+
.option('--team-id <id>', '团队范围内限定团队 ID')).action(async (opts) => {
|
|
307
|
+
const scope = parseScope(opts.scope);
|
|
308
|
+
const payload = await documentsRequest('GET', '/v1/documents', {
|
|
309
|
+
baseUrl: opts.baseUrl,
|
|
310
|
+
query: {
|
|
311
|
+
q: opts.query,
|
|
312
|
+
scope: scope === 'mine' ? undefined : scope,
|
|
313
|
+
teamId: opts.teamId,
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
printDocumentsResult(opts.json, payload);
|
|
317
|
+
});
|
|
318
|
+
attachBaseOptions(documents
|
|
319
|
+
.command('+get')
|
|
320
|
+
.description('读取文档详情与可提取正文')
|
|
321
|
+
.requiredOption('--doc-id <id>', '文档 ID')).action(async (opts) => {
|
|
322
|
+
const payload = await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id')), { baseUrl: opts.baseUrl });
|
|
323
|
+
printDocumentsResult(opts.json, payload);
|
|
324
|
+
});
|
|
325
|
+
attachBaseOptions(documents
|
|
326
|
+
.command('+create')
|
|
327
|
+
.description('新建文档、智能表格、多维表或文件夹')
|
|
328
|
+
.requiredOption('--title <title>', '标题')
|
|
329
|
+
.option('--type <type>', 'block / sheet / folder / base', 'block')
|
|
330
|
+
.option('--content <text>', '新建文档的 Markdown 初始正文')
|
|
331
|
+
.option('--content-file <path>', '从 UTF-8 文件读取初始正文;- 表示 stdin')
|
|
332
|
+
.option('--parent-id <id>', '父文件夹 ID')).action(async (opts) => {
|
|
333
|
+
const type = parseDocType(opts.type);
|
|
334
|
+
const content = await resolveContent(opts);
|
|
335
|
+
if (content !== undefined && type !== 'block') {
|
|
336
|
+
throw new VertuError('--content / --content-file 当前仅支持 block 文档。');
|
|
337
|
+
}
|
|
338
|
+
const payload = await documentsRequest('POST', '/v1/documents', {
|
|
339
|
+
baseUrl: opts.baseUrl,
|
|
340
|
+
body: {
|
|
341
|
+
title: required(opts.title, '--title'),
|
|
342
|
+
type,
|
|
343
|
+
parentId: opts.parentId?.trim() || null,
|
|
344
|
+
...(content === undefined
|
|
345
|
+
? {}
|
|
346
|
+
: { initialPayload: JSON.stringify({ format: 'markdown', content }) }),
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
printDocumentsResult(opts.json, payload);
|
|
350
|
+
});
|
|
351
|
+
attachBaseOptions(documents
|
|
352
|
+
.command('+rename')
|
|
353
|
+
.description('重命名文档、表格或文件夹')
|
|
354
|
+
.requiredOption('--doc-id <id>', '文档 ID')
|
|
355
|
+
.requiredOption('--title <title>', '新标题')).action(async (opts) => {
|
|
356
|
+
const payload = await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), '/rename'), {
|
|
357
|
+
baseUrl: opts.baseUrl,
|
|
358
|
+
body: { title: required(opts.title, '--title') },
|
|
359
|
+
});
|
|
360
|
+
printDocumentsResult(opts.json, payload);
|
|
361
|
+
});
|
|
362
|
+
attachBaseOptions(documents
|
|
363
|
+
.command('+set-content')
|
|
364
|
+
.description('仅为尚未进入实时协作的文档设置 Markdown 正文')
|
|
365
|
+
.requiredOption('--doc-id <id>', '文档 ID')
|
|
366
|
+
.option('--content <text>', 'Markdown 正文')
|
|
367
|
+
.option('--content-file <path>', '从 UTF-8 文件读取正文;- 表示 stdin')).action(async (opts) => {
|
|
368
|
+
const content = await resolveContent(opts);
|
|
369
|
+
if (content === undefined) {
|
|
370
|
+
throw new VertuError('请提供 --content 或 --content-file。');
|
|
371
|
+
}
|
|
372
|
+
const payload = await documentsRequest('PUT', documentPath(required(opts.docId, '--doc-id'), '/content'), { baseUrl: opts.baseUrl, body: { content } });
|
|
373
|
+
printDocumentsResult(opts.json, payload);
|
|
374
|
+
});
|
|
375
|
+
attachBaseOptions(documents
|
|
376
|
+
.command('+block-schema')
|
|
377
|
+
.description('返回 Agent 可读取的块类型、格式属性和并发规则')).action(async (opts) => {
|
|
378
|
+
const payload = await documentsRequest('GET', '/v1/documents/capabilities/blocks', { baseUrl: opts.baseUrl });
|
|
379
|
+
printDocumentsResult(opts.json, payload);
|
|
380
|
+
});
|
|
381
|
+
attachBaseOptions(documents
|
|
382
|
+
.command('+get-blocks')
|
|
383
|
+
.description('读取协作块文档的块 ID、格式属性和并发版本')
|
|
384
|
+
.requiredOption('--doc-id <id>', '文档 ID')).action(async (opts) => {
|
|
385
|
+
const payload = await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), '/blocks'), { baseUrl: opts.baseUrl });
|
|
386
|
+
printDocumentsResult(opts.json, payload);
|
|
387
|
+
});
|
|
388
|
+
const updateBlock = documents
|
|
389
|
+
.command('+update-block')
|
|
390
|
+
.description('按稳定 block ID 精确更新协作文档段落格式')
|
|
391
|
+
.requiredOption('--doc-id <id>', '文档 ID')
|
|
392
|
+
.requiredOption('--block-id <id>', '块 ID')
|
|
393
|
+
.option('--expected-version <version>', '由 +get-blocks 返回的并发版本')
|
|
394
|
+
.option('--type <type>', 'paragraph / heading / numberedListItem / bulletListItem / checkListItem / codeBlock / quote')
|
|
395
|
+
.option('--level <1-5>', '标题级别;仅适用于 heading')
|
|
396
|
+
.option('--align <alignment>', 'left / center / right')
|
|
397
|
+
.option('--background <color>', 'default / yellow')
|
|
398
|
+
.option('--patch <json>', '高级格式 patch JSON;不能与结构化格式参数混用')
|
|
399
|
+
.option('--patch-file <path>', '从 UTF-8 JSON 文件读取高级 patch;- 表示 stdin');
|
|
400
|
+
updateBlock.addHelpText('after', `
|
|
401
|
+
Workflow:
|
|
402
|
+
1. vps-work docs +block-schema
|
|
403
|
+
2. vps-work docs +get-blocks --doc-id <uuid>
|
|
404
|
+
3. Use blocks[].id and version in +update-block.
|
|
405
|
+
|
|
406
|
+
Examples:
|
|
407
|
+
$ vps-work docs +update-block --doc-id <uuid> --block-id <id> --expected-version <version> --type heading --level 2 --align center --background yellow
|
|
408
|
+
$ vps-work docs +update-block --doc-id <uuid> --block-id <id> --expected-version <version> --patch-file ./patch.json
|
|
409
|
+
|
|
410
|
+
Notes:
|
|
411
|
+
--level requires heading. quote/codeBlock do not support --align; codeBlock does not support --background.
|
|
412
|
+
On a version conflict, run +get-blocks again and verify the target block before retrying.
|
|
413
|
+
`);
|
|
414
|
+
attachBaseOptions(updateBlock).action(async (opts) => {
|
|
415
|
+
const docId = required(opts.docId, '--doc-id');
|
|
416
|
+
const blockId = required(opts.blockId, '--block-id');
|
|
417
|
+
const payload = await documentsRequest('PATCH', documentPath(docId, `/blocks/${encodeURIComponent(blockId)}`), {
|
|
418
|
+
baseUrl: opts.baseUrl,
|
|
419
|
+
body: {
|
|
420
|
+
expectedVersion: opts.expectedVersion?.trim() || undefined,
|
|
421
|
+
patch: await resolveBlockPatch(opts),
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
printDocumentsResult(opts.json, payload);
|
|
425
|
+
});
|
|
426
|
+
attachBaseOptions(documents
|
|
427
|
+
.command('+sheet-get')
|
|
428
|
+
.description('读取智能表格的当前工作簿数据')
|
|
429
|
+
.requiredOption('--doc-id <id>', '智能表格文档 ID')).action(async (opts) => {
|
|
430
|
+
const payload = await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), '/sheet'), { baseUrl: opts.baseUrl });
|
|
431
|
+
printDocumentsResult(opts.json, payload);
|
|
432
|
+
});
|
|
433
|
+
attachBaseOptions(documents
|
|
434
|
+
.command('+sheet-set-cells')
|
|
435
|
+
.description('批量更新智能表格单元格;value=null 表示清空')
|
|
436
|
+
.requiredOption('--doc-id <id>', '智能表格文档 ID')
|
|
437
|
+
.option('--sheet <id-or-name>', '工作表 ID 或名称;默认第一张工作表')
|
|
438
|
+
.option('--updates <json>', '单元格更新 JSON 数组')
|
|
439
|
+
.option('--updates-file <path>', '从 UTF-8 JSON 文件读取更新;- 表示 stdin')).action(async (opts) => {
|
|
440
|
+
const payload = await documentsRequest('PUT', documentPath(required(opts.docId, '--doc-id'), '/sheet'), {
|
|
441
|
+
baseUrl: opts.baseUrl,
|
|
442
|
+
body: {
|
|
443
|
+
cells: await resolveSheetUpdates(opts),
|
|
444
|
+
sheet: opts.sheet?.trim() || undefined,
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
printDocumentsResult(opts.json, payload);
|
|
448
|
+
});
|
|
449
|
+
attachBaseOptions(documents
|
|
450
|
+
.command('+base-schema')
|
|
451
|
+
.description('读取多维表的表、字段和视图')
|
|
452
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')).action(async (opts) => {
|
|
453
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), '/base'), {
|
|
454
|
+
baseUrl: opts.baseUrl,
|
|
455
|
+
}));
|
|
456
|
+
});
|
|
457
|
+
attachBaseOptions(documents
|
|
458
|
+
.command('+base-tables')
|
|
459
|
+
.description('列出多维表中的数据表')
|
|
460
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')).action(async (opts) => {
|
|
461
|
+
const payload = await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), '/base'), { baseUrl: opts.baseUrl });
|
|
462
|
+
printDocumentsResult(opts.json, { tables: payload.base?.tables ?? [] });
|
|
463
|
+
});
|
|
464
|
+
attachBaseOptions(documents
|
|
465
|
+
.command('+base-create-table')
|
|
466
|
+
.description('新建一张数据表')
|
|
467
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
468
|
+
.requiredOption('--name <name>', '数据表名称')).action(async (opts) => {
|
|
469
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), '/base/tables'), {
|
|
470
|
+
baseUrl: opts.baseUrl,
|
|
471
|
+
body: { name: required(opts.name, '--name') },
|
|
472
|
+
}));
|
|
473
|
+
});
|
|
474
|
+
attachBaseOptions(documents
|
|
475
|
+
.command('+base-records')
|
|
476
|
+
.description('读取多维表记录')
|
|
477
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
478
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
479
|
+
.option('--view-id <id>', '按视图过滤')
|
|
480
|
+
.option('--query <text>', '关键字搜索')
|
|
481
|
+
.option('--limit <n>', '返回条数')
|
|
482
|
+
.option('--archived', '只列归档记录')).action(async (opts) => {
|
|
483
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records`), {
|
|
484
|
+
baseUrl: opts.baseUrl,
|
|
485
|
+
query: {
|
|
486
|
+
archived: opts.archived ? '1' : undefined,
|
|
487
|
+
limit: opts.limit,
|
|
488
|
+
q: opts.query,
|
|
489
|
+
viewId: opts.viewId,
|
|
490
|
+
},
|
|
491
|
+
}));
|
|
492
|
+
});
|
|
493
|
+
attachBaseOptions(documents
|
|
494
|
+
.command('+base-create-records')
|
|
495
|
+
.description('批量新增记录,fields 使用字段 ID')
|
|
496
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
497
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
498
|
+
.option('--records <json>', '记录 JSON 数组')
|
|
499
|
+
.option('--records-file <path>', '从 UTF-8 JSON 文件读取记录;- 表示 stdin')
|
|
500
|
+
.option('--view-id <id>', '插入到指定视图锚点')
|
|
501
|
+
.option('--anchor-id <id>', '锚点记录 ID')
|
|
502
|
+
.option('--position <before|after>', '插到锚点前或后')).action(async (opts) => {
|
|
503
|
+
const order = opts.viewId || opts.anchorId || opts.position ? recordOrder(opts) : undefined;
|
|
504
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records`), {
|
|
505
|
+
baseUrl: opts.baseUrl,
|
|
506
|
+
body: { order, records: await resolveJsonRecords(opts) },
|
|
507
|
+
}));
|
|
508
|
+
});
|
|
509
|
+
attachBaseOptions(documents
|
|
510
|
+
.command('+base-update-records')
|
|
511
|
+
.description('批量更新记录,每项需含 id 和 fields')
|
|
512
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
513
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
514
|
+
.option('--records <json>', '更新 JSON 数组')
|
|
515
|
+
.option('--records-file <path>', '从 UTF-8 JSON 文件读取更新;- 表示 stdin')).action(async (opts) => {
|
|
516
|
+
printDocumentsResult(opts.json, await documentsRequest('PATCH', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records`), {
|
|
517
|
+
baseUrl: opts.baseUrl,
|
|
518
|
+
body: { records: await resolveJsonRecords(opts) },
|
|
519
|
+
}));
|
|
520
|
+
});
|
|
521
|
+
attachBaseOptions(documents
|
|
522
|
+
.command('+base-insert-records')
|
|
523
|
+
.description('在锚点前/后插入空记录')
|
|
524
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
525
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
526
|
+
.requiredOption('--view-id <id>', '视图 ID')
|
|
527
|
+
.requiredOption('--anchor-id <id>', '锚点记录 ID')
|
|
528
|
+
.requiredOption('--position <before|after>', '插到锚点前或后')
|
|
529
|
+
.option('--count <n>', '插入条数,1–1000,默认 1')).action(async (opts) => {
|
|
530
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records/insert`), {
|
|
531
|
+
baseUrl: opts.baseUrl,
|
|
532
|
+
body: { count: opts.count === undefined ? 1 : Number(opts.count), order: recordOrder(opts) },
|
|
533
|
+
}));
|
|
534
|
+
});
|
|
535
|
+
attachBaseOptions(documents
|
|
536
|
+
.command('+base-duplicate-records')
|
|
537
|
+
.description('复制记录并插到最后一条之后')
|
|
538
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
539
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
540
|
+
.requiredOption('--view-id <id>', '视图 ID')
|
|
541
|
+
.requiredOption('--ids <ids>', '逗号分隔的记录 ID')).action(async (opts) => {
|
|
542
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records/duplicate`), {
|
|
543
|
+
baseUrl: opts.baseUrl,
|
|
544
|
+
body: { ids: parseRecordIds(opts.ids), viewId: required(opts.viewId, '--view-id') },
|
|
545
|
+
}));
|
|
546
|
+
});
|
|
547
|
+
attachBaseOptions(documents
|
|
548
|
+
.command('+base-reorder-records')
|
|
549
|
+
.description('按当前视图把记录拖到 dropIndex')
|
|
550
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
551
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
552
|
+
.requiredOption('--view-id <id>', '视图 ID')
|
|
553
|
+
.requiredOption('--ids <ids>', '逗号分隔的记录 ID')
|
|
554
|
+
.requiredOption('--drop-index <n>', '目标位置')).action(async (opts) => {
|
|
555
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records/reorder`), {
|
|
556
|
+
baseUrl: opts.baseUrl,
|
|
557
|
+
body: {
|
|
558
|
+
dropIndex: Number(required(opts.dropIndex, '--drop-index')),
|
|
559
|
+
ids: parseRecordIds(opts.ids),
|
|
560
|
+
viewId: required(opts.viewId, '--view-id'),
|
|
561
|
+
},
|
|
562
|
+
}));
|
|
563
|
+
});
|
|
564
|
+
attachBaseOptions(documents
|
|
565
|
+
.command('+base-archive-records')
|
|
566
|
+
.description('归档记录')
|
|
567
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
568
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
569
|
+
.requiredOption('--ids <ids>', '逗号分隔的记录 ID')).action(async (opts) => {
|
|
570
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records/archive`), {
|
|
571
|
+
baseUrl: opts.baseUrl,
|
|
572
|
+
body: { ids: parseRecordIds(opts.ids) },
|
|
573
|
+
}));
|
|
574
|
+
});
|
|
575
|
+
attachBaseOptions(documents
|
|
576
|
+
.command('+base-restore-records')
|
|
577
|
+
.description('从归档区恢复记录')
|
|
578
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
579
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
580
|
+
.requiredOption('--ids <ids>', '逗号分隔的记录 ID')).action(async (opts) => {
|
|
581
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records/restore`), {
|
|
582
|
+
baseUrl: opts.baseUrl,
|
|
583
|
+
body: { ids: parseRecordIds(opts.ids) },
|
|
584
|
+
}));
|
|
585
|
+
});
|
|
586
|
+
attachBaseOptions(documents
|
|
587
|
+
.command('+base-create-field')
|
|
588
|
+
.description('新增字段')
|
|
589
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
590
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
591
|
+
.requiredOption('--name <name>', '字段名称')
|
|
592
|
+
.requiredOption('--type <type>', 'Teable 字段类型,如 singleLineText / rating / formula / link / rollup / conditionalRollup / autoNumber / createdTime')
|
|
593
|
+
.option('--choices <json>', '单选/多选选项 JSON 数组')
|
|
594
|
+
.option('--multiple', '人员字段允许多人')
|
|
595
|
+
.option('--expression <expr>', '公式或汇总表达式')
|
|
596
|
+
.option('--foreign-table <id>', '关联 / 条件汇总目标表')
|
|
597
|
+
.option('--relationship <rel>', '关联关系 oneOne / manyMany / oneMany / manyOne')
|
|
598
|
+
.option('--one-way', '单向关联,不创建对称字段')
|
|
599
|
+
.option('--lookup', '查找模式')
|
|
600
|
+
.option('--conditional-lookup', '条件查找模式')
|
|
601
|
+
.option('--lookup-options <json>', '查找 / 汇总 lookupOptions JSON')
|
|
602
|
+
.option('--view-id <id>', '插入到该视图的列序')
|
|
603
|
+
.option('--order <n>', '插入列的 columnMeta.order')).action(async (opts) => {
|
|
604
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/fields`), {
|
|
605
|
+
baseUrl: opts.baseUrl,
|
|
606
|
+
body: {
|
|
607
|
+
isConditionalLookup: opts.conditionalLookup === true || undefined,
|
|
608
|
+
isLookup: opts.lookup === true || opts.conditionalLookup === true || undefined,
|
|
609
|
+
lookupOptions: opts.lookupOptions
|
|
610
|
+
? parseJsonArg(opts.lookupOptions, '--lookup-options')
|
|
611
|
+
: undefined,
|
|
612
|
+
name: required(opts.name, '--name'),
|
|
613
|
+
options: fieldOptions(opts),
|
|
614
|
+
order: opts.order === undefined ? undefined : Number(opts.order),
|
|
615
|
+
type: required(opts.type, '--type'),
|
|
616
|
+
viewId: opts.viewId,
|
|
617
|
+
},
|
|
618
|
+
}));
|
|
619
|
+
});
|
|
620
|
+
attachBaseOptions(documents
|
|
621
|
+
.command('+base-views')
|
|
622
|
+
.description('列出数据表视图')
|
|
623
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
624
|
+
.requiredOption('--table <id>', '数据表 ID')).action(async (opts) => {
|
|
625
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/views`), { baseUrl: opts.baseUrl }));
|
|
626
|
+
});
|
|
627
|
+
attachBaseOptions(documents
|
|
628
|
+
.command('+base-create-view')
|
|
629
|
+
.description('新建只读视图定义')
|
|
630
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
631
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
632
|
+
.requiredOption('--name <name>', '视图名称')
|
|
633
|
+
.option('--type <type>', 'grid / kanban / form', 'grid')).action(async (opts) => {
|
|
634
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/views`), {
|
|
635
|
+
baseUrl: opts.baseUrl,
|
|
636
|
+
body: { name: required(opts.name, '--name'), type: opts.type ?? 'grid' },
|
|
637
|
+
}));
|
|
638
|
+
});
|
|
639
|
+
attachBaseOptions(documents
|
|
640
|
+
.command('+base-lock-view')
|
|
641
|
+
.description('锁定或解锁视图配置')
|
|
642
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
643
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
644
|
+
.requiredOption('--view-id <id>', '视图 ID')
|
|
645
|
+
.requiredOption('--locked <bool>', 'true 锁定 / false 解锁')).action(async (opts) => {
|
|
646
|
+
printDocumentsResult(opts.json, await documentsRequest('PATCH', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/views/${encodeURIComponent(required(opts.viewId, '--view-id'))}/locked`), {
|
|
647
|
+
baseUrl: opts.baseUrl,
|
|
648
|
+
body: { isLocked: parseLockedFlag(opts.locked) },
|
|
649
|
+
}));
|
|
650
|
+
});
|
|
651
|
+
attachBaseOptions(documents
|
|
652
|
+
.command('+base-update-view')
|
|
653
|
+
.description('更新视图配置,可设置冻结列')
|
|
654
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
655
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
656
|
+
.requiredOption('--view-id <id>', '视图 ID')
|
|
657
|
+
.option('--frozen-field-id <id>', '冻结至此字段')
|
|
658
|
+
.option('--config-file <path>', '完整视图 config JSON')).action(async (opts) => {
|
|
659
|
+
if (!opts.configFile && opts.frozenFieldId === undefined) {
|
|
660
|
+
throw new VertuError('请提供 --frozen-field-id 或 --config-file。');
|
|
661
|
+
}
|
|
662
|
+
const tablePath = `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}`;
|
|
663
|
+
const listed = await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), `${tablePath}/views`), { baseUrl: opts.baseUrl });
|
|
664
|
+
const current = listed.items?.find((item) => item.id === required(opts.viewId, '--view-id'));
|
|
665
|
+
if (!current)
|
|
666
|
+
throw new VertuError('找不到指定视图。');
|
|
667
|
+
const config = opts.configFile
|
|
668
|
+
? parseJsonArg(await readUtf8File(opts.configFile), '--config-file')
|
|
669
|
+
: { ...(current.config ?? {}) };
|
|
670
|
+
if (opts.frozenFieldId !== undefined) {
|
|
671
|
+
if (!opts.frozenFieldId.trim())
|
|
672
|
+
delete config.frozenFieldId;
|
|
673
|
+
else
|
|
674
|
+
config.frozenFieldId = opts.frozenFieldId.trim();
|
|
675
|
+
}
|
|
676
|
+
printDocumentsResult(opts.json, await documentsRequest('PATCH', documentPath(required(opts.docId, '--doc-id'), `${tablePath}/views/${encodeURIComponent(required(opts.viewId, '--view-id'))}`), { baseUrl: opts.baseUrl, body: { config } }));
|
|
677
|
+
});
|
|
678
|
+
attachBaseOptions(documents
|
|
679
|
+
.command('+base-create-mirror')
|
|
680
|
+
.description('从指定视图创建镜像表')
|
|
681
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
682
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
683
|
+
.requiredOption('--view-id <id>', '源视图 ID')
|
|
684
|
+
.requiredOption('--name <name>', '镜像表名称')).action(async (opts) => {
|
|
685
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/mirrors`), {
|
|
686
|
+
baseUrl: opts.baseUrl,
|
|
687
|
+
body: { name: required(opts.name, '--name'), viewId: required(opts.viewId, '--view-id') },
|
|
688
|
+
}));
|
|
689
|
+
});
|
|
690
|
+
attachBaseOptions(documents
|
|
691
|
+
.command('+base-rename-mirror')
|
|
692
|
+
.description('重命名镜像表')
|
|
693
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
694
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
695
|
+
.requiredOption('--mirror-id <id>', '镜像表 ID')
|
|
696
|
+
.requiredOption('--name <name>', '新名称')).action(async (opts) => {
|
|
697
|
+
printDocumentsResult(opts.json, await documentsRequest('PATCH', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/mirrors/${encodeURIComponent(required(opts.mirrorId, '--mirror-id'))}`), { baseUrl: opts.baseUrl, body: { name: required(opts.name, '--name') } }));
|
|
698
|
+
});
|
|
699
|
+
attachBaseOptions(documents
|
|
700
|
+
.command('+base-delete-mirror')
|
|
701
|
+
.description('删除镜像表')
|
|
702
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
703
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
704
|
+
.requiredOption('--mirror-id <id>', '镜像表 ID')).action(async (opts) => {
|
|
705
|
+
printDocumentsResult(opts.json, await documentsRequest('DELETE', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/mirrors/${encodeURIComponent(required(opts.mirrorId, '--mirror-id'))}`), { baseUrl: opts.baseUrl }));
|
|
706
|
+
});
|
|
707
|
+
attachBaseOptions(documents
|
|
708
|
+
.command('+base-set-mirror-grants')
|
|
709
|
+
.description('覆盖镜像表通讯录授权')
|
|
710
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
711
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
712
|
+
.requiredOption('--mirror-id <id>', '镜像表 ID')
|
|
713
|
+
.requiredOption('--grants-file <path>', '授权 JSON 数组')).action(async (opts) => {
|
|
714
|
+
printDocumentsResult(opts.json, await documentsRequest('PUT', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/mirrors/${encodeURIComponent(required(opts.mirrorId, '--mirror-id'))}/grants`), {
|
|
715
|
+
baseUrl: opts.baseUrl,
|
|
716
|
+
body: { grants: parseJsonArg(await readUtf8File(required(opts.grantsFile, '--grants-file')), '--grants-file') },
|
|
717
|
+
}));
|
|
718
|
+
});
|
|
719
|
+
attachBaseOptions(documents
|
|
720
|
+
.command('+base-record-history')
|
|
721
|
+
.description('读取一条记录的字段变更历史')
|
|
722
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
723
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
724
|
+
.requiredOption('--record <id>', '记录 ID')
|
|
725
|
+
.option('--start-date <date>', '起始日期')
|
|
726
|
+
.option('--end-date <date>', '结束日期')
|
|
727
|
+
.option('--field-ids <ids>', '字段 ID,逗号分隔')
|
|
728
|
+
.option('--created-by-ids <ids>', '修改人 ID,逗号分隔')
|
|
729
|
+
.option('--cursor <cursor>', '分页游标')
|
|
730
|
+
.option('--mirror-id <id>', '镜像表 ID')).action(async (opts) => {
|
|
731
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records/${encodeURIComponent(required(opts.record, '--record'))}/history`), { baseUrl: opts.baseUrl, query: historyQuery(opts) }));
|
|
732
|
+
});
|
|
733
|
+
attachBaseOptions(documents
|
|
734
|
+
.command('+base-table-history')
|
|
735
|
+
.description('读取整表字段变更历史')
|
|
736
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
737
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
738
|
+
.option('--start-date <date>', '起始日期')
|
|
739
|
+
.option('--end-date <date>', '结束日期')
|
|
740
|
+
.option('--field-ids <ids>', '字段 ID,逗号分隔')
|
|
741
|
+
.option('--created-by-ids <ids>', '修改人 ID,逗号分隔')
|
|
742
|
+
.option('--cursor <cursor>', '分页游标')
|
|
743
|
+
.option('--mirror-id <id>', '镜像表 ID')).action(async (opts) => {
|
|
744
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records/history`), { baseUrl: opts.baseUrl, query: historyQuery(opts) }));
|
|
745
|
+
});
|
|
746
|
+
attachBaseOptions(documents
|
|
747
|
+
.command('+base-import')
|
|
748
|
+
.description('从 CSV 导入记录')
|
|
749
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
750
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
751
|
+
.option('--csv <text>', 'CSV 文本')
|
|
752
|
+
.option('--csv-file <path>', '从 UTF-8 文件读取 CSV;- 表示 stdin')).action(async (opts) => {
|
|
753
|
+
const csv = await resolveContent({ content: opts.csv, contentFile: opts.csvFile });
|
|
754
|
+
if (csv === undefined) {
|
|
755
|
+
throw new VertuError('请提供 --csv 或 --csv-file。');
|
|
756
|
+
}
|
|
757
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/import`), { baseUrl: opts.baseUrl, body: { csv, hasHeader: true } }));
|
|
758
|
+
});
|
|
759
|
+
attachBaseOptions(documents
|
|
760
|
+
.command('+base-export')
|
|
761
|
+
.description('导出当前视图或全表为 CSV')
|
|
762
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
763
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
764
|
+
.option('--view-id <id>', '按视图导出')).action(async (opts) => {
|
|
765
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/export`), { baseUrl: opts.baseUrl, query: { viewId: opts.viewId } }));
|
|
766
|
+
});
|
|
767
|
+
attachBaseOptions(documents
|
|
768
|
+
.command('+base-workflows')
|
|
769
|
+
.description('列出多维表自动化工作流')
|
|
770
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')).action(async (opts) => {
|
|
771
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), '/base/workflows'), {
|
|
772
|
+
baseUrl: opts.baseUrl,
|
|
773
|
+
}));
|
|
774
|
+
});
|
|
775
|
+
attachBaseOptions(documents
|
|
776
|
+
.command('+base-create-workflow')
|
|
777
|
+
.description('创建自动化工作流草稿')
|
|
778
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
779
|
+
.requiredOption('--name <name>', '工作流名称')
|
|
780
|
+
.option('--draft <json>', '工作流快照 JSON')
|
|
781
|
+
.option('--draft-file <path>', '从 UTF-8 JSON 文件读取快照;- 表示 stdin')).action(async (opts) => {
|
|
782
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), '/base/workflows'), {
|
|
783
|
+
baseUrl: opts.baseUrl,
|
|
784
|
+
body: { draft: await resolveAutomationDraft(opts), name: required(opts.name, '--name') },
|
|
785
|
+
}));
|
|
786
|
+
});
|
|
787
|
+
attachBaseOptions(documents
|
|
788
|
+
.command('+base-update-workflow')
|
|
789
|
+
.description('更新自动化工作流草稿')
|
|
790
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
791
|
+
.requiredOption('--workflow-id <id>', '工作流 ID')
|
|
792
|
+
.option('--name <name>', '工作流名称')
|
|
793
|
+
.option('--draft <json>', '工作流快照 JSON')
|
|
794
|
+
.option('--draft-file <path>', '从 UTF-8 JSON 文件读取快照;- 表示 stdin')).action(async (opts) => {
|
|
795
|
+
printDocumentsResult(opts.json, await documentsRequest('PATCH', documentPath(required(opts.docId, '--doc-id'), `/base/workflows/${encodeURIComponent(required(opts.workflowId, '--workflow-id'))}`), {
|
|
796
|
+
baseUrl: opts.baseUrl,
|
|
797
|
+
body: {
|
|
798
|
+
draft: await resolveOptionalDraft(opts),
|
|
799
|
+
name: opts.name,
|
|
800
|
+
},
|
|
801
|
+
}));
|
|
802
|
+
});
|
|
803
|
+
attachBaseOptions(documents
|
|
804
|
+
.command('+base-apply-workflow')
|
|
805
|
+
.description('发布并启用自动化工作流')
|
|
806
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
807
|
+
.requiredOption('--workflow-id <id>', '工作流 ID')).action(async (opts) => {
|
|
808
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/workflows/${encodeURIComponent(required(opts.workflowId, '--workflow-id'))}/apply`), { baseUrl: opts.baseUrl }));
|
|
809
|
+
});
|
|
810
|
+
attachBaseOptions(documents
|
|
811
|
+
.command('+base-workflow-runs')
|
|
812
|
+
.description('查看自动化运行历史')
|
|
813
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
814
|
+
.requiredOption('--workflow-id <id>', '工作流 ID')).action(async (opts) => {
|
|
815
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), `/base/workflows/${encodeURIComponent(required(opts.workflowId, '--workflow-id'))}/runs`), { baseUrl: opts.baseUrl }));
|
|
816
|
+
});
|
|
817
|
+
attachBaseOptions(documents
|
|
818
|
+
.command('+base-rerun-workflow')
|
|
819
|
+
.description('重跑失败的自动化')
|
|
820
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
821
|
+
.requiredOption('--workflow-id <id>', '工作流 ID')
|
|
822
|
+
.requiredOption('--run-id <id>', '运行 ID')
|
|
823
|
+
.option('--mode <mode>', 'full / resumeFromFailedStep', 'full')).action(async (opts) => {
|
|
824
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/workflows/${encodeURIComponent(required(opts.workflowId, '--workflow-id'))}/runs/${encodeURIComponent(required(opts.runId, '--run-id'))}/rerun`), { baseUrl: opts.baseUrl, body: { mode: opts.mode === 'resumeFromFailedStep' ? 'resumeFromFailedStep' : 'full' } }));
|
|
825
|
+
});
|
|
826
|
+
attachBaseOptions(documents
|
|
827
|
+
.command('+base-routines')
|
|
828
|
+
.description('列出多维表 Routine')
|
|
829
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')).action(async (opts) => {
|
|
830
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), '/base/routines'), {
|
|
831
|
+
baseUrl: opts.baseUrl,
|
|
832
|
+
}));
|
|
833
|
+
});
|
|
834
|
+
attachBaseOptions(documents
|
|
835
|
+
.command('+base-create-routine')
|
|
836
|
+
.description('创建 Routine 草稿')
|
|
837
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
838
|
+
.requiredOption('--name <name>', 'Routine 名称')
|
|
839
|
+
.option('--draft <json>', 'Routine 快照 JSON')
|
|
840
|
+
.option('--draft-file <path>', '从 UTF-8 JSON 文件读取快照;- 表示 stdin')).action(async (opts) => {
|
|
841
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), '/base/routines'), {
|
|
842
|
+
baseUrl: opts.baseUrl,
|
|
843
|
+
body: { draft: await resolveAutomationDraft(opts), name: required(opts.name, '--name') },
|
|
844
|
+
}));
|
|
845
|
+
});
|
|
846
|
+
attachBaseOptions(documents
|
|
847
|
+
.command('+base-apply-routine')
|
|
848
|
+
.description('发布并启用 Routine')
|
|
849
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
850
|
+
.requiredOption('--routine-id <id>', 'Routine ID')).action(async (opts) => {
|
|
851
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/routines/${encodeURIComponent(required(opts.routineId, '--routine-id'))}/apply`), { baseUrl: opts.baseUrl }));
|
|
852
|
+
});
|
|
853
|
+
attachBaseOptions(documents
|
|
854
|
+
.command('+base-run-routine')
|
|
855
|
+
.description('立即运行 Routine')
|
|
856
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
857
|
+
.requiredOption('--routine-id <id>', 'Routine ID')).action(async (opts) => {
|
|
858
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/routines/${encodeURIComponent(required(opts.routineId, '--routine-id'))}/run`), { baseUrl: opts.baseUrl }));
|
|
859
|
+
});
|
|
860
|
+
attachBaseOptions(documents
|
|
861
|
+
.command('+base-secrets')
|
|
862
|
+
.description('列出自动化密钥名称')
|
|
863
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')).action(async (opts) => {
|
|
864
|
+
printDocumentsResult(opts.json, await documentsRequest('GET', documentPath(required(opts.docId, '--doc-id'), '/base/secrets'), {
|
|
865
|
+
baseUrl: opts.baseUrl,
|
|
866
|
+
}));
|
|
867
|
+
});
|
|
868
|
+
attachBaseOptions(documents
|
|
869
|
+
.command('+base-put-secret')
|
|
870
|
+
.description('写入自动化密钥')
|
|
871
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
872
|
+
.requiredOption('--name <name>', '密钥名')
|
|
873
|
+
.requiredOption('--value <value>', '密钥值')).action(async (opts) => {
|
|
874
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), '/base/secrets'), {
|
|
875
|
+
baseUrl: opts.baseUrl,
|
|
876
|
+
body: { name: required(opts.name, '--name'), value: required(opts.value, '--value') },
|
|
877
|
+
}));
|
|
878
|
+
});
|
|
879
|
+
attachBaseOptions(documents
|
|
880
|
+
.command('+base-click-button')
|
|
881
|
+
.description('点击多维表按钮字段并触发自动化')
|
|
882
|
+
.requiredOption('--doc-id <id>', '多维表文档 ID')
|
|
883
|
+
.requiredOption('--table <id>', '数据表 ID')
|
|
884
|
+
.requiredOption('--record <id>', '记录 ID')
|
|
885
|
+
.requiredOption('--field-id <id>', '按钮字段 ID')).action(async (opts) => {
|
|
886
|
+
printDocumentsResult(opts.json, await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), `/base/tables/${encodeURIComponent(required(opts.table, '--table'))}/records/${encodeURIComponent(required(opts.record, '--record'))}/fields/${encodeURIComponent(required(opts.fieldId, '--field-id'))}/click`), { baseUrl: opts.baseUrl }));
|
|
887
|
+
});
|
|
888
|
+
attachBaseOptions(documents
|
|
889
|
+
.command('+share')
|
|
890
|
+
.description('设置链接或企业内部共享权限')
|
|
891
|
+
.requiredOption('--doc-id <id>', '文档 ID')
|
|
892
|
+
.requiredOption('--mode <mode>', 'private / public-view / public-edit / login-view / login-edit / team-view / team-edit')).action(async (opts) => {
|
|
893
|
+
const payload = await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), '/share'), {
|
|
894
|
+
baseUrl: opts.baseUrl,
|
|
895
|
+
body: { mode: parseShareMode(opts.mode) },
|
|
896
|
+
});
|
|
897
|
+
printDocumentsResult(opts.json, payload);
|
|
898
|
+
});
|
|
899
|
+
attachBaseOptions(documents
|
|
900
|
+
.command('+send')
|
|
901
|
+
.description('把云文档卡片发送到当前 Hermes/VPS IM 会话')
|
|
902
|
+
.requiredOption('--doc-id <id>', '文档 ID')
|
|
903
|
+
.option('--permission <permission>', 'request / view / edit', 'request')
|
|
904
|
+
.option('--channel-id <id>', '目标 IM 会话 id;Hermes 中默认使用当前会话')).action(async (opts) => {
|
|
905
|
+
const docId = required(opts.docId, '--doc-id');
|
|
906
|
+
const permission = parseSharePermission(opts.permission);
|
|
907
|
+
const detail = record(await documentsRequest('GET', documentPath(docId), { baseUrl: opts.baseUrl }), '文档详情响应无效。');
|
|
908
|
+
const doc = record(detail.doc, '文档详情缺少 doc。');
|
|
909
|
+
const title = text(doc, 'title') || '未命名文档';
|
|
910
|
+
const type = documentType(doc.type);
|
|
911
|
+
let shareToken = text(doc, 'shareToken', 'share_token');
|
|
912
|
+
let shareMode = text(doc, 'shareMode', 'share_mode') || null;
|
|
913
|
+
if (permission !== 'request') {
|
|
914
|
+
shareMode = permission === 'edit' ? 'login-edit' : 'login-view';
|
|
915
|
+
const share = record(await documentsRequest('POST', documentPath(docId, '/share'), {
|
|
916
|
+
baseUrl: opts.baseUrl,
|
|
917
|
+
body: { mode: shareMode },
|
|
918
|
+
}), '文档共享响应无效。');
|
|
919
|
+
shareToken = text(share, 'token') || shareToken;
|
|
920
|
+
}
|
|
921
|
+
const channelId = opts.channelId?.trim() || firstEnv('VERTU_AGENT_CHANNEL_ID');
|
|
922
|
+
const agentSlug = firstEnv('VERTU_AGENT_SLUG', 'HERMES_AGENT_SLUG');
|
|
923
|
+
const agentName = firstEnv('VERTU_AGENT_NAME', 'VERTU_AGENT_PROFILE_NAME');
|
|
924
|
+
const sessionId = firstEnv('VERTU_AGENT_SESSION_ID', 'HERMES_SESSION_ID');
|
|
925
|
+
const userId = positiveIntegerEnv('VERTU_AGENT_USER_ID', 'VERTU_AGENT_ODOO_USER_ID');
|
|
926
|
+
if (!channelId && !agentSlug) {
|
|
927
|
+
throw new VertuError('无法确定目标会话:请在 Hermes 当前会话中执行,或提供 --channel-id。');
|
|
928
|
+
}
|
|
929
|
+
const clientMessageId = `docs-share-${randomUUID()}`;
|
|
930
|
+
const metadata = {
|
|
931
|
+
card: 'document_share',
|
|
932
|
+
document_id: docId,
|
|
933
|
+
title,
|
|
934
|
+
creator_name: text(doc, 'creatorName', 'creator_name') || '文档成员',
|
|
935
|
+
share_url: shareToken ? `vertu://documents/share/${shareToken}` : '',
|
|
936
|
+
excerpt: documentExcerpt(doc.content),
|
|
937
|
+
document_type: type,
|
|
938
|
+
permission,
|
|
939
|
+
};
|
|
940
|
+
const message = {
|
|
941
|
+
body: `[文档] ${title}`,
|
|
942
|
+
message_type: 'interactive_card',
|
|
943
|
+
client_message_id: clientMessageId,
|
|
944
|
+
idempotency_key: clientMessageId,
|
|
945
|
+
metadata,
|
|
946
|
+
};
|
|
947
|
+
const delivery = agentSlug
|
|
948
|
+
? await imRequest('POST', '/v1/im/agents/hermes/notify', {
|
|
949
|
+
baseUrl: opts.baseUrl,
|
|
950
|
+
body: {
|
|
951
|
+
...message,
|
|
952
|
+
agent_slug: agentSlug,
|
|
953
|
+
...(agentName ? { agent_name: agentName } : {}),
|
|
954
|
+
...(sessionId ? { session_id: sessionId } : {}),
|
|
955
|
+
...(channelId ? { channel_id: channelId } : {}),
|
|
956
|
+
...(userId ? { user_id: userId } : {}),
|
|
957
|
+
event_id: clientMessageId,
|
|
958
|
+
},
|
|
959
|
+
})
|
|
960
|
+
: await imRequest('POST', `/v1/im/channels/${encodeURIComponent(channelId)}/messages`, { baseUrl: opts.baseUrl, body: message });
|
|
961
|
+
const deliveryRecord = record(delivery, 'IM 投递响应无效。');
|
|
962
|
+
printDocumentsResult(opts.json, {
|
|
963
|
+
ok: true,
|
|
964
|
+
document: { id: docId, title, type },
|
|
965
|
+
permission,
|
|
966
|
+
share_mode: shareMode,
|
|
967
|
+
channel_id: text(deliveryRecord, 'channel_id') || channelId || null,
|
|
968
|
+
message: deliveryRecord.message ?? null,
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
for (const [name, description, suffix] of [
|
|
972
|
+
['+duplicate', '复制文档或表格', '/duplicate'],
|
|
973
|
+
['+export', '导出文档原始数据与协作状态', '/export'],
|
|
974
|
+
['+export-markdown', '将文档导出为 Markdown 文本', '/export-markdown'],
|
|
975
|
+
]) {
|
|
976
|
+
attachBaseOptions(documents
|
|
977
|
+
.command(name)
|
|
978
|
+
.description(description)
|
|
979
|
+
.requiredOption('--doc-id <id>', '文档 ID')).action(async (opts) => {
|
|
980
|
+
const payload = await documentsRequest('POST', documentPath(required(opts.docId, '--doc-id'), suffix), { baseUrl: opts.baseUrl });
|
|
981
|
+
printDocumentsResult(opts.json, payload);
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
documents.addHelpText('after', `
|
|
985
|
+
Agent block-format workflow:
|
|
986
|
+
vps-work docs +block-schema
|
|
987
|
+
vps-work docs +get-blocks --doc-id <uuid>
|
|
988
|
+
vps-work docs +update-block --doc-id <uuid> --block-id <id> --expected-version <version> --type heading --level 2 --align center
|
|
989
|
+
`);
|
|
990
|
+
return documents;
|
|
991
|
+
}
|