openyida 2026.7.23-2 → 2026.7.25-beta.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/lib/app/canvas-compile.js +46 -1
- package/lib/app/create-form.js +132 -20
- package/lib/auth/token-auth.js +13 -0
- package/lib/auth/token-store.js +94 -2
- package/lib/core/agent-capabilities.js +3 -0
- package/lib/core/utils.js +11 -4
- package/package.json +1 -1
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +1 -0
- package/yida-skills/skills-index.json +9 -0
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* 这些 UMD 依赖由画布运行时依据 importedModules 白名单按需注入。
|
|
26
26
|
*
|
|
27
27
|
* 因此本地编译 = Babel 把 JSX/TS 转成 ES5 → 把 import 改写成 window 别名引用、
|
|
28
|
-
* 把 export default
|
|
28
|
+
* 把 export default 改写成画布入口 `YidaComp` → 正则抽出依赖包名。
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
const Babel = require('@babel/standalone');
|
|
@@ -152,6 +152,15 @@ function esmToWindowPlugin({ types: t }, options = {}) {
|
|
|
152
152
|
);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function hasProgramBinding(path, name) {
|
|
156
|
+
const program = path.findParent((parentPath) => parentPath.isProgram());
|
|
157
|
+
return Boolean(program && program.scope.hasBinding(name));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function isExistingYidaCompDefault(path, decl) {
|
|
161
|
+
return t.isIdentifier(decl, { name: 'YidaComp' }) && hasProgramBinding(path, 'YidaComp');
|
|
162
|
+
}
|
|
163
|
+
|
|
155
164
|
return {
|
|
156
165
|
name: 'yida-esm-to-window',
|
|
157
166
|
visitor: {
|
|
@@ -217,6 +226,10 @@ function esmToWindowPlugin({ types: t }, options = {}) {
|
|
|
217
226
|
const decl = path.node.declaration;
|
|
218
227
|
if (t.isFunctionDeclaration(decl) || t.isClassDeclaration(decl)) {
|
|
219
228
|
if (decl.id) {
|
|
229
|
+
if (decl.id.name === 'YidaComp') {
|
|
230
|
+
path.replaceWith(decl);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
220
233
|
// 具名声明:保留声明本体,再补 `var YidaComp = <name>;`
|
|
221
234
|
const idName = decl.id.name;
|
|
222
235
|
path.replaceWithMultiple([
|
|
@@ -235,6 +248,10 @@ function esmToWindowPlugin({ types: t }, options = {}) {
|
|
|
235
248
|
);
|
|
236
249
|
return;
|
|
237
250
|
}
|
|
251
|
+
if (isExistingYidaCompDefault(path, decl)) {
|
|
252
|
+
path.remove();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
238
255
|
// export default <表达式>
|
|
239
256
|
path.replaceWith(
|
|
240
257
|
t.variableDeclaration('var', [t.variableDeclarator(t.identifier('YidaComp'), decl)])
|
|
@@ -258,6 +275,32 @@ function esmToWindowPlugin({ types: t }, options = {}) {
|
|
|
258
275
|
};
|
|
259
276
|
}
|
|
260
277
|
|
|
278
|
+
function buildCanvasRuntimeWrapper(runtimeCode) {
|
|
279
|
+
return (
|
|
280
|
+
'return function(iframeWindow, parentWindow){ const window = iframeWindow; '
|
|
281
|
+
+ runtimeCode
|
|
282
|
+
+ ' return YidaComp; }'
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function assertCanvasRuntimeParseable(runtimeCode, options = {}) {
|
|
287
|
+
try {
|
|
288
|
+
// 只编译 wrapper,不执行 factory 或用户组件,避免触发源码里的运行期副作用。
|
|
289
|
+
// eslint-disable-next-line no-new-func
|
|
290
|
+
new Function(buildCanvasRuntimeWrapper(runtimeCode));
|
|
291
|
+
} catch (error) {
|
|
292
|
+
const detail = error && error.message ? error.message : String(error);
|
|
293
|
+
const sourceLabel = options.sourcePath ? ` (${options.sourcePath})` : '';
|
|
294
|
+
const compileError = new Error(
|
|
295
|
+
`Code Canvas runtimeCode 无法通过画布运行时装配校验${sourceLabel}: ${detail}`
|
|
296
|
+
+ '。请检查默认导出、YidaComp 入口或 window 等运行时保留变量是否重复声明。'
|
|
297
|
+
);
|
|
298
|
+
compileError.code = 'OPENYIDA_CANVAS_RUNTIME_PARSE_FAILED';
|
|
299
|
+
compileError.cause = error;
|
|
300
|
+
throw compileError;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
261
304
|
/**
|
|
262
305
|
* 本地编译 Code Canvas 源码。
|
|
263
306
|
* @param {string} source 原始 React/JSX/TSX 源码
|
|
@@ -322,6 +365,7 @@ function compileCanvasLocal(source, options = {}) {
|
|
|
322
365
|
artifact: options.sourcePath ? options.sourcePath + ' runtime' : 'Code Canvas runtime',
|
|
323
366
|
code: 'OPENYIDA_CANVAS_SOURCE_EMOJI_FORBIDDEN',
|
|
324
367
|
});
|
|
368
|
+
assertCanvasRuntimeParseable(runtimeCode, options);
|
|
325
369
|
return {
|
|
326
370
|
runtimeCode,
|
|
327
371
|
importedModules: JSON.stringify(importedModules),
|
|
@@ -356,5 +400,6 @@ module.exports = {
|
|
|
356
400
|
extractImportedModules,
|
|
357
401
|
resolveWindowAlias,
|
|
358
402
|
shouldAllowUnsupportedBareImports,
|
|
403
|
+
assertCanvasRuntimeParseable,
|
|
359
404
|
MODULE_ALIAS_MAP,
|
|
360
405
|
};
|
package/lib/app/create-form.js
CHANGED
|
@@ -4334,6 +4334,109 @@ function sanitizeFailureResult(result) {
|
|
|
4334
4334
|
return sanitized;
|
|
4335
4335
|
}
|
|
4336
4336
|
|
|
4337
|
+
function getUpdateFormConfigRetryDelays() {
|
|
4338
|
+
const rawDelays = process.env.OPENYIDA_UPDATE_FORM_CONFIG_RETRY_DELAYS_MS;
|
|
4339
|
+
if (rawDelays === undefined) {
|
|
4340
|
+
return [500, 1000];
|
|
4341
|
+
}
|
|
4342
|
+
return String(rawDelays)
|
|
4343
|
+
.split(',')
|
|
4344
|
+
.map(function (part) { return Number(part.trim()); })
|
|
4345
|
+
.filter(function (delay) { return Number.isFinite(delay) && delay >= 0; });
|
|
4346
|
+
}
|
|
4347
|
+
|
|
4348
|
+
function waitForUpdateFormConfigRetry(delayMs) {
|
|
4349
|
+
if (!delayMs) {
|
|
4350
|
+
return Promise.resolve();
|
|
4351
|
+
}
|
|
4352
|
+
return new Promise(function (resolve) {
|
|
4353
|
+
setTimeout(resolve, delayMs);
|
|
4354
|
+
});
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
function getUpdateFormConfigErrorText(result) {
|
|
4358
|
+
if (!result || typeof result !== 'object') {
|
|
4359
|
+
return '';
|
|
4360
|
+
}
|
|
4361
|
+
return [
|
|
4362
|
+
result.errorMsg,
|
|
4363
|
+
result.message,
|
|
4364
|
+
result.error,
|
|
4365
|
+
result.errorCode,
|
|
4366
|
+
result.code,
|
|
4367
|
+
]
|
|
4368
|
+
.filter(function (part) { return part !== undefined && part !== null; })
|
|
4369
|
+
.map(function (part) { return String(part); })
|
|
4370
|
+
.join(' ');
|
|
4371
|
+
}
|
|
4372
|
+
|
|
4373
|
+
function getUpdateFormConfigWarningMessage(result) {
|
|
4374
|
+
if (!result || typeof result !== 'object') {
|
|
4375
|
+
return t('common.request_failed');
|
|
4376
|
+
}
|
|
4377
|
+
return result.errorMsg || result.message || result.error || result.errorCode || result.code || t('common.unknown_error');
|
|
4378
|
+
}
|
|
4379
|
+
|
|
4380
|
+
function isRetryablePostSaveUpdateConfigResult(result) {
|
|
4381
|
+
if (!result || result.success || result.__needLogin || result.__csrfExpired) {
|
|
4382
|
+
return false;
|
|
4383
|
+
}
|
|
4384
|
+
const status = Number(result.__httpStatus || result.status || result.statusCode);
|
|
4385
|
+
if (status === 400 || status === 401 || status === 403 || status >= 500) {
|
|
4386
|
+
return false;
|
|
4387
|
+
}
|
|
4388
|
+
const text = getUpdateFormConfigErrorText(result);
|
|
4389
|
+
const lowerText = text.toLowerCase();
|
|
4390
|
+
const normalizedText = lowerText.replace(/[\s_-]/g, '');
|
|
4391
|
+
if (/csrf|auth|login|permission|forbidden|unauthori[sz]ed|invalid|param|权限|登录|认证|授权|参数/.test(lowerText)) {
|
|
4392
|
+
return false;
|
|
4393
|
+
}
|
|
4394
|
+
if (text.indexOf('表单') !== -1 && text.indexOf('不存在') !== -1) {
|
|
4395
|
+
return true;
|
|
4396
|
+
}
|
|
4397
|
+
if (text.indexOf('表单') !== -1 && text.indexOf('未找到') !== -1) {
|
|
4398
|
+
return true;
|
|
4399
|
+
}
|
|
4400
|
+
if (
|
|
4401
|
+
normalizedText.indexOf('formnotfound') !== -1 ||
|
|
4402
|
+
normalizedText.indexOf('formnotexist') !== -1 ||
|
|
4403
|
+
normalizedText.indexOf('formdoesnotexist') !== -1
|
|
4404
|
+
) {
|
|
4405
|
+
return true;
|
|
4406
|
+
}
|
|
4407
|
+
return lowerText.indexOf('not found') !== -1 && /form|formuuid|form uuid/.test(lowerText);
|
|
4408
|
+
}
|
|
4409
|
+
|
|
4410
|
+
async function sendPostSaveUpdateConfigRequest(authRef, appType, formUuid, version) {
|
|
4411
|
+
const retryDelays = getUpdateFormConfigRetryDelays();
|
|
4412
|
+
for (let attemptIndex = 0; attemptIndex <= retryDelays.length; attemptIndex += 1) {
|
|
4413
|
+
let configResult;
|
|
4414
|
+
try {
|
|
4415
|
+
configResult = await requestWithAutoLogin(function (auth) {
|
|
4416
|
+
return sendUpdateConfigRequest(auth.baseUrl, appType, formUuid, version || 1, 0, auth);
|
|
4417
|
+
}, authRef);
|
|
4418
|
+
} catch (err) {
|
|
4419
|
+
return {
|
|
4420
|
+
success: false,
|
|
4421
|
+
errorMsg: err && err.message ? err.message : String(err || 'request failed'),
|
|
4422
|
+
errorCode: err && err.code ? err.code : 'CREATE_FORM_UPDATE_CONFIG_FAILED',
|
|
4423
|
+
};
|
|
4424
|
+
}
|
|
4425
|
+
if (configResult && configResult.success) {
|
|
4426
|
+
return configResult;
|
|
4427
|
+
}
|
|
4428
|
+
if (attemptIndex >= retryDelays.length || !isRetryablePostSaveUpdateConfigResult(configResult)) {
|
|
4429
|
+
return configResult;
|
|
4430
|
+
}
|
|
4431
|
+
await waitForUpdateFormConfigRetry(retryDelays[attemptIndex]);
|
|
4432
|
+
}
|
|
4433
|
+
return {
|
|
4434
|
+
success: false,
|
|
4435
|
+
errorMsg: t('common.request_failed'),
|
|
4436
|
+
errorCode: 'CREATE_FORM_UPDATE_CONFIG_FAILED',
|
|
4437
|
+
};
|
|
4438
|
+
}
|
|
4439
|
+
|
|
4337
4440
|
function buildCreateFormPostCreateFailurePayload(context) {
|
|
4338
4441
|
const errorObject = context && context.error;
|
|
4339
4442
|
const payload = {
|
|
@@ -4387,6 +4490,7 @@ function assertNoEmojiInDefinitionFileName(value) {
|
|
|
4387
4490
|
// ── 保存 Schema 并更新表单配置(create/update 共用)──
|
|
4388
4491
|
//
|
|
4389
4492
|
// 封装了 saveFormSchema + updateFormConfig 两步,以及各自的 302 自动重登录重试。
|
|
4493
|
+
// direct create-form 语义:saveFormSchema 成功后,updateFormConfig 只是后置配置通知。
|
|
4390
4494
|
// 返回 { saveResult, configResult }。
|
|
4391
4495
|
|
|
4392
4496
|
async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, version, stepOffset, failureContext) {
|
|
@@ -4447,9 +4551,7 @@ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, ver
|
|
|
4447
4551
|
step(configStep, t('create_form.step_update_config', configStep));
|
|
4448
4552
|
info(t('create_form.sending_config'));
|
|
4449
4553
|
|
|
4450
|
-
const configResult = await
|
|
4451
|
-
return sendUpdateConfigRequest(auth.baseUrl, appType, formUuid, version || 1, 0, auth);
|
|
4452
|
-
}, authRef);
|
|
4554
|
+
const configResult = await sendPostSaveUpdateConfigRequest(authRef, appType, formUuid, version);
|
|
4453
4555
|
|
|
4454
4556
|
return { saveResult: saveResult, configResult: configResult };
|
|
4455
4557
|
}
|
|
@@ -4571,31 +4673,29 @@ async function mainCreate(parsedArgs, authRef) {
|
|
|
4571
4673
|
parsedArgs.browserOpenMode
|
|
4572
4674
|
)));
|
|
4573
4675
|
} else {
|
|
4574
|
-
const configErrorMsg = configResult
|
|
4575
|
-
result(
|
|
4676
|
+
const configErrorMsg = getUpdateFormConfigWarningMessage(configResult);
|
|
4677
|
+
result(true, t('create_form.create_success'), [
|
|
4576
4678
|
['Form UUID', formUuid],
|
|
4577
4679
|
['URL', formUrl],
|
|
4578
4680
|
]);
|
|
4681
|
+
warn(t('create_form.config_failed', configErrorMsg));
|
|
4579
4682
|
hint(t('create_form.schema_ok_config_failed'));
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
result: sanitizeFailureResult(configResult),
|
|
4584
|
-
});
|
|
4585
|
-
console.log(JSON.stringify(Object.assign(
|
|
4586
|
-
buildCreateFormPostCreateFailurePayload({
|
|
4683
|
+
console.log(JSON.stringify(withBrowserHandoff(
|
|
4684
|
+
{
|
|
4685
|
+
success: true,
|
|
4587
4686
|
appType,
|
|
4588
4687
|
formTitle,
|
|
4589
4688
|
formUuid,
|
|
4590
4689
|
fieldCount,
|
|
4591
|
-
stage: 'updateFormConfig',
|
|
4592
|
-
error: configError,
|
|
4593
|
-
}),
|
|
4594
|
-
{
|
|
4595
4690
|
url: formUrl,
|
|
4691
|
+
stage: 'updateFormConfig',
|
|
4596
4692
|
schemaSaved: true,
|
|
4597
4693
|
configWarning: configErrorMsg,
|
|
4598
|
-
|
|
4694
|
+
configResult: sanitizeFailureResult(configResult),
|
|
4695
|
+
},
|
|
4696
|
+
formUrl,
|
|
4697
|
+
{ stage: 'create_form_success', title: formTitle },
|
|
4698
|
+
parsedArgs.browserOpenMode
|
|
4599
4699
|
)));
|
|
4600
4700
|
}
|
|
4601
4701
|
}
|
|
@@ -5517,15 +5617,27 @@ async function mainUpdate(parsedArgs, authRef) {
|
|
|
5517
5617
|
parsedArgs.browserOpenMode
|
|
5518
5618
|
)));
|
|
5519
5619
|
} else {
|
|
5520
|
-
const configErrorMsg = configResult
|
|
5521
|
-
result(
|
|
5620
|
+
const configErrorMsg = getUpdateFormConfigWarningMessage(configResult);
|
|
5621
|
+
result(true, t('create_form.update_success'), [
|
|
5522
5622
|
['Form UUID', formUuid],
|
|
5523
5623
|
['URL', formUrl],
|
|
5524
5624
|
['Changes', String(appliedChanges.length)],
|
|
5525
5625
|
]);
|
|
5626
|
+
warn(t('create_form.config_failed', configErrorMsg));
|
|
5526
5627
|
hint(t('create_form.schema_ok_config_failed'));
|
|
5527
5628
|
console.log(JSON.stringify(withBrowserHandoff(
|
|
5528
|
-
{
|
|
5629
|
+
{
|
|
5630
|
+
success: true,
|
|
5631
|
+
formUuid,
|
|
5632
|
+
appType,
|
|
5633
|
+
changesApplied: appliedChanges.length,
|
|
5634
|
+
changes: appliedChanges,
|
|
5635
|
+
url: formUrl,
|
|
5636
|
+
stage: 'updateFormConfig',
|
|
5637
|
+
schemaSaved: true,
|
|
5638
|
+
configWarning: configErrorMsg,
|
|
5639
|
+
configResult: sanitizeFailureResult(configResult),
|
|
5640
|
+
},
|
|
5529
5641
|
formUrl,
|
|
5530
5642
|
{ stage: 'update_form_success', title: formUuid },
|
|
5531
5643
|
parsedArgs.browserOpenMode
|
package/lib/auth/token-auth.js
CHANGED
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
loadTokenSession,
|
|
18
18
|
maskToken,
|
|
19
19
|
normalizeTokenSession,
|
|
20
|
+
saveBusinessContext,
|
|
20
21
|
saveTokenSession,
|
|
21
22
|
} = require('./token-store');
|
|
22
23
|
|
|
@@ -380,6 +381,7 @@ async function tokenRefresh(options = {}) {
|
|
|
380
381
|
});
|
|
381
382
|
requireOkResponse(response);
|
|
382
383
|
const data = getTokenPayload(response);
|
|
384
|
+
const responseBusinessBaseUrl = data.base_url || data.baseUrl;
|
|
383
385
|
const businessBaseUrl = session.base_url || authBaseUrlValue;
|
|
384
386
|
const normalized = normalizeTokenResponse({
|
|
385
387
|
...data,
|
|
@@ -397,6 +399,17 @@ async function tokenRefresh(options = {}) {
|
|
|
397
399
|
message: normalized.message || 'auth service did not return access_token',
|
|
398
400
|
};
|
|
399
401
|
}
|
|
402
|
+
if (responseBusinessBaseUrl && normalized.corp_id) {
|
|
403
|
+
try {
|
|
404
|
+
saveBusinessContext({
|
|
405
|
+
corp_id: normalized.corp_id,
|
|
406
|
+
base_url: responseBusinessBaseUrl,
|
|
407
|
+
}, options);
|
|
408
|
+
} catch {
|
|
409
|
+
// The refresh result remains usable in this process even when the
|
|
410
|
+
// cross-process business context cache cannot be written.
|
|
411
|
+
}
|
|
412
|
+
}
|
|
400
413
|
const env = options.env || process.env;
|
|
401
414
|
if (isHostInjectedTokenMode(env) && session.auth_source === 'env') {
|
|
402
415
|
env.OPENYIDA_ACCESS_TOKEN = normalized.access_token;
|
package/lib/auth/token-store.js
CHANGED
|
@@ -4,6 +4,8 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
6
|
const TOKEN_FILE_PREFIX = 'auth-token';
|
|
7
|
+
const BUSINESS_CONTEXT_FILE_PREFIX = 'auth-token-business-context';
|
|
8
|
+
const BUSINESS_CONTEXT_VERSION = 1;
|
|
7
9
|
|
|
8
10
|
function sanitizeEnvName(envName) {
|
|
9
11
|
return String(envName || 'public').replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
@@ -36,17 +38,94 @@ function getTokenFilePath(options = {}) {
|
|
|
36
38
|
return path.join(root, '.cache', `${TOKEN_FILE_PREFIX}-${envName}.json`);
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
function getBusinessContextFilePath(options = {}) {
|
|
42
|
+
const root = resolveProjectRoot(options);
|
|
43
|
+
const envName = resolveEnvName(options);
|
|
44
|
+
return path.join(
|
|
45
|
+
root,
|
|
46
|
+
'.cache',
|
|
47
|
+
'openyida',
|
|
48
|
+
`${BUSINESS_CONTEXT_FILE_PREFIX}-${envName}.json`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
function normalizeBaseUrl(value) {
|
|
40
53
|
if (!value) {
|
|
41
54
|
return undefined;
|
|
42
55
|
}
|
|
43
56
|
try {
|
|
44
|
-
|
|
57
|
+
const parsed = new URL(String(value));
|
|
58
|
+
if (
|
|
59
|
+
!['http:', 'https:'].includes(parsed.protocol) ||
|
|
60
|
+
parsed.username ||
|
|
61
|
+
parsed.password
|
|
62
|
+
) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return parsed.origin.replace(/\/+$/, '');
|
|
45
66
|
} catch {
|
|
46
67
|
return undefined;
|
|
47
68
|
}
|
|
48
69
|
}
|
|
49
70
|
|
|
71
|
+
function normalizeBusinessContext(value = {}) {
|
|
72
|
+
if (!value || typeof value !== 'object') {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const corpId = String(value.corp_id || value.corpId || '').trim();
|
|
76
|
+
const baseUrl = normalizeBaseUrl(value.base_url || value.baseUrl);
|
|
77
|
+
if (!corpId || !baseUrl) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
version: BUSINESS_CONTEXT_VERSION,
|
|
82
|
+
corp_id: corpId,
|
|
83
|
+
base_url: baseUrl,
|
|
84
|
+
updated_at: value.updated_at || value.updatedAt || new Date().toISOString(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function saveBusinessContext(value, options = {}) {
|
|
89
|
+
const normalized = normalizeBusinessContext(value);
|
|
90
|
+
if (!normalized) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const contextFile = getBusinessContextFilePath(options);
|
|
95
|
+
const tempFile = `${contextFile}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
96
|
+
fs.mkdirSync(path.dirname(contextFile), { recursive: true });
|
|
97
|
+
try {
|
|
98
|
+
fs.writeFileSync(tempFile, JSON.stringify(normalized, null, 2), { mode: 0o600 });
|
|
99
|
+
fs.renameSync(tempFile, contextFile);
|
|
100
|
+
try {
|
|
101
|
+
fs.chmodSync(contextFile, 0o600);
|
|
102
|
+
} catch {
|
|
103
|
+
// chmod is best-effort on non-POSIX filesystems.
|
|
104
|
+
}
|
|
105
|
+
} finally {
|
|
106
|
+
try {
|
|
107
|
+
if (fs.existsSync(tempFile)) {
|
|
108
|
+
fs.unlinkSync(tempFile);
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
// Cleanup is best-effort.
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return normalized;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function loadBusinessContext(options = {}) {
|
|
118
|
+
const contextFile = getBusinessContextFilePath(options);
|
|
119
|
+
if (!fs.existsSync(contextFile)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
return normalizeBusinessContext(JSON.parse(fs.readFileSync(contextFile, 'utf8')));
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
50
129
|
function normalizeExpiresAt(session) {
|
|
51
130
|
if (!session) {
|
|
52
131
|
return undefined;
|
|
@@ -160,7 +239,17 @@ function loadTokenSession(options = {}) {
|
|
|
160
239
|
const env = options.env || process.env;
|
|
161
240
|
const envSession = loadEnvTokenSession(env);
|
|
162
241
|
if (isHostInjectedTokenMode(env)) {
|
|
163
|
-
|
|
242
|
+
if (!envSession || !envSession.corp_id) {
|
|
243
|
+
return envSession;
|
|
244
|
+
}
|
|
245
|
+
const businessContext = loadBusinessContext(options);
|
|
246
|
+
if (!businessContext || businessContext.corp_id !== envSession.corp_id) {
|
|
247
|
+
return envSession;
|
|
248
|
+
}
|
|
249
|
+
return normalizeTokenSession({
|
|
250
|
+
...envSession,
|
|
251
|
+
base_url: businessContext.base_url,
|
|
252
|
+
});
|
|
164
253
|
}
|
|
165
254
|
|
|
166
255
|
const localSession = loadLocalTokenSession(options);
|
|
@@ -221,8 +310,11 @@ function maskToken(token) {
|
|
|
221
310
|
|
|
222
311
|
module.exports = {
|
|
223
312
|
getTokenFilePath,
|
|
313
|
+
getBusinessContextFilePath,
|
|
224
314
|
loadTokenSession,
|
|
225
315
|
saveTokenSession,
|
|
316
|
+
loadBusinessContext,
|
|
317
|
+
saveBusinessContext,
|
|
226
318
|
clearTokenSession,
|
|
227
319
|
normalizeTokenSession,
|
|
228
320
|
loadEnvTokenSession,
|
package/lib/core/utils.js
CHANGED
|
@@ -612,8 +612,8 @@ function isHttpAuthStatus(statusCode) {
|
|
|
612
612
|
* 从 cookieData 中解析 base_url,支持多环境配置优先级。
|
|
613
613
|
*
|
|
614
614
|
* 优先级(高 → 低):
|
|
615
|
-
* 1.
|
|
616
|
-
* 2.
|
|
615
|
+
* 1. cookieData.base_url(鉴权服务返回的企业业务域名)
|
|
616
|
+
* 2. OPENYIDA_ENDPOINT 环境变量(鉴权端点兼业务兜底)
|
|
617
617
|
* 3. 当前激活的私有化环境配置
|
|
618
618
|
* 4. 当前激活的环境配置(公有云默认)
|
|
619
619
|
* 5. defaultBaseUrl 参数 / 公有云兜底
|
|
@@ -623,8 +623,15 @@ function isHttpAuthStatus(statusCode) {
|
|
|
623
623
|
* @returns {string}
|
|
624
624
|
*/
|
|
625
625
|
function resolveBaseUrl(cookieData, defaultBaseUrl) {
|
|
626
|
-
const { resolveEndpoint } = require('./env-manager');
|
|
627
|
-
const
|
|
626
|
+
const { normalizeBaseUrl, resolveEndpoint } = require('./env-manager');
|
|
627
|
+
const businessBaseUrl = normalizeBaseUrl(
|
|
628
|
+
cookieData && cookieData.base_url,
|
|
629
|
+
null
|
|
630
|
+
);
|
|
631
|
+
if (businessBaseUrl) {
|
|
632
|
+
return businessBaseUrl;
|
|
633
|
+
}
|
|
634
|
+
const resolved = resolveEndpoint(null, undefined);
|
|
628
635
|
if (defaultBaseUrl && resolved === 'https://www.aliwork.com' && (!cookieData || !cookieData.base_url)) {
|
|
629
636
|
return defaultBaseUrl.replace(/\/+$/, '');
|
|
630
637
|
}
|
package/package.json
CHANGED
|
@@ -24,6 +24,7 @@ Code Canvas 是宜搭的代码画布自定义页面链路:以 `YidaCodeCanvas`
|
|
|
24
24
|
- Canvas 源码写成 `.canvas.jsx` / `.canvas.tsx`,`openyida publish` 会自动走 Canvas 链路。
|
|
25
25
|
- 页面源码路径按 Bash cwd 选择:从仓库根执行命令时用 `project/pages/src/...`;如果 cwd 已是 `<workspace>/project`,用 `pages/src/...`,不要写成 `project/pages/src/...`。
|
|
26
26
|
- `runtimeCode` 在宿主页真实 `window` 中执行,入口必须返回 `YidaComp` / `YidaComp.default` / 组件函数。
|
|
27
|
+
- 推荐入口写法是 `function YidaComp(props) { ... }`,或 `const App = ...; export default App;`。CLI 已兼容 `const/let/class YidaComp; export default YidaComp`,但生成新代码时优先避开同名默认导出,减少不同 Canvas 运行态装配器下的重复声明风险。
|
|
27
28
|
- Canvas 组件没有普通页面实例上下文;数据读写通过 fetch、开放 API、连接器代理或显式 props 数据桥完成。
|
|
28
29
|
- 第三方依赖走白名单;React、antd、ahooks、d3、recharts、Radix、framer-motion 等可按规则 import。
|
|
29
30
|
- 宜搭运行态组件通过“原生组件桥 + fallback + 值归一化”接入;不改 `vc-deep-yida` 时,以宿主已存在的 `window.Deep` / `window.DeepYida` 探测为主,`window.YidaNativeComponents` 仅作为兼容入口。需要嵌入门户数据管理视图时探测 `DataManageViews`,并显式传入目标表单 `form.value/formUuid`。
|
|
@@ -724,6 +724,11 @@
|
|
|
724
724
|
],
|
|
725
725
|
"aliases": [
|
|
726
726
|
"登录态"
|
|
727
|
+
],
|
|
728
|
+
"command_ids": [
|
|
729
|
+
"agent-capabilities",
|
|
730
|
+
"login",
|
|
731
|
+
"auth"
|
|
727
732
|
]
|
|
728
733
|
},
|
|
729
734
|
{
|
|
@@ -739,6 +744,10 @@
|
|
|
739
744
|
],
|
|
740
745
|
"aliases": [
|
|
741
746
|
"登出"
|
|
747
|
+
],
|
|
748
|
+
"command_ids": [
|
|
749
|
+
"logout",
|
|
750
|
+
"auth"
|
|
742
751
|
]
|
|
743
752
|
},
|
|
744
753
|
{
|