openyida 2026.8.19 → 2026.8.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/bin/yida.js +3 -7
- package/lib/auth/org.js +8 -8
- package/lib/auth/profile.js +6 -6
- package/lib/auth/token-auth.js +39 -28
- package/lib/auth/token-store.js +58 -34
- package/lib/core/agent-capabilities.js +14 -102
- package/lib/core/locales/en.js +1 -1
- package/lib/core/locales/zh.js +1 -1
- package/lib/core/query-data.js +21 -0
- package/lib/core/utils.js +15 -11
- package/lib/permission/matrix-service.js +56 -0
- package/lib/permission/save-permission.js +139 -19
- package/package.json +1 -1
- package/yida-skills/skills/yida-data-management/SKILL.md +1 -0
- package/yida-skills/skills/yida-form-permission/SKILL.md +63 -1
package/bin/yida.js
CHANGED
|
@@ -670,15 +670,13 @@ async function main() {
|
|
|
670
670
|
|
|
671
671
|
case 'login': {
|
|
672
672
|
const loginArgs = applyLoginEnvironmentFlags(args, { inferTargetUrl: true });
|
|
673
|
-
const { getAuthStatus
|
|
673
|
+
const { getAuthStatus } = require('../lib/core/utils');
|
|
674
674
|
if (hasHelpFlag(loginArgs)) {
|
|
675
675
|
printLoginHelp();
|
|
676
676
|
} else {
|
|
677
677
|
assertNoUnsupportedLegacyLoginFlags(rawArgs, loginArgs);
|
|
678
678
|
if (loginArgs.includes('--check-only')) {
|
|
679
679
|
console.log(JSON.stringify(getAuthStatus(buildTokenLoginOptions(loginArgs)), null, 2));
|
|
680
|
-
} else if (isEnvAuthMode()) {
|
|
681
|
-
printLoginResult(getAuthStatus(buildTokenLoginOptions(loginArgs)));
|
|
682
680
|
} else {
|
|
683
681
|
const { tokenLogin } = require('../lib/auth/token-auth');
|
|
684
682
|
const result = await tokenLogin(buildTokenLoginOptions(loginArgs));
|
|
@@ -697,7 +695,7 @@ async function main() {
|
|
|
697
695
|
case 'auth': {
|
|
698
696
|
const subCommand = args[0];
|
|
699
697
|
const authArgs = applyLoginEnvironmentFlags(args.slice(1), { inferTargetUrl: true });
|
|
700
|
-
const { getAuthStatus
|
|
698
|
+
const { getAuthStatus } = require('../lib/core/utils');
|
|
701
699
|
const { tokenLogin, tokenLogout, tokenRefresh } = require('../lib/auth/token-auth');
|
|
702
700
|
if (!subCommand || subCommand === '--help' || subCommand === '-h') {
|
|
703
701
|
printAuthHelp();
|
|
@@ -720,9 +718,7 @@ async function main() {
|
|
|
720
718
|
printLoginHelp();
|
|
721
719
|
} else {
|
|
722
720
|
assertNoUnsupportedLegacyLoginFlags(rawArgs.slice(1), authArgs);
|
|
723
|
-
const result =
|
|
724
|
-
? getAuthStatus(buildTokenLoginOptions(authArgs))
|
|
725
|
-
: await tokenLogin(buildTokenLoginOptions(authArgs));
|
|
721
|
+
const result = await tokenLogin(buildTokenLoginOptions(authArgs));
|
|
726
722
|
printLoginResult(result);
|
|
727
723
|
}
|
|
728
724
|
} else if (subCommand === 'refresh') {
|
package/lib/auth/org.js
CHANGED
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
loadTokenSession,
|
|
14
14
|
normalizeCorpName,
|
|
15
15
|
resolveTokenSession,
|
|
16
|
-
|
|
16
|
+
isEnvTokenAuthMode,
|
|
17
17
|
saveAuthProfilePointer,
|
|
18
18
|
saveProjectLegacyTokenSession,
|
|
19
19
|
saveTokenSession,
|
|
@@ -70,15 +70,15 @@ function createProfileRequiredError(targetCorpId, resolution = {}) {
|
|
|
70
70
|
return error;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function
|
|
73
|
+
function createEnvTokenSwitchError(targetCorpId, currentSession, resolution = {}) {
|
|
74
74
|
const actualCorpId = currentSession && currentSession.corp_id;
|
|
75
|
-
const error = new Error(
|
|
76
|
-
error.code = '
|
|
77
|
-
error.status = '
|
|
75
|
+
const error = new Error(`当前为 env token 登录态,不能通过 OAuth 切换组织:target=${targetCorpId}, actual=${actualCorpId || 'unknown'}`);
|
|
76
|
+
error.code = 'ORG_SWITCH_ENV_TOKEN_MISMATCH';
|
|
77
|
+
error.status = 'env_token_mismatch';
|
|
78
78
|
error.targetCorpId = targetCorpId;
|
|
79
79
|
error.actualCorpId = actualCorpId;
|
|
80
80
|
error.auth_source = resolution.auth_source || (currentSession && currentSession.auth_source) || 'env';
|
|
81
|
-
error.auth_store = resolution.auth_store || (currentSession && currentSession.auth_store) || '
|
|
81
|
+
error.auth_store = resolution.auth_store || (currentSession && currentSession.auth_store) || 'env';
|
|
82
82
|
error.can_auto_use = false;
|
|
83
83
|
return error;
|
|
84
84
|
}
|
|
@@ -220,8 +220,8 @@ async function switchOrganization(targetCorpId, options = {}) {
|
|
|
220
220
|
if (targetResolution.status === 'profile_required') {
|
|
221
221
|
throw createProfileRequiredError(targetCorpId, targetResolution);
|
|
222
222
|
}
|
|
223
|
-
if (
|
|
224
|
-
throw
|
|
223
|
+
if (isEnvTokenAuthMode(projectOptions.env)) {
|
|
224
|
+
throw createEnvTokenSwitchError(targetCorpId, currentSession, targetResolution);
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
const loginOptions = {
|
package/lib/auth/profile.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { CliError } = require('../core/cli-error');
|
|
4
4
|
const {
|
|
5
|
-
|
|
5
|
+
isEnvTokenAuthMode,
|
|
6
6
|
listUserAuthProfiles,
|
|
7
7
|
loadAuthProfilePointer,
|
|
8
8
|
loadTokenSession,
|
|
@@ -129,14 +129,14 @@ function resolveSwitchTarget(target, options = {}) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
function switchAuthProfile(target, options = {}) {
|
|
132
|
-
if (
|
|
132
|
+
if (isEnvTokenAuthMode(options.env || process.env)) {
|
|
133
133
|
throw new CliError(
|
|
134
|
-
'
|
|
134
|
+
'env token mode cannot switch local auth profiles',
|
|
135
135
|
{
|
|
136
|
-
code: '
|
|
136
|
+
code: 'AUTH_PROFILE_SWITCH_ENV_TOKEN',
|
|
137
137
|
details: {
|
|
138
|
-
nextStep: 'Ask the
|
|
139
|
-
next_step: 'Ask the
|
|
138
|
+
nextStep: 'Ask the runtime to inject the target organization token instead of switching local profiles.',
|
|
139
|
+
next_step: 'Ask the runtime to inject the target organization token instead of switching local profiles.',
|
|
140
140
|
},
|
|
141
141
|
}
|
|
142
142
|
);
|
package/lib/auth/token-auth.js
CHANGED
|
@@ -15,14 +15,13 @@ const {
|
|
|
15
15
|
clearAllUserAuthProfiles,
|
|
16
16
|
clearTokenSession,
|
|
17
17
|
deleteUserAuthProfile,
|
|
18
|
-
|
|
18
|
+
isEnvTokenAuthMode,
|
|
19
19
|
listUserAuthProfiles,
|
|
20
20
|
loadLocalTokenSession,
|
|
21
21
|
loadTokenSession,
|
|
22
22
|
loadUserProfileFile,
|
|
23
23
|
maskToken,
|
|
24
24
|
normalizeCorpName,
|
|
25
|
-
normalizeTokenSession,
|
|
26
25
|
resolveTokenSession,
|
|
27
26
|
saveBusinessContext,
|
|
28
27
|
saveTokenSession,
|
|
@@ -274,6 +273,32 @@ function isRefreshAuthRequired(value) {
|
|
|
274
273
|
}
|
|
275
274
|
|
|
276
275
|
async function tokenLogin(options = {}) {
|
|
276
|
+
const env = options.env || process.env;
|
|
277
|
+
if (isEnvTokenAuthMode(env)) {
|
|
278
|
+
const status = tokenStatus(options);
|
|
279
|
+
if (status && status.can_auto_use) {
|
|
280
|
+
return {
|
|
281
|
+
...status,
|
|
282
|
+
ok: true,
|
|
283
|
+
status: 'ok',
|
|
284
|
+
can_auto_use: true,
|
|
285
|
+
message: 'env token bootstrap credentials are already available; OAuth login was skipped',
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
...status,
|
|
290
|
+
ok: false,
|
|
291
|
+
auth_mode: 'token',
|
|
292
|
+
auth_source: status && status.auth_source ? status.auth_source : 'env',
|
|
293
|
+
auth_store: status && status.auth_store ? status.auth_store : 'env',
|
|
294
|
+
status: status && status.status ? status.status : 'not_logged_in',
|
|
295
|
+
can_auto_use: false,
|
|
296
|
+
message: status && status.message
|
|
297
|
+
? status.message
|
|
298
|
+
: 'env token bootstrap credential is missing. Ask the runtime to inject OPENYIDA_ACCESS_TOKEN or OPENYIDA_REFRESH_TOKEN.',
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
277
302
|
const baseUrl = resolveTokenBaseUrl(options);
|
|
278
303
|
const authBaseUrl = appendPath(baseUrl, DEFAULT_AUTH_PATH_PREFIX);
|
|
279
304
|
const clientId = options.clientId || process.env.OPENYIDA_DINGTALK_CLIENT_ID || DINGTALK_OAUTH_CLIENT_ID;
|
|
@@ -315,21 +340,21 @@ function tokenStatus(options = {}) {
|
|
|
315
340
|
const session = resolution.session;
|
|
316
341
|
if (!session || (!session.access_token && !session.refresh_token)) {
|
|
317
342
|
const env = options.env || process.env;
|
|
318
|
-
const
|
|
343
|
+
const envTokenMode = isEnvTokenAuthMode(env);
|
|
319
344
|
const status = {
|
|
320
345
|
ok: false,
|
|
321
346
|
auth_mode: 'token',
|
|
322
|
-
auth_source: resolution.auth_source || (
|
|
347
|
+
auth_source: resolution.auth_source || (envTokenMode ? 'env' : undefined),
|
|
323
348
|
auth_store: resolution.auth_store,
|
|
324
349
|
auth_profile: resolution.auth_profile,
|
|
325
350
|
status: resolution.status === 'profile_required' ? 'profile_required' : 'not_logged_in',
|
|
326
351
|
can_auto_use: false,
|
|
327
|
-
failure_reason:
|
|
328
|
-
message: resolution.message || (
|
|
329
|
-
? '
|
|
352
|
+
failure_reason: envTokenMode ? 'env_token_missing' : resolution.status,
|
|
353
|
+
message: resolution.message || (envTokenMode
|
|
354
|
+
? 'env token bootstrap credential is missing. Ask the runtime to inject OPENYIDA_ACCESS_TOKEN or OPENYIDA_REFRESH_TOKEN.'
|
|
330
355
|
: undefined),
|
|
331
|
-
next_step: resolution.next_step || (
|
|
332
|
-
? 'Ask the
|
|
356
|
+
next_step: resolution.next_step || (envTokenMode
|
|
357
|
+
? 'Ask the runtime to inject OPENYIDA_ACCESS_TOKEN or OPENYIDA_REFRESH_TOKEN; stop until credentials are available.'
|
|
333
358
|
: 'Run openyida login to add an auth profile.'),
|
|
334
359
|
next_step_commands: resolution.next_step_commands,
|
|
335
360
|
candidate_count: resolution.candidate_count,
|
|
@@ -338,7 +363,7 @@ function tokenStatus(options = {}) {
|
|
|
338
363
|
persistence_scope: resolution.persistence_scope,
|
|
339
364
|
warning: resolution.warning,
|
|
340
365
|
};
|
|
341
|
-
if (!
|
|
366
|
+
if (!envTokenMode) {
|
|
342
367
|
status.token_file = require('./token-store').getTokenFilePath(options);
|
|
343
368
|
}
|
|
344
369
|
return status;
|
|
@@ -353,7 +378,7 @@ function tokenStatus(options = {}) {
|
|
|
353
378
|
persistence_scope: session.persistence_scope,
|
|
354
379
|
user_auth_store_writable: session.user_auth_store_writable,
|
|
355
380
|
warning: session.warning,
|
|
356
|
-
status: '
|
|
381
|
+
status: 'ok',
|
|
357
382
|
can_auto_use: true,
|
|
358
383
|
refresh_token: maskToken(session.refresh_token),
|
|
359
384
|
base_url: session.base_url,
|
|
@@ -446,7 +471,7 @@ async function tokenRefresh(options = {}) {
|
|
|
446
471
|
}
|
|
447
472
|
}
|
|
448
473
|
const env = options.env || process.env;
|
|
449
|
-
if (
|
|
474
|
+
if (isEnvTokenAuthMode(env) && ['env', 'project_legacy'].includes(session.auth_source)) {
|
|
450
475
|
env.OPENYIDA_ACCESS_TOKEN = normalized.access_token;
|
|
451
476
|
if (normalized.refresh_token) {
|
|
452
477
|
env.OPENYIDA_REFRESH_TOKEN = normalized.refresh_token;
|
|
@@ -454,10 +479,10 @@ async function tokenRefresh(options = {}) {
|
|
|
454
479
|
if (normalized.expires_at) {
|
|
455
480
|
env.OPENYIDA_ACCESS_TOKEN_EXPIRES_AT = String(normalized.expires_at);
|
|
456
481
|
}
|
|
457
|
-
return
|
|
482
|
+
return saveTokenSession({
|
|
458
483
|
...normalized,
|
|
459
484
|
auth_source: 'env',
|
|
460
|
-
});
|
|
485
|
+
}, options);
|
|
461
486
|
}
|
|
462
487
|
return saveTokenSession(normalized, options);
|
|
463
488
|
}
|
|
@@ -496,20 +521,6 @@ function toLogoutProfileSummary(session = {}) {
|
|
|
496
521
|
}
|
|
497
522
|
|
|
498
523
|
async function tokenLogout(options = {}) {
|
|
499
|
-
const env = options.env || process.env;
|
|
500
|
-
if (isHostInjectedTokenMode(env)) {
|
|
501
|
-
return {
|
|
502
|
-
ok: true,
|
|
503
|
-
auth_mode: 'token',
|
|
504
|
-
status: 'host_injected_noop',
|
|
505
|
-
can_auto_use: false,
|
|
506
|
-
auth_source: 'env',
|
|
507
|
-
auth_store: 'host_injected',
|
|
508
|
-
persistence_scope: 'host',
|
|
509
|
-
message: 'host-injected token mode does not write or delete local auth profiles',
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
|
|
513
524
|
const authProfile = options.authProfile || options.profile;
|
|
514
525
|
const deleteAllProfiles = options.allProfiles === true || options.all === true;
|
|
515
526
|
if (deleteAllProfiles) {
|
package/lib/auth/token-store.js
CHANGED
|
@@ -394,6 +394,7 @@ function buildAuthProfileNextStep(status) {
|
|
|
394
394
|
|
|
395
395
|
function getAuthProfileSelector(options = {}) {
|
|
396
396
|
const env = options.env || process.env;
|
|
397
|
+
const envTokenMode = isEnvTokenAuthMode(env);
|
|
397
398
|
return {
|
|
398
399
|
authProfile: sanitizeProfileName(
|
|
399
400
|
options.authProfile ||
|
|
@@ -401,8 +402,18 @@ function getAuthProfileSelector(options = {}) {
|
|
|
401
402
|
env.OPENYIDA_AUTH_PROFILE ||
|
|
402
403
|
''
|
|
403
404
|
),
|
|
404
|
-
corpId: String(
|
|
405
|
-
|
|
405
|
+
corpId: String(
|
|
406
|
+
options.corpId ||
|
|
407
|
+
env.OPENYIDA_AUTH_CORP_ID ||
|
|
408
|
+
(envTokenMode ? env.OPENYIDA_TOKEN_CORP_ID : '') ||
|
|
409
|
+
''
|
|
410
|
+
).trim(),
|
|
411
|
+
userId: String(
|
|
412
|
+
options.userId ||
|
|
413
|
+
env.OPENYIDA_AUTH_USER_ID ||
|
|
414
|
+
(envTokenMode ? env.OPENYIDA_TOKEN_USER_ID : '') ||
|
|
415
|
+
''
|
|
416
|
+
).trim(),
|
|
406
417
|
};
|
|
407
418
|
}
|
|
408
419
|
|
|
@@ -585,13 +596,8 @@ function saveUserTokenSession(session, options = {}) {
|
|
|
585
596
|
function saveTokenSession(session, options = {}) {
|
|
586
597
|
const env = options.env || process.env;
|
|
587
598
|
const normalized = normalizeTokenSession(session);
|
|
588
|
-
if (
|
|
589
|
-
return
|
|
590
|
-
auth_source: 'env',
|
|
591
|
-
auth_store: 'host_injected',
|
|
592
|
-
persistence_scope: 'host',
|
|
593
|
-
user_auth_store_writable: null,
|
|
594
|
-
});
|
|
599
|
+
if (isEnvTokenAuthMode(env) && normalized.auth_source === 'env') {
|
|
600
|
+
return saveProjectLegacyTokenSession(normalized, options);
|
|
595
601
|
}
|
|
596
602
|
|
|
597
603
|
try {
|
|
@@ -616,9 +622,15 @@ function saveTokenSession(session, options = {}) {
|
|
|
616
622
|
}
|
|
617
623
|
}
|
|
618
624
|
|
|
619
|
-
function
|
|
620
|
-
|
|
621
|
-
|
|
625
|
+
function isEnvTokenAuthMode(env = process.env) {
|
|
626
|
+
return String(env.OPENYIDA_AUTH_MODE || '').trim().toLowerCase() === 'token';
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function hasEnvTokenCredential(env = process.env) {
|
|
630
|
+
return !!(
|
|
631
|
+
String(env.OPENYIDA_ACCESS_TOKEN || '').trim() ||
|
|
632
|
+
String(env.OPENYIDA_REFRESH_TOKEN || '').trim()
|
|
633
|
+
);
|
|
622
634
|
}
|
|
623
635
|
|
|
624
636
|
function loadEnvTokenSession(env = process.env) {
|
|
@@ -773,29 +785,40 @@ function resolveTokenSession(options = {}) {
|
|
|
773
785
|
const env = options.env || process.env;
|
|
774
786
|
const selector = getAuthProfileSelector(options);
|
|
775
787
|
const envSession = applyBusinessContextToEnvSession(loadEnvTokenSession(env), options);
|
|
776
|
-
if (
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
788
|
+
if (isEnvTokenAuthMode(env)) {
|
|
789
|
+
const localSession = loadLocalTokenSession(options);
|
|
790
|
+
if (
|
|
791
|
+
localSession &&
|
|
792
|
+
(localSession.access_token || localSession.refresh_token) &&
|
|
793
|
+
sessionMatchesSelector(localSession, selector)
|
|
794
|
+
) {
|
|
795
|
+
return {
|
|
796
|
+
session: localSession,
|
|
797
|
+
status: 'ok',
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
if (
|
|
801
|
+
hasEnvTokenCredential(env) &&
|
|
802
|
+
envSession &&
|
|
803
|
+
(envSession.access_token || envSession.refresh_token) &&
|
|
804
|
+
sessionMatchesSelector(envSession, selector, { allowMissingIdentity: true })
|
|
805
|
+
) {
|
|
806
|
+
return {
|
|
807
|
+
session: attachAuthStoreMetadata(envSession, {
|
|
808
|
+
auth_source: 'env',
|
|
809
|
+
auth_store: 'env',
|
|
810
|
+
persistence_scope: 'process',
|
|
811
|
+
user_auth_store_writable: null,
|
|
812
|
+
}),
|
|
813
|
+
status: 'ok',
|
|
814
|
+
};
|
|
815
|
+
}
|
|
793
816
|
return {
|
|
794
817
|
session: null,
|
|
795
818
|
status: 'env_token_missing',
|
|
796
819
|
auth_source: 'env',
|
|
797
|
-
auth_store: '
|
|
798
|
-
persistence_scope: '
|
|
820
|
+
auth_store: 'env',
|
|
821
|
+
persistence_scope: 'process',
|
|
799
822
|
user_auth_store_writable: null,
|
|
800
823
|
};
|
|
801
824
|
}
|
|
@@ -807,7 +830,7 @@ function resolveTokenSession(options = {}) {
|
|
|
807
830
|
return {
|
|
808
831
|
session: attachAuthStoreMetadata(envSession, {
|
|
809
832
|
auth_source: 'env',
|
|
810
|
-
auth_store: '
|
|
833
|
+
auth_store: 'env',
|
|
811
834
|
persistence_scope: 'process',
|
|
812
835
|
user_auth_store_writable: getUserAuthStoreWritable(options),
|
|
813
836
|
}),
|
|
@@ -895,7 +918,7 @@ function resolveTokenSession(options = {}) {
|
|
|
895
918
|
return {
|
|
896
919
|
session: attachAuthStoreMetadata(envSession, {
|
|
897
920
|
auth_source: 'env',
|
|
898
|
-
auth_store: '
|
|
921
|
+
auth_store: 'env',
|
|
899
922
|
persistence_scope: 'process',
|
|
900
923
|
user_auth_store_writable: getUserAuthStoreWritable(options),
|
|
901
924
|
}),
|
|
@@ -975,7 +998,8 @@ module.exports = {
|
|
|
975
998
|
loadEnvTokenSession,
|
|
976
999
|
loadLocalTokenSession,
|
|
977
1000
|
saveProjectLegacyTokenSession,
|
|
978
|
-
|
|
1001
|
+
isEnvTokenAuthMode,
|
|
1002
|
+
hasEnvTokenCredential,
|
|
979
1003
|
isAccessTokenUsable,
|
|
980
1004
|
maskToken,
|
|
981
1005
|
resolveEnvName,
|
|
@@ -105,38 +105,6 @@ function compactSkillsDiagnostics(skillsDiagnostics) {
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
function isTruthyEnv(value) {
|
|
109
|
-
return ['1', 'true', 'yes', 'on'].includes(String(value || '').trim().toLowerCase());
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function hasEnvTokenCredential(env = process.env) {
|
|
113
|
-
return !!(
|
|
114
|
-
String(env.OPENYIDA_ACCESS_TOKEN || '').trim() ||
|
|
115
|
-
String(env.OPENYIDA_REFRESH_TOKEN || '').trim()
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function hasRuntimeProvisionedAccessToken(env = process.env) {
|
|
120
|
-
return isTruthyEnv(env.YIDA_AUTH_ENABLED) &&
|
|
121
|
-
!!String(env.OPENYIDA_ACCESS_TOKEN || '').trim();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function buildRuntimeProvisionedLoginStatus(env = process.env) {
|
|
125
|
-
return {
|
|
126
|
-
ok: true,
|
|
127
|
-
auth_mode: 'token',
|
|
128
|
-
auth_source: 'env',
|
|
129
|
-
auth_store: 'host_injected',
|
|
130
|
-
corp_id: env.OPENYIDA_TOKEN_CORP_ID,
|
|
131
|
-
corp_name: env.OPENYIDA_TOKEN_CORP_NAME,
|
|
132
|
-
user_id: env.OPENYIDA_TOKEN_USER_ID,
|
|
133
|
-
user_auth_store_writable: null,
|
|
134
|
-
persistence_scope: 'host',
|
|
135
|
-
status: 'ok',
|
|
136
|
-
can_auto_use: true,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
108
|
function buildRuntimeSnapshot(projectRoot) {
|
|
141
109
|
const detected = typeof coreUtils.detectRuntimeCapabilities === 'function'
|
|
142
110
|
? coreUtils.detectRuntimeCapabilities()
|
|
@@ -204,18 +172,12 @@ function forbiddenAliasBriefs(manifest) {
|
|
|
204
172
|
}
|
|
205
173
|
|
|
206
174
|
function buildAuthPath(login, env = process.env) {
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
const authSource = login.auth_source || (hostInjectedTokenMode || envTokenPresent ? 'env' : 'token_session');
|
|
210
|
-
const hostTokenEnvDetected = hostInjectedTokenMode || envTokenPresent || authSource === 'env';
|
|
211
|
-
const authStore = login.auth_store || (hostTokenEnvDetected ? 'host_injected' : undefined);
|
|
212
|
-
const runtimeAuthProvisioned = hasRuntimeProvisionedAccessToken(env) &&
|
|
213
|
-
authSource === 'env' &&
|
|
214
|
-
login.status === 'ok';
|
|
175
|
+
const authSource = login.auth_source || 'token_session';
|
|
176
|
+
const envTokenSession = authSource === 'env' || login.failure_reason === 'env_token_missing';
|
|
215
177
|
const authPath = {
|
|
216
178
|
mode: login.auth_mode || 'token',
|
|
217
179
|
source: authSource,
|
|
218
|
-
store:
|
|
180
|
+
store: login.auth_store,
|
|
219
181
|
auth_profile: login.auth_profile,
|
|
220
182
|
corp_id: login.corp_id,
|
|
221
183
|
corp_name: login.corp_name,
|
|
@@ -227,12 +189,9 @@ function buildAuthPath(login, env = process.env) {
|
|
|
227
189
|
next_step: login.next_step,
|
|
228
190
|
next_step_commands: login.next_step_commands,
|
|
229
191
|
can_auto_use: login.can_auto_use === true,
|
|
230
|
-
|
|
231
|
-
host_token_env_detected: hostTokenEnvDetected,
|
|
232
|
-
env_token_present: envTokenPresent,
|
|
233
|
-
interactive_login_allowed: !hostTokenEnvDetected,
|
|
192
|
+
interactive_login_allowed: !envTokenSession,
|
|
234
193
|
browser_session_auth_allowed: false,
|
|
235
|
-
auth_runtime: 'token_oauth_session',
|
|
194
|
+
auth_runtime: envTokenSession ? 'env_token_bootstrap' : 'token_oauth_session',
|
|
236
195
|
cookie_auth_supported: false,
|
|
237
196
|
cookie_check_required: false,
|
|
238
197
|
playwright_cookie_check_required: false,
|
|
@@ -243,13 +202,10 @@ function buildAuthPath(login, env = process.env) {
|
|
|
243
202
|
'qr_login',
|
|
244
203
|
'cookie_cache',
|
|
245
204
|
],
|
|
246
|
-
missing_token_action:
|
|
247
|
-
? '
|
|
205
|
+
missing_token_action: envTokenSession
|
|
206
|
+
? 'STOP_AND_REQUEST_ENV_TOKEN'
|
|
248
207
|
: 'RUN_OPENYIDA_LOGIN_IF_USER_APPROVES',
|
|
249
208
|
};
|
|
250
|
-
if (runtimeAuthProvisioned) {
|
|
251
|
-
authPath.runtime_auth_provisioned = true;
|
|
252
|
-
}
|
|
253
209
|
return authPath;
|
|
254
210
|
}
|
|
255
211
|
|
|
@@ -261,14 +217,14 @@ function buildInteractiveLogin(auth, runtime) {
|
|
|
261
217
|
playwright_required: false,
|
|
262
218
|
};
|
|
263
219
|
|
|
264
|
-
if (auth.
|
|
220
|
+
if (!auth.interactive_login_allowed) {
|
|
265
221
|
return {
|
|
266
222
|
mode: 'not_required',
|
|
267
223
|
browser_default: 'not_required',
|
|
268
224
|
browser_owner: 'none',
|
|
269
225
|
recommended_command: null,
|
|
270
226
|
agent_action: 'do_not_run_oauth_login',
|
|
271
|
-
reason: auth.
|
|
227
|
+
reason: auth.source === 'env' ? 'env_token_bootstrap' : 'login_not_required',
|
|
272
228
|
...base,
|
|
273
229
|
};
|
|
274
230
|
}
|
|
@@ -306,7 +262,7 @@ function buildInteractiveLogin(auth, runtime) {
|
|
|
306
262
|
browser_default: 'unsupported',
|
|
307
263
|
browser_owner: 'none',
|
|
308
264
|
recommended_command: null,
|
|
309
|
-
agent_action: '
|
|
265
|
+
agent_action: 'ask_user_for_browser_access',
|
|
310
266
|
reason: 'no_desktop_shell_or_agent_browser_detected',
|
|
311
267
|
...base,
|
|
312
268
|
};
|
|
@@ -314,7 +270,7 @@ function buildInteractiveLogin(auth, runtime) {
|
|
|
314
270
|
|
|
315
271
|
function buildBuilderPath(login, projectRoot, manifest, env = process.env, runtimeSnapshot = null) {
|
|
316
272
|
const auth = buildAuthPath(login, env);
|
|
317
|
-
const canTrustSummaryPreflight = auth.can_auto_use === true
|
|
273
|
+
const canTrustSummaryPreflight = auth.can_auto_use === true;
|
|
318
274
|
const runtime = runtimeSnapshot || buildRuntimeSnapshot(projectRoot);
|
|
319
275
|
|
|
320
276
|
return {
|
|
@@ -352,10 +308,10 @@ function buildBuilderPath(login, projectRoot, manifest, env = process.env, runti
|
|
|
352
308
|
skip_help_discovery_default: true,
|
|
353
309
|
skip_env_noise_default: true,
|
|
354
310
|
skip_login_check_only_default: canTrustSummaryPreflight,
|
|
355
|
-
skip_browser_login_default: auth.
|
|
311
|
+
skip_browser_login_default: !auth.interactive_login_allowed,
|
|
356
312
|
skip_cookie_or_playwright_checks_default: true,
|
|
357
313
|
default_app_list_policy: 'skip_when_bound_app_type_unique',
|
|
358
|
-
|
|
314
|
+
stop_when_env_token_missing: auth.missing_token_action === 'STOP_AND_REQUEST_ENV_TOKEN' && !auth.can_auto_use,
|
|
359
315
|
},
|
|
360
316
|
command_contract: {
|
|
361
317
|
command_prefix: manifest.command_prefix,
|
|
@@ -437,9 +393,6 @@ function compactBuilderPath(builderPath) {
|
|
|
437
393
|
next_step: builderPath.auth.next_step,
|
|
438
394
|
next_step_commands: builderPath.auth.next_step_commands,
|
|
439
395
|
can_auto_use: builderPath.auth.can_auto_use,
|
|
440
|
-
host_injected_token_mode: builderPath.auth.host_injected_token_mode,
|
|
441
|
-
host_token_env_detected: builderPath.auth.host_token_env_detected,
|
|
442
|
-
env_token_present: builderPath.auth.env_token_present,
|
|
443
396
|
interactive_login_allowed: builderPath.auth.interactive_login_allowed,
|
|
444
397
|
browser_session_auth_allowed: builderPath.auth.browser_session_auth_allowed,
|
|
445
398
|
auth_runtime: builderPath.auth.auth_runtime,
|
|
@@ -449,10 +402,6 @@ function compactBuilderPath(builderPath) {
|
|
|
449
402
|
qr_login_required: builderPath.auth.qr_login_required,
|
|
450
403
|
missing_token_action: builderPath.auth.missing_token_action,
|
|
451
404
|
};
|
|
452
|
-
if (builderPath.auth.runtime_auth_provisioned === true) {
|
|
453
|
-
auth.runtime_auth_provisioned = true;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
405
|
return {
|
|
457
406
|
schema_version: builderPath.schema_version,
|
|
458
407
|
preflight: builderPath.preflight,
|
|
@@ -465,7 +414,7 @@ function compactBuilderPath(builderPath) {
|
|
|
465
414
|
skip_login_check_only_default: environment.skip_login_check_only_default,
|
|
466
415
|
skip_browser_login_default: environment.skip_browser_login_default,
|
|
467
416
|
skip_cookie_or_playwright_checks_default: environment.skip_cookie_or_playwright_checks_default,
|
|
468
|
-
|
|
417
|
+
stop_when_env_token_missing: environment.stop_when_env_token_missing,
|
|
469
418
|
default_app_list_policy: environment.default_app_list_policy,
|
|
470
419
|
},
|
|
471
420
|
command_contract: {
|
|
@@ -541,43 +490,6 @@ function buildCommandManifestDigest(manifest) {
|
|
|
541
490
|
|
|
542
491
|
function buildAgentCapabilitiesSummary() {
|
|
543
492
|
const manifest = buildCommandManifest({ t, version });
|
|
544
|
-
|
|
545
|
-
if (hasRuntimeProvisionedAccessToken(process.env)) {
|
|
546
|
-
const projectResolution = resolveProjectRootCompat();
|
|
547
|
-
const projectRoot = projectResolution.projectRoot;
|
|
548
|
-
const skillsDiagnostics = compactSkillsDiagnostics(buildSkillsDiagnosticsCompat(projectResolution));
|
|
549
|
-
const loginStatus = buildRuntimeProvisionedLoginStatus();
|
|
550
|
-
const runtime = buildRuntimeSnapshot(projectRoot);
|
|
551
|
-
const builderPath = compactBuilderPath(
|
|
552
|
-
buildBuilderPath(loginStatus, projectRoot, manifest, process.env, runtime)
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
return {
|
|
556
|
-
schema_version: 1,
|
|
557
|
-
name: 'openyida-agent-capabilities-summary',
|
|
558
|
-
version,
|
|
559
|
-
login: compactLogin(loginStatus),
|
|
560
|
-
precheck: {
|
|
561
|
-
skipped: true,
|
|
562
|
-
reason: 'runtime_auth_provisioned',
|
|
563
|
-
},
|
|
564
|
-
workdir: projectRoot,
|
|
565
|
-
workdir_exists: fs.existsSync(projectRoot),
|
|
566
|
-
workdir_source: projectResolution.source,
|
|
567
|
-
workdir_reason: projectResolution.reason,
|
|
568
|
-
project_root: compactProjectRootResolution(projectResolution),
|
|
569
|
-
cache_dir: path.join(projectRoot, '.cache'),
|
|
570
|
-
openyida_task_cache_dir: path.join(projectRoot, '.cache', 'openyida'),
|
|
571
|
-
skills: skillsDiagnostics,
|
|
572
|
-
command_manifest_digest: buildCommandManifestDigest(manifest),
|
|
573
|
-
command_manifest_digest_algorithm: 'sha256',
|
|
574
|
-
command_count: manifest.summary.command_count,
|
|
575
|
-
full_capabilities_command: 'openyida agent-capabilities --json',
|
|
576
|
-
runtime,
|
|
577
|
-
builder_path: builderPath,
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
|
|
581
493
|
const envSnapshot = buildEnvironmentSnapshot();
|
|
582
494
|
const projectResolution = resolveProjectRootCompat({
|
|
583
495
|
projectRoot: envSnapshot.active.projectRoot,
|
package/lib/core/locales/en.js
CHANGED
|
@@ -306,7 +306,7 @@ Examples:
|
|
|
306
306
|
exec_failed: '\n❌ Execution failed: {0}',
|
|
307
307
|
login_usage: 'Usage: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
|
|
308
308
|
login_example: 'Examples:\n openyida login # Automatically open the browser via OAuth loopback login\n openyida login --no-browser # Let the caller handle the authorization URL\n openyida login --check-only --json # Check token auth status only\n openyida login --intl # Login against the international environment\n OPENYIDA_NO_BROWSER=1 openyida login # Suppress auto-opening the browser with an environment variable\n openyida auth login # Login alias',
|
|
309
|
-
login_unsupported_option: 'Removed legacy login option is no longer supported: {0}. Use token/OAuth login;
|
|
309
|
+
login_unsupported_option: 'Removed legacy login option is no longer supported: {0}. Use token/OAuth login; env token mode reads only OPENYIDA_* tokens.',
|
|
310
310
|
auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch>',
|
|
311
311
|
auth_example: 'Examples:\n openyida auth status # View login status\n openyida auth profiles # List existing login profiles\n openyida auth profile switch <auth_profile> # Switch current project to an existing profile\n openyida auth login # Add a profile when the target does not exist\n openyida auth refresh # Refresh login session\n openyida auth logout # Unbind current project auth\n openyida auth logout --profile <auth_profile> # Delete a shared profile explicitly',
|
|
312
312
|
org_usage: 'Usage: openyida org <list|switch> [--json] [--corp-id <corpId>]',
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -307,7 +307,7 @@ openyida - 宜搭命令行工具
|
|
|
307
307
|
exec_failed: '\n❌ 执行失败: {0}',
|
|
308
308
|
login_usage: '用法: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
|
|
309
309
|
login_example: '示例:\n openyida login # 通过 OAuth loopback 自动打开浏览器登录\n openyida login --no-browser # 不自动打开浏览器,由调用方接管授权链接\n openyida login --check-only --json # 只检查 token 登录态\n openyida login --intl # 使用国际站环境登录\n OPENYIDA_NO_BROWSER=1 openyida login # 通过环境变量抑制自动打开浏览器\n openyida auth login # 登录入口别名',
|
|
310
|
-
login_unsupported_option: '已删除的旧登录参数不再支持: {0}。请使用 token/OAuth
|
|
310
|
+
login_unsupported_option: '已删除的旧登录参数不再支持: {0}。请使用 token/OAuth 登录;env token 模式下只读取 OPENYIDA_* token。',
|
|
311
311
|
first_run_title: ' 🤖 OpenYida - AI 问答模式已开启! ',
|
|
312
312
|
first_run_welcome: ' {0}欢迎首次使用 OpenYida!{1} 以下是快速上手指南:',
|
|
313
313
|
first_run_way1_title: ' 📝 方式一:直接描述需求',
|
package/lib/core/query-data.js
CHANGED
|
@@ -18,6 +18,7 @@ const { CliError } = require('./cli-error');
|
|
|
18
18
|
const { createAuthRef, createYidaClient, isAuthRefReady } = require('./yida-client');
|
|
19
19
|
|
|
20
20
|
const { buildComponentAliasMaps } = require('../app/get-schema');
|
|
21
|
+
const { getProcessCodeFromFormBinding } = require('../app/services/form-mode-service');
|
|
21
22
|
|
|
22
23
|
const USAGE = `openyida data - Unified Yida data CLI
|
|
23
24
|
|
|
@@ -25,6 +26,7 @@ Usage:
|
|
|
25
26
|
openyida data query form <appType> <formUuid> [--page N] [--size N] [--all] [--max-pages N] [--search-json JSON|--search-file .cache/openyida/search.json] [--resolve-aliases] [--inst-id ID] [--no-hydrate-subforms]
|
|
26
27
|
openyida data get form <appType> --inst-id <formInstId> [--form-uuid <formUuid>] [--no-hydrate-subforms]
|
|
27
28
|
openyida data create form <appType> <formUuid> (--data-json <JSON>|--data-file .cache/openyida/data.json) [--dept-id ID] [--resolve-aliases]
|
|
29
|
+
说明:若目标表单为流程表单,会自动使用 /v1/process/startInstance.json 发起流程。
|
|
28
30
|
openyida data update form <appType> --inst-id <formInstId> (--data-json <JSON>|--data-file .cache/openyida/data.json) [--form-uuid <formUuid>] [--use-latest-version y] [--resolve-aliases]
|
|
29
31
|
openyida data query subform <appType> <formUuid> --inst-id <formInstId> --table-field-id <fieldId|alias> [--page N] [--size N] [--resolve-aliases]
|
|
30
32
|
|
|
@@ -592,6 +594,15 @@ async function getForm(positionals, options, session) {
|
|
|
592
594
|
}));
|
|
593
595
|
}
|
|
594
596
|
|
|
597
|
+
async function resolveProcessCode(session, appType, formUuid) {
|
|
598
|
+
try {
|
|
599
|
+
return await getProcessCodeFromFormBinding(session, appType, formUuid);
|
|
600
|
+
} catch (err) {
|
|
601
|
+
console.warn(`⚠️ 无法判断表单 ${formUuid} 是否为流程表单,将使用 saveFormData 提交:${err.message}`);
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
595
606
|
async function createForm(positionals, options, session) {
|
|
596
607
|
requirePositionals(positionals, 2, ['appType', 'formUuid']);
|
|
597
608
|
const dataJson = requireJsonOption(options, 'data_json', 'data_file', '数据');
|
|
@@ -603,6 +614,16 @@ async function createForm(positionals, options, session) {
|
|
|
603
614
|
formDataJson: translateJsonWithAliases(dataJson, aliasContext, translateFormDataObject),
|
|
604
615
|
};
|
|
605
616
|
if (options.dept_id) {params.deptId = options.dept_id;}
|
|
617
|
+
|
|
618
|
+
const processCode = await resolveProcessCode(session, appType, formUuid);
|
|
619
|
+
if (processCode) {
|
|
620
|
+
printResult(await sendPost(session, appType, `/dingtalk/web/${appType}/v1/process/startInstance.json`, {
|
|
621
|
+
...params,
|
|
622
|
+
processCode,
|
|
623
|
+
}));
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
|
|
606
627
|
printResult(await sendPost(session, appType, `/dingtalk/web/${appType}/v1/form/saveFormData.json`, params));
|
|
607
628
|
}
|
|
608
629
|
|
package/lib/core/utils.js
CHANGED
|
@@ -454,13 +454,9 @@ function getNodeExecutable() {
|
|
|
454
454
|
return 'node';
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
-
function isInjectedAuthMode(env = process.env) {
|
|
458
|
-
const authEnabled = String(env.YIDA_AUTH_ENABLED || '').trim().toLowerCase();
|
|
459
|
-
return ['1', 'true', 'yes', 'on'].includes(authEnabled);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
457
|
function isEnvAuthMode(env = process.env) {
|
|
463
|
-
|
|
458
|
+
const { isEnvTokenAuthMode } = require('../auth/token-store');
|
|
459
|
+
return isEnvTokenAuthMode(env);
|
|
464
460
|
}
|
|
465
461
|
|
|
466
462
|
function isTokenAuthMode(_env = process.env) {
|
|
@@ -942,7 +938,7 @@ function loadCookieData(projectRoot, defaultBaseUrl) {
|
|
|
942
938
|
|
|
943
939
|
/**
|
|
944
940
|
* 读取当前默认登录态。
|
|
945
|
-
* 默认使用 OAuth token session;
|
|
941
|
+
* 默认使用 OAuth token session;env token mode 时仅使用项目缓存或运行环境注入 token。
|
|
946
942
|
* @param {string} [projectRoot]
|
|
947
943
|
* @param {string} [defaultBaseUrl]
|
|
948
944
|
* @returns {object|null}
|
|
@@ -961,7 +957,7 @@ function getAuthStatus(options = {}) {
|
|
|
961
957
|
// ── 登录触发 ──────────────────────────────────────────
|
|
962
958
|
|
|
963
959
|
/**
|
|
964
|
-
* 校验默认登录态;
|
|
960
|
+
* 校验默认登录态;env token mode 下不会触发 OAuth,本地模式提示用户执行 openyida login。
|
|
965
961
|
* @param {object} [options]
|
|
966
962
|
* @param {boolean} [options.force=false] - 是否跳过本地缓存,强制重新登录
|
|
967
963
|
* @returns {object} loginResult
|
|
@@ -977,11 +973,12 @@ function triggerLogin(options = {}) {
|
|
|
977
973
|
: 'env_token_missing';
|
|
978
974
|
const { CliError } = require('./cli-error');
|
|
979
975
|
throw new CliError(
|
|
980
|
-
`not_logged_in:
|
|
976
|
+
`not_logged_in: env token bootstrap is unavailable (${reason}). Ask the runtime to inject OPENYIDA_ACCESS_TOKEN or OPENYIDA_REFRESH_TOKEN.`,
|
|
981
977
|
{
|
|
982
|
-
code: '
|
|
978
|
+
code: 'ENV_TOKEN_AUTH_REQUIRED',
|
|
983
979
|
details: {
|
|
984
980
|
authMode: 'token',
|
|
981
|
+
authSource: 'env',
|
|
985
982
|
failure_reason: reason,
|
|
986
983
|
},
|
|
987
984
|
}
|
|
@@ -1462,6 +1459,14 @@ function normalizeRefreshedTokenAuthData(refreshResult, fallbackBaseUrl) {
|
|
|
1462
1459
|
}
|
|
1463
1460
|
|
|
1464
1461
|
function tokenAuthRequiredResult() {
|
|
1462
|
+
if (isEnvAuthMode()) {
|
|
1463
|
+
return {
|
|
1464
|
+
success: false,
|
|
1465
|
+
__needLogin: true,
|
|
1466
|
+
errorCode: 'TOKEN_AUTH_REQUIRED',
|
|
1467
|
+
errorMsg: 'not_logged_in: env token bootstrap is unavailable. Ask the runtime to inject OPENYIDA_ACCESS_TOKEN or OPENYIDA_REFRESH_TOKEN.',
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1465
1470
|
return {
|
|
1466
1471
|
success: false,
|
|
1467
1472
|
__needLogin: true,
|
|
@@ -1502,7 +1507,6 @@ module.exports = {
|
|
|
1502
1507
|
getNpmExecutable,
|
|
1503
1508
|
getNodeExecutable,
|
|
1504
1509
|
resolveWukongWorkspaceRoot,
|
|
1505
|
-
isInjectedAuthMode,
|
|
1506
1510
|
isEnvAuthMode,
|
|
1507
1511
|
isTokenAuthMode,
|
|
1508
1512
|
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* matrix-service.js - 宜搭权限矩阵查询服务
|
|
3
|
+
*
|
|
4
|
+
* 提供权限矩阵列表查询与单个矩阵详情查询,供 save-permission 等命令使用。
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const { createAuthRef, createYidaClient } = require('../core/yida-client');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 查询权限矩阵列表
|
|
12
|
+
*
|
|
13
|
+
* @param {object} authRef - 认证引用对象
|
|
14
|
+
* @param {object} options - 查询选项
|
|
15
|
+
* @param {string} [options.keyword] - 搜索关键词
|
|
16
|
+
* @param {number} [options.page=1] - 页码
|
|
17
|
+
* @param {number} [options.limit=10] - 每页条数
|
|
18
|
+
* @returns {Promise<Array>} 权限矩阵列表
|
|
19
|
+
*/
|
|
20
|
+
async function getMatrixList(authRef, options = {}) {
|
|
21
|
+
const ref = authRef || createAuthRef();
|
|
22
|
+
const { keyword = '', page = 1, limit = 10 } = options;
|
|
23
|
+
const client = createYidaClient({ authRef: ref });
|
|
24
|
+
const result = await client.getContent('/query/matrix/getMatrixList.json', {
|
|
25
|
+
keyword,
|
|
26
|
+
page,
|
|
27
|
+
limit,
|
|
28
|
+
}, {
|
|
29
|
+
action: 'getMatrixList',
|
|
30
|
+
failMessage: '获取权限矩阵列表失败',
|
|
31
|
+
});
|
|
32
|
+
return (result && result.data) || [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 根据 ID 查询单个权限矩阵详情
|
|
37
|
+
*
|
|
38
|
+
* @param {object} authRef - 认证引用对象
|
|
39
|
+
* @param {string} matrixId - 权限矩阵 ID
|
|
40
|
+
* @returns {Promise<object>} 权限矩阵详情
|
|
41
|
+
*/
|
|
42
|
+
async function getMatrixById(authRef, matrixId) {
|
|
43
|
+
const ref = authRef || createAuthRef();
|
|
44
|
+
const client = createYidaClient({ authRef: ref });
|
|
45
|
+
return client.getContent('/query/matrix/getMatrixById.json', {
|
|
46
|
+
matrixId,
|
|
47
|
+
}, {
|
|
48
|
+
action: 'getMatrixById',
|
|
49
|
+
failMessage: `获取权限矩阵 ${matrixId} 详情失败`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
getMatrixList,
|
|
55
|
+
getMatrixById,
|
|
56
|
+
};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* openyida save-permission <appType> <formUuid> --action-permission <json>
|
|
7
7
|
* openyida save-permission <appType> <formUuid> --field-permission <json>
|
|
8
8
|
* openyida save-permission <appType> <formUuid> --members <userIds> --data-permission <json>
|
|
9
|
+
* openyida save-permission <appType> <formUuid> --matrix <json> --data-permission <json>
|
|
9
10
|
*
|
|
10
11
|
* 用法(新增权限组):
|
|
11
12
|
* openyida save-permission <appType> <formUuid> --create --name <权限组名称> [--members <userIds>] [--data-permission <json>] [--action-permission <json>] [--field-permission <json>]
|
|
@@ -13,6 +14,9 @@
|
|
|
13
14
|
* --members 参数:指定权限组成员,多个钉钉 userId 用逗号分隔
|
|
14
15
|
* 示例:--members "54255850977641,12345678901234"
|
|
15
16
|
* 不传则保持原有成员配置不变(更新模式)或仅包含管理员(新增模式)
|
|
17
|
+
* --all-members 参数:新增/更新权限组时设置为「全员可见」(roleType=DEFAULT, roleValue=ALL)
|
|
18
|
+
* --matrix 参数:使用权限矩阵作为权限成员,JSON 格式 {"matrixId":"MATRIX-XXX","columnId":"column_YYY"}
|
|
19
|
+
* 与 --members / --all-members 互斥
|
|
16
20
|
*
|
|
17
21
|
* 注意:--field-permission 透传宜搭 fieldPermit 原始 JSON,使用前建议先通过 get-permission 查看现有结构。
|
|
18
22
|
*/
|
|
@@ -39,6 +43,7 @@ const DATA_RANGE_TO_PERMIT_TYPE = {
|
|
|
39
43
|
FREE_LOGIN: 'FREE_LOGIN',
|
|
40
44
|
CUSTOM_DEPARTMENT: 'CUSTOM_DEPARTMENT',
|
|
41
45
|
FORMULA: 'FORMULA',
|
|
46
|
+
MATRIX: 'MATRIX',
|
|
42
47
|
};
|
|
43
48
|
|
|
44
49
|
// 所有支持的操作权限 key
|
|
@@ -62,9 +67,11 @@ const VALID_OPERATE_KEYS = [
|
|
|
62
67
|
function parseArgs(args) {
|
|
63
68
|
if (args.length < 2) {
|
|
64
69
|
throw new CliError([
|
|
65
|
-
'用法: openyida save-permission <appType> <formUuid> [--create --name <名称>] [--data-permission <json>] [--action-permission <json>] [--field-permission <json>] [--members <userIds>]',
|
|
70
|
+
'用法: openyida save-permission <appType> <formUuid> [--create --name <名称>] [--data-permission <json>] [--action-permission <json>] [--field-permission <json>] [--members <userIds>] [--all-members] [--matrix <json>]',
|
|
66
71
|
'示例(更新): openyida save-permission APP_XXX FORM-XXX --data-permission \'{"role":"DEFAULT","dataRange":"SELF"}\'',
|
|
67
|
-
'
|
|
72
|
+
'示例(新增全员): openyida save-permission APP_XXX FORM-XXX --create --name "全部人员看全部数据" --all-members --data-permission \'{"dataRange":"ALL"}\'',
|
|
73
|
+
'示例(新增指定人员): openyida save-permission APP_XXX FORM-XXX --create --name "只读权限组" --members "54255850977641"',
|
|
74
|
+
'示例(新增矩阵): openyida save-permission APP_XXX FORM-XXX --create --name "矩阵权限组" --matrix \'{"matrixId":"MATRIX-XXX","columnId":"column_YYY"}\' --data-permission \'{"rule":[{"type":"ORIGINATOR","value":"y"},{"type":"MATRIX","value":"y"}]}\'',
|
|
68
75
|
].join('\n'), {
|
|
69
76
|
code: 'SAVE_PERMISSION_INVALID_ARGUMENTS',
|
|
70
77
|
});
|
|
@@ -76,6 +83,8 @@ function parseArgs(args) {
|
|
|
76
83
|
let actionPermission = null;
|
|
77
84
|
let fieldPermission = null;
|
|
78
85
|
let members = null;
|
|
86
|
+
let allMembers = false;
|
|
87
|
+
let matrix = null;
|
|
79
88
|
let createMode = false;
|
|
80
89
|
let groupName = null;
|
|
81
90
|
|
|
@@ -116,6 +125,17 @@ function parseArgs(args) {
|
|
|
116
125
|
// 多个钉钉 userId 用逗号分隔,如 "54255850977641,12345678901234"
|
|
117
126
|
members = args[index + 1].split(',').map((id) => id.trim()).filter(Boolean);
|
|
118
127
|
index++;
|
|
128
|
+
} else if (args[index] === '--all-members') {
|
|
129
|
+
allMembers = true;
|
|
130
|
+
} else if (args[index] === '--matrix' && args[index + 1]) {
|
|
131
|
+
try {
|
|
132
|
+
matrix = JSON.parse(args[index + 1]);
|
|
133
|
+
} catch {
|
|
134
|
+
throw new CliError(`--matrix 参数 JSON 解析失败: ${args[index + 1]},格式: {"matrixId":"MATRIX-XXX","columnId":"column_YYY"}`, {
|
|
135
|
+
code: 'SAVE_PERMISSION_INVALID_ARGUMENTS',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
index++;
|
|
119
139
|
}
|
|
120
140
|
}
|
|
121
141
|
|
|
@@ -125,16 +145,39 @@ function parseArgs(args) {
|
|
|
125
145
|
});
|
|
126
146
|
}
|
|
127
147
|
|
|
128
|
-
if (!createMode && !dataPermission && !actionPermission && !fieldPermission && !members) {
|
|
129
|
-
throw new CliError('请至少提供 --data-permission、--action-permission、--field-permission 或 --
|
|
148
|
+
if (!createMode && !dataPermission && !actionPermission && !fieldPermission && !members && !matrix) {
|
|
149
|
+
throw new CliError('请至少提供 --data-permission、--action-permission、--field-permission、--members 或 --matrix 参数之一', {
|
|
130
150
|
code: 'SAVE_PERMISSION_INVALID_ARGUMENTS',
|
|
131
151
|
});
|
|
132
152
|
}
|
|
133
153
|
|
|
134
|
-
return { appType, formUuid, dataPermission, actionPermission, fieldPermission, members, createMode, groupName };
|
|
154
|
+
return { appType, formUuid, dataPermission, actionPermission, fieldPermission, members, allMembers, matrix, createMode, groupName };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function buildDataPermit(dataPermission) {
|
|
158
|
+
if (dataPermission && Array.isArray(dataPermission.rule)) {
|
|
159
|
+
return JSON.stringify(dataPermission);
|
|
160
|
+
}
|
|
161
|
+
const dataRange = (dataPermission && dataPermission.dataRange) || 'ALL';
|
|
162
|
+
const permitType = DATA_RANGE_TO_PERMIT_TYPE[dataRange] || dataRange;
|
|
163
|
+
return JSON.stringify({ rule: [{ type: permitType, value: 'y' }] });
|
|
135
164
|
}
|
|
136
165
|
|
|
137
166
|
function validateDataPermission(dataPermission) {
|
|
167
|
+
if (dataPermission && Array.isArray(dataPermission.rule)) {
|
|
168
|
+
const validTypes = new Set([
|
|
169
|
+
...Object.keys(DATA_RANGE_TO_PERMIT_TYPE),
|
|
170
|
+
...Object.values(DATA_RANGE_TO_PERMIT_TYPE),
|
|
171
|
+
]);
|
|
172
|
+
for (const item of dataPermission.rule) {
|
|
173
|
+
if (item.type && !validTypes.has(item.type)) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`无效的 rule type: ${item.type},有效值: ${Array.from(validTypes).join(', ')}`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
138
181
|
const validRanges = Object.keys(DATA_RANGE_TO_PERMIT_TYPE);
|
|
139
182
|
if (dataPermission.dataRange && !validRanges.includes(dataPermission.dataRange)) {
|
|
140
183
|
throw new Error(
|
|
@@ -143,6 +186,18 @@ function validateDataPermission(dataPermission) {
|
|
|
143
186
|
}
|
|
144
187
|
}
|
|
145
188
|
|
|
189
|
+
function validateMatrix(matrix) {
|
|
190
|
+
if (!matrix || typeof matrix !== 'object') {
|
|
191
|
+
throw new Error('--matrix 参数必须是 JSON 对象');
|
|
192
|
+
}
|
|
193
|
+
if (!matrix.matrixId || typeof matrix.matrixId !== 'string') {
|
|
194
|
+
throw new Error('--matrix 参数必须包含 matrixId 字符串');
|
|
195
|
+
}
|
|
196
|
+
if (!matrix.columnId || typeof matrix.columnId !== 'string') {
|
|
197
|
+
throw new Error('--matrix 参数必须包含 columnId 字符串');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
146
201
|
function validateActionPermission(actionPermission) {
|
|
147
202
|
if (!actionPermission.operations || typeof actionPermission.operations !== 'object') {
|
|
148
203
|
throw new Error(
|
|
@@ -280,7 +335,7 @@ function savePermitPackage(appType, formUuid, permitPackage, overrideMembers, au
|
|
|
280
335
|
}
|
|
281
336
|
|
|
282
337
|
async function run(args) {
|
|
283
|
-
const { appType, formUuid, dataPermission, actionPermission, fieldPermission, members, createMode, groupName } = parseArgs(args);
|
|
338
|
+
const { appType, formUuid, dataPermission, actionPermission, fieldPermission, members, allMembers, matrix, createMode, groupName } = parseArgs(args);
|
|
284
339
|
|
|
285
340
|
warn(SEP);
|
|
286
341
|
warn(' save-permission - 宜搭表单权限配置保存');
|
|
@@ -296,10 +351,17 @@ async function run(args) {
|
|
|
296
351
|
// Step 0: 参数校验
|
|
297
352
|
warn('\n📋 Step 0: 验证参数');
|
|
298
353
|
try {
|
|
354
|
+
if (matrix && (members || allMembers)) {
|
|
355
|
+
throw new Error('--matrix 与 --members / --all-members 互斥,请勿同时指定');
|
|
356
|
+
}
|
|
299
357
|
if (dataPermission) {
|
|
300
358
|
validateDataPermission(dataPermission);
|
|
301
359
|
warn(` ✅ 数据权限验证通过(dataRange: ${dataPermission.dataRange || 'ALL'})`);
|
|
302
360
|
}
|
|
361
|
+
if (matrix) {
|
|
362
|
+
validateMatrix(matrix);
|
|
363
|
+
warn(` ✅ 权限矩阵验证通过(matrixId: ${matrix.matrixId}, columnId: ${matrix.columnId})`);
|
|
364
|
+
}
|
|
303
365
|
if (actionPermission) {
|
|
304
366
|
validateActionPermission(actionPermission);
|
|
305
367
|
warn(' ✅ 操作权限验证通过');
|
|
@@ -335,6 +397,7 @@ async function run(args) {
|
|
|
335
397
|
// 构建新权限组数据
|
|
336
398
|
const dataRange = (dataPermission && dataPermission.dataRange) || 'ALL';
|
|
337
399
|
const permitType = DATA_RANGE_TO_PERMIT_TYPE[dataRange] || dataRange;
|
|
400
|
+
const dataPermitStr = buildDataPermit(dataPermission);
|
|
338
401
|
|
|
339
402
|
const newOperatePermit = {};
|
|
340
403
|
if (actionPermission) {
|
|
@@ -346,10 +409,19 @@ async function run(args) {
|
|
|
346
409
|
newOperatePermit['OPERATE_VIEW'] = 'y';
|
|
347
410
|
}
|
|
348
411
|
|
|
349
|
-
// 构建 roleData
|
|
350
|
-
|
|
351
|
-
if (
|
|
352
|
-
roleInclude
|
|
412
|
+
// 构建 roleData:--matrix / --all-members / --members / 默认管理员 四选一
|
|
413
|
+
let roleInclude;
|
|
414
|
+
if (matrix) {
|
|
415
|
+
roleInclude = [{ roleType: 'MATRIX', roleValue: [{ matrixId: matrix.matrixId, columnId: matrix.columnId }] }];
|
|
416
|
+
} else if (allMembers) {
|
|
417
|
+
roleInclude = [{ roleType: 'DEFAULT', roleValue: 'ALL' }];
|
|
418
|
+
} else if (members && members.length > 0) {
|
|
419
|
+
roleInclude = [
|
|
420
|
+
{ roleType: 'MANAGER', roleValue: 'appMainAdminRole,corpAdminRole' },
|
|
421
|
+
{ roleType: 'PERSONS', roleValue: members.join(',') },
|
|
422
|
+
];
|
|
423
|
+
} else {
|
|
424
|
+
roleInclude = [{ roleType: 'MANAGER', roleValue: 'appMainAdminRole,corpAdminRole' }];
|
|
353
425
|
}
|
|
354
426
|
|
|
355
427
|
const newPkg = {
|
|
@@ -357,7 +429,7 @@ async function run(args) {
|
|
|
357
429
|
packageName: { zh_CN: groupName, en_US: groupName, type: 'i18n' },
|
|
358
430
|
description: { zh_CN: groupName, en_US: groupName, type: 'i18n' },
|
|
359
431
|
roleData: JSON.stringify({ include: roleInclude }),
|
|
360
|
-
dataPermit:
|
|
432
|
+
dataPermit: dataPermitStr,
|
|
361
433
|
operatePermit: JSON.stringify(newOperatePermit),
|
|
362
434
|
customButtonPermit: '[]',
|
|
363
435
|
fieldPermit: JSON.stringify(normalizedFieldPermission || { fieldRange: 'FORM' }),
|
|
@@ -366,10 +438,18 @@ async function run(args) {
|
|
|
366
438
|
};
|
|
367
439
|
|
|
368
440
|
warn(` → 权限组名称: ${groupName}`);
|
|
369
|
-
|
|
441
|
+
if (dataPermission && Array.isArray(dataPermission.rule)) {
|
|
442
|
+
warn(` → 数据范围: 自定义规则(${dataPermission.rule.length} 条)`);
|
|
443
|
+
} else {
|
|
444
|
+
warn(` → 数据范围: ${dataRange} → ${permitType}`);
|
|
445
|
+
}
|
|
370
446
|
warn(` → 操作权限: ${Object.keys(newOperatePermit).join(', ') || '(无)'}`);
|
|
371
447
|
if (normalizedFieldPermission) {warn(' → 字段权限: 自定义 fieldPermit');}
|
|
372
|
-
if (
|
|
448
|
+
if (matrix) {
|
|
449
|
+
warn(` → 权限矩阵: ${matrix.matrixId} / ${matrix.columnId}`);
|
|
450
|
+
} else if (members) {
|
|
451
|
+
warn(` → 成员: ${members.join(', ')}`);
|
|
452
|
+
}
|
|
373
453
|
|
|
374
454
|
const createResult = await savePermitPackage(appType, formUuid, newPkg, null, authRef);
|
|
375
455
|
|
|
@@ -378,15 +458,28 @@ async function run(args) {
|
|
|
378
458
|
const newPackageUuid = createResult.content || '';
|
|
379
459
|
warn(' ✅ 权限组新增成功!');
|
|
380
460
|
warn(SEP);
|
|
461
|
+
const dataPermissionSummary = (dataPermission && Array.isArray(dataPermission.rule))
|
|
462
|
+
? `数据范围: 自定义规则(${dataPermission.rule.length} 条)`
|
|
463
|
+
: `数据范围: ${dataRange}`;
|
|
464
|
+
let membersSummary;
|
|
465
|
+
if (matrix) {
|
|
466
|
+
membersSummary = `权限矩阵: ${matrix.matrixId} / ${matrix.columnId}`;
|
|
467
|
+
} else if (allMembers) {
|
|
468
|
+
membersSummary = '成员: 全员';
|
|
469
|
+
} else if (members) {
|
|
470
|
+
membersSummary = `成员: ${members.join(', ')}`;
|
|
471
|
+
} else {
|
|
472
|
+
membersSummary = '仅管理员';
|
|
473
|
+
}
|
|
381
474
|
console.log(JSON.stringify({
|
|
382
475
|
success: true,
|
|
383
476
|
packageUuid: newPackageUuid,
|
|
384
477
|
summary: {
|
|
385
478
|
name: groupName,
|
|
386
|
-
dataPermission:
|
|
479
|
+
dataPermission: dataPermissionSummary,
|
|
387
480
|
actionPermission: `操作权限: ${Object.keys(newOperatePermit).join(', ') || '(无)'}`,
|
|
388
481
|
fieldPermission: normalizedFieldPermission ? '自定义 fieldPermit' : '全部字段',
|
|
389
|
-
members:
|
|
482
|
+
members: membersSummary,
|
|
390
483
|
},
|
|
391
484
|
message: '权限组已新增',
|
|
392
485
|
}, null, 2));
|
|
@@ -424,7 +517,10 @@ async function run(args) {
|
|
|
424
517
|
warn(` ✅ 获取到 ${packages.length} 个权限组`);
|
|
425
518
|
|
|
426
519
|
// 根据 role 筛选要更新的权限组
|
|
427
|
-
|
|
520
|
+
let targetRole = (dataPermission || actionPermission || fieldPermission || {}).role || 'DEFAULT';
|
|
521
|
+
if (matrix) {
|
|
522
|
+
targetRole = 'MATRIX';
|
|
523
|
+
}
|
|
428
524
|
const packagesToUpdate = packages.filter((pkg) => {
|
|
429
525
|
if (targetRole === 'DEFAULT') {
|
|
430
526
|
return pkg.roleMembers && pkg.roleMembers.some((rm) => rm.roleType === 'DEFAULT');
|
|
@@ -432,6 +528,9 @@ async function run(args) {
|
|
|
432
528
|
if (targetRole === 'MANAGER') {
|
|
433
529
|
return pkg.roleMembers && pkg.roleMembers.some((rm) => rm.roleType === 'MANAGER');
|
|
434
530
|
}
|
|
531
|
+
if (targetRole === 'MATRIX') {
|
|
532
|
+
return pkg.roleMembers && pkg.roleMembers.some((rm) => rm.roleType === 'MATRIX');
|
|
533
|
+
}
|
|
435
534
|
return true;
|
|
436
535
|
});
|
|
437
536
|
|
|
@@ -447,8 +546,12 @@ async function run(args) {
|
|
|
447
546
|
let permitType = null;
|
|
448
547
|
const stepParts = [];
|
|
449
548
|
if (dataPermission) {
|
|
450
|
-
|
|
451
|
-
|
|
549
|
+
if (Array.isArray(dataPermission.rule)) {
|
|
550
|
+
stepParts.push(`数据权限: 自定义规则(${dataPermission.rule.length} 条)`);
|
|
551
|
+
} else {
|
|
552
|
+
permitType = DATA_RANGE_TO_PERMIT_TYPE[dataPermission.dataRange] || dataPermission.dataRange;
|
|
553
|
+
stepParts.push(`数据权限: ${dataPermission.dataRange} → ${permitType}`);
|
|
554
|
+
}
|
|
452
555
|
}
|
|
453
556
|
if (actionPermission) {
|
|
454
557
|
stepParts.push('操作权限: 同步更新');
|
|
@@ -456,6 +559,9 @@ async function run(args) {
|
|
|
456
559
|
if (normalizedFieldPermission) {
|
|
457
560
|
stepParts.push('字段权限: 同步更新');
|
|
458
561
|
}
|
|
562
|
+
if (matrix) {
|
|
563
|
+
stepParts.push(`权限矩阵: ${matrix.matrixId} / ${matrix.columnId}`);
|
|
564
|
+
}
|
|
459
565
|
if (members) {
|
|
460
566
|
stepParts.push(`成员: ${members.join(', ')}`);
|
|
461
567
|
}
|
|
@@ -469,7 +575,7 @@ async function run(args) {
|
|
|
469
575
|
const updatedPkg = { ...pkg };
|
|
470
576
|
|
|
471
577
|
if (dataPermission) {
|
|
472
|
-
updatedPkg.dataPermit =
|
|
578
|
+
updatedPkg.dataPermit = buildDataPermit(dataPermission);
|
|
473
579
|
}
|
|
474
580
|
|
|
475
581
|
if (actionPermission) {
|
|
@@ -487,6 +593,18 @@ async function run(args) {
|
|
|
487
593
|
updatedPkg.fieldPermit = JSON.stringify(normalizedFieldPermission);
|
|
488
594
|
}
|
|
489
595
|
|
|
596
|
+
// --all-members 时强制将权限组改为全员可见
|
|
597
|
+
if (allMembers) {
|
|
598
|
+
updatedPkg.roleData = JSON.stringify({ include: [{ roleType: 'DEFAULT', roleValue: 'ALL' }] });
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// --matrix 时强制将权限组改为权限矩阵
|
|
602
|
+
if (matrix) {
|
|
603
|
+
updatedPkg.roleData = JSON.stringify({
|
|
604
|
+
include: [{ roleType: 'MATRIX', roleValue: [{ matrixId: matrix.matrixId, columnId: matrix.columnId }] }],
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
|
|
490
608
|
// members 参数传给 savePermitPackage,null 表示不修改成员
|
|
491
609
|
const overrideMembers = members || null;
|
|
492
610
|
|
|
@@ -524,7 +642,9 @@ module.exports = {
|
|
|
524
642
|
parseArgs,
|
|
525
643
|
validateDataPermission,
|
|
526
644
|
validateActionPermission,
|
|
645
|
+
validateMatrix,
|
|
527
646
|
normalizeFieldPermission,
|
|
647
|
+
buildDataPermit,
|
|
528
648
|
fetchPermitPackages,
|
|
529
649
|
buildRoleData,
|
|
530
650
|
savePermitPackage,
|
package/package.json
CHANGED
|
@@ -110,6 +110,7 @@ openyida data query form <appType> <formUuid> [--page 1 --size 20] [--search-jso
|
|
|
110
110
|
openyida data get form <appType> --inst-id <formInstId>
|
|
111
111
|
openyida data create form <appType> <formUuid> --data-json '<json>' [--resolve-aliases]
|
|
112
112
|
openyida data create form <appType> <formUuid> --data-file .cache/openyida/<项目名或任务名>/data-import/record.json [--resolve-aliases]
|
|
113
|
+
> `create form` 会自动探测表单类型;当目标表单为流程表单时,会改用 `/v1/process/startInstance.json` 发起流程。若已知 `processCode`,仍推荐显式使用 `create process`。
|
|
113
114
|
openyida data update form <appType> --inst-id <formInstId> --form-uuid <formUuid> --data-json '<json>' [--resolve-aliases]
|
|
114
115
|
openyida data update form <appType> --inst-id <formInstId> --form-uuid <formUuid> --data-file .cache/openyida/<项目名或任务名>/data-import/patch.json [--resolve-aliases]
|
|
115
116
|
openyida data query subform <appType> <formUuid> --inst-id <formInstId> --table-field-id <fieldId|alias> [--page 1 --size 100] [--resolve-aliases]
|
|
@@ -55,6 +55,8 @@ openyida save-permission <appType> <formUuid> [选项]
|
|
|
55
55
|
| `--action-permission <json>` | 修改操作权限(完全替换,只保留 true 的项) |
|
|
56
56
|
| `--field-permission <json>` | 修改字段权限,传入宜搭 `fieldPermit` 对象或 `{ "role": "DEFAULT", "fieldPermit": {...} }` |
|
|
57
57
|
| `--members <userIds>` | 修改成员,多个 userId 逗号分隔 |
|
|
58
|
+
| `--all-members` | 设置权限组为「全员可见」(`roleData.include` 为 `DEFAULT/ALL`) |
|
|
59
|
+
| `--matrix <json>` | 使用权限矩阵作为权限成员,JSON 格式 `{"matrixId":"MATRIX-XXX","columnId":"column_YYY"}`,与 `--members` / `--all-members` 互斥 |
|
|
58
60
|
|
|
59
61
|
### 数据权限 `dataRange` 可选值
|
|
60
62
|
|
|
@@ -65,6 +67,42 @@ openyida save-permission <appType> <formUuid> [选项]
|
|
|
65
67
|
| `DEPARTMENT` / `ORIGINATOR_DEPARTMENT` | 本部门提交 |
|
|
66
68
|
| `SAME_LEVEL_DEPARTMENT` | 同级部门 |
|
|
67
69
|
| `SUBORDINATE_DEPARTMENT` | 下级部门 |
|
|
70
|
+
| `MATRIX` | 权限矩阵条件 |
|
|
71
|
+
|
|
72
|
+
> 如需同时设置多个数据范围、自定义部门或自定义过滤条件,可直接传入宜搭完整的 `dataPermit` JSON(必须包含 `rule` 数组)。例如截图中的「本人提交 + 本部门 + 同级部门 + 下级部门 + 免登 + 自定义部门 + 自定义过滤条件」可表示为:
|
|
73
|
+
>
|
|
74
|
+
> ```json
|
|
75
|
+
> {
|
|
76
|
+
> "rule": [
|
|
77
|
+
> { "type": "ORIGINATOR", "value": "y" },
|
|
78
|
+
> { "type": "ORIGINATOR_DEPARTMENT", "value": "y" },
|
|
79
|
+
> { "type": "SAME_LEVEL_DEPARTMENT", "value": "y" },
|
|
80
|
+
> { "type": "SUBORDINATE_DEPARTMENT", "value": "y" },
|
|
81
|
+
> { "type": "FREE_LOGIN", "value": "y" },
|
|
82
|
+
> { "type": "CUSTOM_DEPARTMENT", "value": "y" },
|
|
83
|
+
> { "type": "FORMULA", "value": "y" }
|
|
84
|
+
> ],
|
|
85
|
+
> "customDepartmentData": {
|
|
86
|
+
> "departmentIds": ["637215248"],
|
|
87
|
+
> "drillDown": "n"
|
|
88
|
+
> },
|
|
89
|
+
> "formulaData": {
|
|
90
|
+
> "condition": "OR",
|
|
91
|
+
> "ruleId": "group-xxx",
|
|
92
|
+
> "rules": []
|
|
93
|
+
> }
|
|
94
|
+
> }
|
|
95
|
+
> ```
|
|
96
|
+
>
|
|
97
|
+
> 命令示例:
|
|
98
|
+
>
|
|
99
|
+
> ```bash
|
|
100
|
+
> openyida save-permission APP_XXX FORM_XXX \
|
|
101
|
+
> --create --name "全部成员可查看本人提交数据" \
|
|
102
|
+
> --all-members \
|
|
103
|
+
> --data-permission '{"rule":[{"type":"ORIGINATOR","value":"y"},{"type":"ORIGINATOR_DEPARTMENT","value":"y"},{"type":"SAME_LEVEL_DEPARTMENT","value":"y"},{"type":"SUBORDINATE_DEPARTMENT","value":"y"},{"type":"FREE_LOGIN","value":"y"},{"type":"CUSTOM_DEPARTMENT","value":"y"},{"type":"FORMULA","value":"y"}],"customDepartmentData":{"departmentIds":["637215248"],"drillDown":"n"},"formulaData":{"condition":"OR","ruleId":"group-xxx","rules":[]}}' \
|
|
104
|
+
> --action-permission '{"operations":{"OPERATE_VIEW":true,"OPERATE_EDIT":true,"OPERATE_DELETE":true,"OPERATE_HISTORY":true,"OPERATE_COMMENT":true,"OPERATE_PRINT":true}}'
|
|
105
|
+
> ```
|
|
68
106
|
|
|
69
107
|
### 操作权限 key
|
|
70
108
|
|
|
@@ -79,7 +117,7 @@ openyida save-permission <appType> <formUuid> --create --name <名称> [选项]
|
|
|
79
117
|
示例:
|
|
80
118
|
|
|
81
119
|
```bash
|
|
82
|
-
openyida save-permission APP_XXX
|
|
120
|
+
openyida save-permission APP_XXX FORM_XXX \
|
|
83
121
|
--create --name "部门数据查看组" \
|
|
84
122
|
--members "54255850977641" \
|
|
85
123
|
--data-permission '{"dataRange":"ORIGINATOR_DEPARTMENT"}' \
|
|
@@ -87,6 +125,30 @@ openyida save-permission APP_XXX FORM-XXX \
|
|
|
87
125
|
--field-permission '{"fieldRange":"FORM"}'
|
|
88
126
|
```
|
|
89
127
|
|
|
128
|
+
> 设置「全部人员看全部数据」时,必须加上 `--all-members`,确保 `roleData.include` 为 `DEFAULT/ALL`:
|
|
129
|
+
>
|
|
130
|
+
> ```bash
|
|
131
|
+
> openyida save-permission APP_XXX FORM_XXX \
|
|
132
|
+
> --create --name "全部人员看全部数据" \
|
|
133
|
+
> --all-members \
|
|
134
|
+
> --data-permission '{"dataRange":"ALL"}' \
|
|
135
|
+
> --action-permission '{"operations":{"OPERATE_VIEW":true}}'
|
|
136
|
+
> ```
|
|
137
|
+
>
|
|
138
|
+
> 若目标表单已存在 DEFAULT 权限组,也可直接用 `--all-members --data-permission '{"dataRange":"ALL"}'` 更新该组。
|
|
139
|
+
>
|
|
140
|
+
> ### 使用权限矩阵
|
|
141
|
+
> 1. 先在宜搭后台「权限矩阵」中获取目标矩阵 ID 与结果列 columnId;或调用底层服务 `/query/matrix/getMatrixList.json` / `/query/matrix/getMatrixById.json` 查询。
|
|
142
|
+
> 2. 创建/更新权限组时指定 `--matrix '{"matrixId":"MATRIX-XXX","columnId":"column_YYY"}'`,并配合 `--data-permission` 设置包含 `MATRIX` 的数据范围。
|
|
143
|
+
>
|
|
144
|
+
> ```bash
|
|
145
|
+
> openyida save-permission APP_XXX FORM_XXX \
|
|
146
|
+
> --create --name "使用权限矩阵的权限组" \
|
|
147
|
+
> --matrix '{"matrixId":"MATRIX-XNCVJYB60YW7L0HPY9HE","columnId":"column_1767839664612"}' \
|
|
148
|
+
> --data-permission '{"rule":[{"type":"ORIGINATOR","value":"y"},{"type":"MATRIX","value":"y"}]}' \
|
|
149
|
+
> --action-permission '{"operations":{"OPERATE_VIEW":true,"OPERATE_EDIT":true,"OPERATE_DELETE":true,"OPERATE_HISTORY":true,"OPERATE_COMMENT":true,"OPERATE_PRINT":true}}'
|
|
150
|
+
> ```
|
|
151
|
+
|
|
90
152
|
## 字段权限
|
|
91
153
|
|
|
92
154
|
- 默认结构通常是 `{ "fieldRange": "FORM" }`,表示继承表单设计中组件状态。
|