openyida 2026.5.19 → 2026.5.20

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/README.md CHANGED
@@ -200,6 +200,24 @@ openyida data create form APP_XXX FORM_XXX --data-file .cache/openyida/data-impo
200
200
  openyida get-permission APP_XXX FORM_XXX
201
201
  ```
202
202
 
203
+ `configure-process` 的流程 JSON 中,审批人可配置为发起人、指定成员、指定角色、部门主管或直属主管,例如:
204
+
205
+ ```json
206
+ {
207
+ "nodes": [
208
+ {
209
+ "type": "approval",
210
+ "name": "主管审批",
211
+ "approver": {
212
+ "type": "user",
213
+ "users": [{ "id": "manager7350", "name": "九神" }],
214
+ "multiApproverType": "all"
215
+ }
216
+ }
217
+ ]
218
+ }
219
+ ```
220
+
203
221
  When creating or updating test data with `openyida data`, Yida date fields must use 13-digit millisecond timestamps, for example `"dateField_xxx": 1719705600000`. Do not submit `YYYY-MM-DD` strings for `DateField` or `CascadeDateField` values.
204
222
  Temporary JSON, CSV, and one-off import scripts should live under `.cache/openyida/` so generated run artifacts do not clutter the repository root.
205
223
 
@@ -279,6 +297,7 @@ For overseas apps, pass `--locale en_US` or `--locale ja_JP` on creation command
279
297
  | `openyida corp-efficiency [overview\|details\|detail\|groups\|notify] [options] [--open\|--no-open]` | Query enterprise efficiency metrics, detail report entries, and related notification actions |
280
298
  | `openyida create-app "<name>"\|--name <name> [options] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create an application and output `appType` |
281
299
  | `openyida update-app <appType> --name "..."` | Update application metadata |
300
+ | `openyida nav-group <list\|create\|rename\|delete\|move\|hide\|show> <appType> ...` | Manage sidebar navigation groups and move pages between groups |
282
301
  | `openyida app-permission <get\|set\|add\|remove\|search-user> ...` | Manage app primary admins, data admins, and developer members |
283
302
  | `openyida i18n <overview\|config\|languages\|list\|upsert\|delete\|translate\|translate-all\|upgrade> <appType> ...` | Manage app multilingual copy and language configuration |
284
303
  | `openyida export <appType> [output]` | Export an application migration package |
@@ -290,6 +309,9 @@ For overseas apps, pass `--locale en_US` or `--locale ja_JP` on creation command
290
309
  |---------|-------------|
291
310
  | `openyida create-form create <appType> "<name>" <fields.json> [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a form page |
292
311
  | `openyida create-form update <appType> <formUuid> <changes.json> [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Update a form page |
312
+ | `openyida create-form patch <appType> <formUuid> <patch.json> [--open\|--no-open]` | Apply controlled schema patches for designer-only form settings |
313
+ | `openyida create-form rule <appType> <formUuid> <rules.json> [--open\|--no-open]` | Configure field show/hide linkage and onChange auto-assignment rules |
314
+ | `openyida create-form bind-datasource <appType> <formUuid> <fieldLabelOrId> <datasource.json> [--open\|--no-open]` | Bind URL/search data sources to SelectField/MultiSelectField-style option fields |
293
315
  | `openyida create-form add-option <appType> <formUuid> <fieldLabel> <option1> [option2] ...` | Append options to a SelectField/RadioField/CheckboxField/MultiSelectField |
294
316
  | `openyida list-forms <appType> [--keyword <text>]` | List forms in an application |
295
317
  | `openyida get-schema <appType> <formUuid\|--all> [--field <labelOrFieldId>]` | Fetch one form schema, batch export all, or pick a single field's full props |
@@ -308,9 +330,10 @@ For overseas apps, pass `--locale en_US` or `--locale ja_JP` on creation command
308
330
  | `openyida data <action> <resource> [args]` | Unified data management for forms, processes, tasks, and subforms |
309
331
  | `openyida data check <appType> <formUuid> <rules.json>` | Detect anomalous process-form records |
310
332
  | `openyida task-center <type> [options]` | Query todo, created, processed, CC, or proxy-submitted tasks |
333
+ | `openyida agent-center <sub-command>` | Manage Yida process delegation and departure delegation |
311
334
  | `openyida basic-info <overview\|commodity\|grant\|capacity\|quota\|abs-path\|dataflow\|i18n\|domain>` | Query organization basic info, capacity, quotas, fixed-domain records, and domain settings |
312
335
  | `openyida get-permission <appType> <formUuid>` | Query form permission configuration |
313
- | `openyida save-permission <appType> <formUuid> [options]` | Save form permission configuration |
336
+ | `openyida save-permission <appType> <formUuid> [options]` | Save form permission configuration, including raw `--field-permission <json>` |
314
337
  | `openyida corp-manager <sub-command>` | Manage platform admins, sub-admins, app admins, and address book visibility |
315
338
  | `openyida verify-short-url <appType> <formUuid> <url>` | Verify a short URL |
316
339
  | `openyida save-share-config <appType> <formUuid> <url> <isOpen> [openAuth]` | Save public access or sharing configuration |
package/bin/yida.js CHANGED
@@ -705,6 +705,13 @@ async function main() {
705
705
  break;
706
706
  }
707
707
 
708
+ case 'nav-group':
709
+ case 'group': {
710
+ const { run: runNavGroup } = require('../lib/app/nav-group');
711
+ await runNavGroup(args);
712
+ break;
713
+ }
714
+
708
715
  case 'app-permission': {
709
716
  const { run: runAppPermission } = require('../lib/app-permission/app-permission');
710
717
  await runAppPermission(args);
@@ -919,6 +926,12 @@ async function main() {
919
926
  break;
920
927
  }
921
928
 
929
+ case 'agent-center': {
930
+ const { run: runAgentCenter } = require('../lib/agent-center/agent-center');
931
+ await runAgentCenter(args);
932
+ break;
933
+ }
934
+
922
935
  case 'flash-to-prd': {
923
936
  const { run: runFlashToPrd } = require('../lib/flash-note/flash-to-prd');
924
937
  await runFlashToPrd(args);
@@ -0,0 +1,261 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+
5
+ const {
6
+ listAgentTasks,
7
+ createAgentTask,
8
+ updateAgentTask,
9
+ cancelAgentTask,
10
+ isLeaderShip,
11
+ getLastDepartureAgent,
12
+ getAgentRange,
13
+ } = require('./api');
14
+ const { searchUsers } = require('../corp-manager/api');
15
+
16
+ const USAGE = `openyida agent-center - 代理中心
17
+
18
+ Usage:
19
+ openyida agent-center list [--status ALL|DIS|EFF|OUT|CANCEL] [--keyword TEXT] [--page N] [--size N]
20
+ openyida agent-center search-user <keyword> [--dept <text>] [--size N]
21
+ openyida agent-center create --source-user <userId> --target-user <userId> [--type normal|departure] [options]
22
+ openyida agent-center update <agentUuid> --target-user <userId> [--type normal|departure] [options]
23
+ openyida agent-center cancel <agentUuid> --type normal|departure
24
+ openyida agent-center range <agentUuid>
25
+ openyida agent-center is-leader
26
+ openyida agent-center last-departure
27
+
28
+ Create/update options for normal agents:
29
+ --start <time> 开始时间,例如 "2026-05-20 09:00" 或毫秒时间戳
30
+ --end <time> 结束时间,例如 "2026-05-21 18:00" 或毫秒时间戳
31
+ --category execute|start execute=代处理流程,start=代提交流程,默认 execute
32
+ --notify-source y|n 代理任务是否通知被代理人,默认 n
33
+ --range all|part 代理范围,默认 all
34
+ --range-form <appType:formUuid[,appType:formUuid]>
35
+ --range-json <json> 代理范围 JSON 数组,元素含 appType/formUuid
36
+ --range-file <file> 从 JSON 文件读取代理范围
37
+
38
+ Examples:
39
+ openyida agent-center list --status EFF --size 20
40
+ openyida agent-center search-user "余浩" --dept "宜搭,钉钉官方同学"
41
+ openyida agent-center create --source-user 111 --target-user 222 --start "2026-05-20 09:00" --end "2026-05-21 18:00"
42
+ openyida agent-center create --type departure --source-user 111 --target-user 222
43
+ openyida agent-center cancel Agent_xxx --type departure
44
+ `;
45
+
46
+ function fail(message) {
47
+ console.error(message);
48
+ console.error(USAGE);
49
+ process.exit(1);
50
+ }
51
+
52
+ function parseCliOptions(tokens) {
53
+ const positionals = [];
54
+ const options = {};
55
+
56
+ for (let i = 0; i < tokens.length; i += 1) {
57
+ const token = tokens[i];
58
+ if (token.startsWith('--')) {
59
+ const key = token.slice(2).replace(/-/g, '_');
60
+ const next = tokens[i + 1];
61
+ const value = next && !next.startsWith('--') ? next : true;
62
+ if (Object.prototype.hasOwnProperty.call(options, key)) {
63
+ options[key] = Array.isArray(options[key]) ? options[key].concat(value) : [options[key], value];
64
+ } else {
65
+ options[key] = value;
66
+ }
67
+ if (value !== true) {
68
+ i += 1;
69
+ }
70
+ } else {
71
+ positionals.push(token);
72
+ }
73
+ }
74
+
75
+ return { positionals, options };
76
+ }
77
+
78
+ function splitList(value) {
79
+ if (!value) {
80
+ return [];
81
+ }
82
+ if (Array.isArray(value)) {
83
+ return value.flatMap(splitList);
84
+ }
85
+ return String(value).split(',').map(item => item.trim()).filter(Boolean);
86
+ }
87
+
88
+ function toPositiveInt(value, defaultValue) {
89
+ const parsed = Number.parseInt(value || `${defaultValue}`, 10);
90
+ if (!Number.isFinite(parsed) || parsed <= 0) {
91
+ return defaultValue;
92
+ }
93
+ return parsed;
94
+ }
95
+
96
+ function parseTimestamp(value, flagName) {
97
+ if (value === undefined || value === null || value === '') {
98
+ return undefined;
99
+ }
100
+ if (/^\d+$/.test(String(value))) {
101
+ return Number(value);
102
+ }
103
+ const normalized = String(value).trim().replace(/\//g, '-');
104
+ const parsed = Date.parse(normalized);
105
+ if (!Number.isFinite(parsed)) {
106
+ throw new Error(`${flagName} 不是有效时间:${value}`);
107
+ }
108
+ return parsed;
109
+ }
110
+
111
+ function parseRangeToken(token) {
112
+ const [appType, formUuid] = String(token).split(':').map(part => part && part.trim());
113
+ if (!appType || !formUuid) {
114
+ throw new Error(`代理范围格式必须是 appType:formUuid:${token}`);
115
+ }
116
+ return { appType, formUuid };
117
+ }
118
+
119
+ function parseRangeValue(options) {
120
+ if (options.range_file) {
121
+ return JSON.parse(fs.readFileSync(options.range_file, 'utf8'));
122
+ }
123
+ if (options.range_json) {
124
+ return JSON.parse(options.range_json);
125
+ }
126
+ const tokens = splitList(options.range_form || options.range_forms);
127
+ if (tokens.length > 0) {
128
+ return tokens.map(parseRangeToken);
129
+ }
130
+ return undefined;
131
+ }
132
+
133
+ function firstOption(options, keys) {
134
+ for (const key of keys) {
135
+ if (options[key] !== undefined) {
136
+ return options[key];
137
+ }
138
+ }
139
+ return undefined;
140
+ }
141
+
142
+ function buildMutationOptions(positionals, options) {
143
+ const typeFromPosition = ['normal', 'departure', 'depart', 'dismissed', 'NORMAL', 'DEPARTURE'].includes(positionals[0])
144
+ ? positionals.shift()
145
+ : undefined;
146
+ const rangeValue = parseRangeValue(options);
147
+ const rangeType = firstOption(options, ['range', 'agent_range_type', 'range_type']) || (rangeValue ? 'part' : undefined);
148
+ return {
149
+ type: firstOption(options, ['type', 'agent_type']) || typeFromPosition || 'normal',
150
+ sourceUserId: firstOption(options, ['source_user', 'source_user_id', 'from_user', 'source']),
151
+ targetUserId: firstOption(options, ['target_user', 'target_user_id', 'to_user', 'target']),
152
+ start: parseTimestamp(firstOption(options, ['start', 'gmt_start_date']), '--start'),
153
+ end: parseTimestamp(firstOption(options, ['end', 'gmt_end_date']), '--end'),
154
+ category: firstOption(options, ['category', 'agent_category']),
155
+ notifySource: firstOption(options, ['notify_source', 'origin_is_view']),
156
+ rangeType,
157
+ rangeValue,
158
+ };
159
+ }
160
+
161
+ function printJson(payload) {
162
+ console.log(JSON.stringify(payload, null, 2));
163
+ }
164
+
165
+ async function runList(options) {
166
+ printJson(await listAgentTasks({
167
+ status: options.status,
168
+ keyword: options.keyword || options.keywords,
169
+ page: toPositiveInt(options.page || options.page_index, 1),
170
+ size: toPositiveInt(options.size || options.page_size, 10),
171
+ }));
172
+ }
173
+
174
+ async function runSearchUser(positionals, options) {
175
+ const keyword = positionals[0];
176
+ if (!keyword) {
177
+ fail('缺少搜索关键词');
178
+ }
179
+ printJson(await searchUsers({
180
+ keyword,
181
+ dept: options.dept || options.department,
182
+ size: toPositiveInt(options.size, 50),
183
+ }));
184
+ }
185
+
186
+ async function runCreate(positionals, options) {
187
+ const mutation = buildMutationOptions(positionals, options);
188
+ printJson(await createAgentTask(mutation));
189
+ }
190
+
191
+ async function runUpdate(positionals, options) {
192
+ const agentUuid = positionals.shift();
193
+ if (!agentUuid) {
194
+ fail('缺少 agentUuid');
195
+ }
196
+ const mutation = buildMutationOptions(positionals, options);
197
+ printJson(await updateAgentTask({
198
+ ...mutation,
199
+ agentUuid,
200
+ }));
201
+ }
202
+
203
+ async function runCancel(positionals, options) {
204
+ const agentUuid = positionals[0];
205
+ if (!agentUuid) {
206
+ fail('缺少 agentUuid');
207
+ }
208
+ printJson(await cancelAgentTask({
209
+ agentUuid,
210
+ type: options.type || options.agent_type || positionals[1] || 'normal',
211
+ }));
212
+ }
213
+
214
+ async function runRange(positionals) {
215
+ const agentUuid = positionals[0];
216
+ if (!agentUuid) {
217
+ fail('缺少 agentUuid');
218
+ }
219
+ printJson(await getAgentRange({ agentUuid }));
220
+ }
221
+
222
+ async function run(args) {
223
+ const { positionals, options } = parseCliOptions(args);
224
+ const action = positionals.shift();
225
+
226
+ if (!action || action === '--help' || action === '-h') {
227
+ console.log(USAGE);
228
+ return;
229
+ }
230
+
231
+ if (action === 'list') {
232
+ await runList(options);
233
+ } else if (action === 'search-user') {
234
+ await runSearchUser(positionals, options);
235
+ } else if (action === 'create') {
236
+ await runCreate(positionals, options);
237
+ } else if (action === 'update') {
238
+ await runUpdate(positionals, options);
239
+ } else if (action === 'cancel' || action === 'revoke') {
240
+ await runCancel(positionals, options);
241
+ } else if (action === 'range') {
242
+ await runRange(positionals);
243
+ } else if (action === 'is-leader') {
244
+ printJson(await isLeaderShip());
245
+ } else if (action === 'last-departure') {
246
+ printJson(await getLastDepartureAgent());
247
+ } else {
248
+ fail(`未知 agent-center 子命令:${action}`);
249
+ }
250
+ }
251
+
252
+ module.exports = {
253
+ USAGE,
254
+ parseCliOptions,
255
+ splitList,
256
+ parseTimestamp,
257
+ parseRangeToken,
258
+ parseRangeValue,
259
+ buildMutationOptions,
260
+ run,
261
+ };