openyida 2026.5.12 → 2026.5.13-beta.0
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 +1 -0
- package/bin/yida.js +19 -1
- package/lib/auth/cdp-browser-login.js +14 -15
- package/lib/auth/login.js +8 -14
- package/lib/auth/org.js +8 -8
- package/lib/auth/qr-login.js +5 -12
- package/lib/core/command-manifest.js +4 -0
- package/lib/core/doctor.js +11 -4
- package/lib/core/env-manager.js +154 -18
- package/lib/core/locales/ar.js +1 -0
- package/lib/core/locales/de.js +1 -0
- package/lib/core/locales/en.js +1 -0
- package/lib/core/locales/es.js +1 -0
- package/lib/core/locales/fr.js +1 -0
- package/lib/core/locales/hi.js +1 -0
- package/lib/core/locales/ja.js +1 -0
- package/lib/core/locales/ko.js +1 -0
- package/lib/core/locales/pt.js +1 -0
- package/lib/core/locales/vi.js +1 -0
- package/lib/core/locales/zh-HK.js +1 -0
- package/lib/core/locales/zh.js +1 -0
- package/lib/dingtalk/dingtalk-link.js +141 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -316,6 +316,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
316
316
|
| `openyida integration enable <appType> <formUuid> <processCode>` | Enable an automation flow |
|
|
317
317
|
| `openyida integration disable <appType> <formUuid> <processCode>` | Disable an automation flow |
|
|
318
318
|
| `openyida dws <command> [args]` | Access DingTalk CLI capabilities such as contacts, calendar, todo, and approval |
|
|
319
|
+
| `openyida dingtalk-link <url> [--target fullScreen] [--legacy-scheme] [--json]` | Generate DingTalk AppLink URLs for opening pages in DingTalk; use `--legacy-scheme` only when old `dingtalk://` links are required |
|
|
319
320
|
|
|
320
321
|
### Utilities
|
|
321
322
|
|
package/bin/yida.js
CHANGED
|
@@ -262,6 +262,15 @@ function shouldUseAgentLogin(cliArgs) {
|
|
|
262
262
|
return isAgentConversationEnvironment();
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
function shouldUsePlaywrightFallbackInAgentLogin() {
|
|
266
|
+
const { detectActiveTool } = require('../lib/core/utils');
|
|
267
|
+
const activeTool = detectActiveTool();
|
|
268
|
+
if (activeTool && activeTool.tool === 'wukong') {
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
return process.env.OPENYIDA_AGENT_PLAYWRIGHT_FALLBACK === '1';
|
|
272
|
+
}
|
|
273
|
+
|
|
265
274
|
function shouldUseCodexQrLogin(cliArgs) {
|
|
266
275
|
if (cliArgs.includes('--codex-qr') || cliArgs.includes('--agent-qr')) {return true;}
|
|
267
276
|
return false;
|
|
@@ -392,7 +401,9 @@ async function main() {
|
|
|
392
401
|
printLoginResult(cachedResult);
|
|
393
402
|
} else {
|
|
394
403
|
const { interactiveLogin } = require('../lib/auth/login');
|
|
395
|
-
const browserResult = interactiveLogin({
|
|
404
|
+
const browserResult = interactiveLogin({
|
|
405
|
+
playwrightFallback: shouldUsePlaywrightFallbackInAgentLogin(),
|
|
406
|
+
});
|
|
396
407
|
if (browserResult) {
|
|
397
408
|
printLoginResult(browserResult);
|
|
398
409
|
} else {
|
|
@@ -879,6 +890,13 @@ async function main() {
|
|
|
879
890
|
await runDws(args);
|
|
880
891
|
break;
|
|
881
892
|
}
|
|
893
|
+
|
|
894
|
+
case 'dingtalk-link': {
|
|
895
|
+
const { run: runDingTalkLink } = require('../lib/dingtalk/dingtalk-link');
|
|
896
|
+
await runDingTalkLink(args);
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
|
|
882
900
|
case 'export-conversation': {
|
|
883
901
|
const { exportConversation } = require('../lib/conversation/export-conversation');
|
|
884
902
|
// 解析选项
|
|
@@ -13,6 +13,7 @@ const path = require('path');
|
|
|
13
13
|
const net = require('net');
|
|
14
14
|
const crypto = require('crypto');
|
|
15
15
|
const { execFileSync, spawn } = require('child_process');
|
|
16
|
+
const { deriveBaseUrlFromCookies, deriveBaseUrlFromUrl } = require('../core/env-manager');
|
|
16
17
|
|
|
17
18
|
function findBrowserExecutable() {
|
|
18
19
|
if (process.env.OPENYIDA_CHROME_PATH && fs.existsSync(process.env.OPENYIDA_CHROME_PATH)) {
|
|
@@ -267,23 +268,19 @@ function createCdpClient(wsUrl) {
|
|
|
267
268
|
}
|
|
268
269
|
|
|
269
270
|
function deriveBaseUrl(cookies, fallbackUrl) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
cookie.domain &&
|
|
273
|
-
cookie.domain.includes('aliwork.com'));
|
|
274
|
-
if (yidaCookie) {
|
|
275
|
-
return `https://${String(yidaCookie.domain).replace(/^\./, '')}`;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const csrfCookie = cookies.find((cookie) => cookie.name === 'tianshu_csrf_token' && cookie.domain);
|
|
279
|
-
if (csrfCookie && csrfCookie.domain !== '.aliwork.com') {
|
|
280
|
-
return `https://${String(csrfCookie.domain).replace(/^\./, '')}`;
|
|
281
|
-
}
|
|
271
|
+
return deriveBaseUrlFromCookies(cookies, fallbackUrl);
|
|
272
|
+
}
|
|
282
273
|
|
|
274
|
+
async function getCurrentPageUrl(client, fallbackUrl) {
|
|
283
275
|
try {
|
|
284
|
-
|
|
276
|
+
const result = await client.send('Runtime.evaluate', {
|
|
277
|
+
expression: 'window.location.href',
|
|
278
|
+
returnByValue: true,
|
|
279
|
+
});
|
|
280
|
+
const value = result && result.result && result.result.value;
|
|
281
|
+
return typeof value === 'string' && value ? value : fallbackUrl;
|
|
285
282
|
} catch {
|
|
286
|
-
return
|
|
283
|
+
return fallbackUrl;
|
|
287
284
|
}
|
|
288
285
|
}
|
|
289
286
|
|
|
@@ -334,9 +331,11 @@ async function runCdpBrowserLogin(options = {}) {
|
|
|
334
331
|
.catch(() => client.send('Storage.getCookies'));
|
|
335
332
|
const cookies = Array.isArray(result.cookies) ? result.cookies : [];
|
|
336
333
|
if (cookies.some((cookie) => cookie.name === 'tianshu_csrf_token' && cookie.value)) {
|
|
334
|
+
const currentPageUrl = await getCurrentPageUrl(client, target.url || loginUrl);
|
|
335
|
+
const fallbackBaseUrl = deriveBaseUrlFromUrl(loginUrl, currentPageUrl);
|
|
337
336
|
return {
|
|
338
337
|
cookies,
|
|
339
|
-
base_url: deriveBaseUrl(cookies,
|
|
338
|
+
base_url: deriveBaseUrl(cookies, fallbackBaseUrl),
|
|
340
339
|
};
|
|
341
340
|
}
|
|
342
341
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
package/lib/auth/login.js
CHANGED
|
@@ -281,6 +281,10 @@ function interactiveLogin(options = {}) {
|
|
|
281
281
|
return null;
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
if (process.env.OPENYIDA_DISABLE_PLAYWRIGHT_LOGIN === '1') {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
|
|
284
288
|
const playwrightPath = getPlaywrightPath();
|
|
285
289
|
if (!playwrightPath) {
|
|
286
290
|
const { error: chalkError3 } = require('../core/chalk');
|
|
@@ -295,13 +299,14 @@ function playwrightInteractiveLogin(loginUrl, playwrightPath) {
|
|
|
295
299
|
const { info: chalkInfo, label: chalkLabel2 } = require('../core/chalk');
|
|
296
300
|
chalkInfo(t('login.browser_opening'));
|
|
297
301
|
chalkLabel2('URL', loginUrl);
|
|
302
|
+
const envManagerPath = path.join(__dirname, '..', 'core', 'env-manager.js');
|
|
298
303
|
|
|
299
304
|
// 通过子进程运行异步 playwright 逻辑,避免顶层 await 兼容性问题
|
|
300
305
|
// 使用 require.resolve 获取的绝对路径,确保临时脚本能找到 playwright
|
|
301
306
|
const scriptContent = `
|
|
302
307
|
const playwright = require(${JSON.stringify(playwrightPath)});
|
|
303
308
|
const { chromium } = playwright;
|
|
304
|
-
const {
|
|
309
|
+
const { deriveBaseUrlFromCookies, deriveBaseUrlFromUrl } = require(${JSON.stringify(envManagerPath)});
|
|
305
310
|
|
|
306
311
|
(async () => {
|
|
307
312
|
// 优先使用本地已安装的 Chrome,避免下载 Playwright 内置 Chromium
|
|
@@ -339,19 +344,8 @@ const { URL } = require('url');
|
|
|
339
344
|
|
|
340
345
|
const currentUrl = page.url();
|
|
341
346
|
const cookies = await context.cookies();
|
|
342
|
-
|
|
343
|
-
const
|
|
344
|
-
let baseUrl;
|
|
345
|
-
if (csrfCookie && csrfCookie.domain && csrfCookie.domain !== '.aliwork.com') {
|
|
346
|
-
baseUrl = 'https://' + csrfCookie.domain.replace(/^\\./, '');
|
|
347
|
-
} else {
|
|
348
|
-
try { baseUrl = new URL(currentUrl).origin; } catch { baseUrl = 'https://www.aliwork.com'; }
|
|
349
|
-
}
|
|
350
|
-
// 再用 yida_user_cookie 的 domain 覆盖,它是最可靠的来源
|
|
351
|
-
const yidaCookie = cookies.find(c => c.name === 'yida_user_cookie');
|
|
352
|
-
if (yidaCookie && yidaCookie.domain && yidaCookie.domain.includes('aliwork.com')) {
|
|
353
|
-
baseUrl = 'https://' + yidaCookie.domain.replace(/^\\./, '');
|
|
354
|
-
}
|
|
347
|
+
const fallbackBaseUrl = deriveBaseUrlFromUrl(${JSON.stringify(loginUrl)}, currentUrl);
|
|
348
|
+
const baseUrl = deriveBaseUrlFromCookies(cookies, fallbackBaseUrl);
|
|
355
349
|
await browser.close();
|
|
356
350
|
|
|
357
351
|
console.log(JSON.stringify({ cookies, base_url: baseUrl }));
|
package/lib/auth/org.js
CHANGED
|
@@ -23,13 +23,11 @@
|
|
|
23
23
|
|
|
24
24
|
const https = require('https');
|
|
25
25
|
const http = require('http');
|
|
26
|
-
const { extractInfoFromCookies } = require('../core/utils');
|
|
26
|
+
const { extractInfoFromCookies, resolveBaseUrl } = require('../core/utils');
|
|
27
27
|
const { saveCookieCache } = require('./login');
|
|
28
28
|
const { saveAuthConfig, loadAuthConfig } = require('./auth');
|
|
29
29
|
const { t } = require('../core/i18n');
|
|
30
30
|
|
|
31
|
-
const DEFAULT_BASE_URL = 'https://www.aliwork.com';
|
|
32
|
-
|
|
33
31
|
// ── HTTP 请求工具 ─────────────────────────────────────
|
|
34
32
|
|
|
35
33
|
/**
|
|
@@ -208,19 +206,21 @@ async function switchOrganization(targetCorpId, cookieData) {
|
|
|
208
206
|
}
|
|
209
207
|
|
|
210
208
|
try {
|
|
209
|
+
const baseUrl = resolveBaseUrl(cookieData);
|
|
210
|
+
|
|
211
211
|
// Step 1: 发起切换请求
|
|
212
212
|
step(1, t('org.step1'), false);
|
|
213
|
-
const step1Url = `${
|
|
213
|
+
const step1Url = `${baseUrl}/start.html?corpid=${targetCorpId}&switchCorp=true`;
|
|
214
214
|
const step1Result = await httpGetWithCookies(step1Url, cookies, false);
|
|
215
215
|
|
|
216
216
|
// Step 2: 确认切换
|
|
217
217
|
step(2, t('org.step2'), false);
|
|
218
|
-
const step2Url = `${
|
|
218
|
+
const step2Url = `${baseUrl}/start.html?corpid=${targetCorpId}&`;
|
|
219
219
|
const step2Result = await httpGetWithCookies(step2Url, step1Result.cookies, false);
|
|
220
220
|
|
|
221
221
|
// Step 3-4: 获取新 Cookie(跟随重定向)
|
|
222
222
|
step(3, t('org.step3'), false);
|
|
223
|
-
const step3Url =
|
|
223
|
+
const step3Url = `${baseUrl}/start.html?corpid=${targetCorpId}&`;
|
|
224
224
|
const step3Result = await httpGetWithCookies(step3Url, step2Result.cookies, false);
|
|
225
225
|
|
|
226
226
|
// 处理重定向
|
|
@@ -235,7 +235,7 @@ async function switchOrganization(targetCorpId, cookieData) {
|
|
|
235
235
|
|
|
236
236
|
// 构建完整 URL
|
|
237
237
|
if (!currentUrl.startsWith('http')) {
|
|
238
|
-
currentUrl =
|
|
238
|
+
currentUrl = new URL(currentUrl, baseUrl).toString();
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
const redirectResult = await httpGetWithCookies(currentUrl, finalCookies, false);
|
|
@@ -261,7 +261,7 @@ async function switchOrganization(targetCorpId, cookieData) {
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
// 保存新的 Cookie
|
|
264
|
-
const newBaseUrl =
|
|
264
|
+
const newBaseUrl = baseUrl;
|
|
265
265
|
saveCookieCache(finalCookies, newBaseUrl);
|
|
266
266
|
|
|
267
267
|
// 更新 auth 配置
|
package/lib/auth/qr-login.js
CHANGED
|
@@ -24,7 +24,7 @@ const { saveCookieCache } = require('./login');
|
|
|
24
24
|
const { t } = require('../core/i18n');
|
|
25
25
|
const { warn } = require('../core/chalk');
|
|
26
26
|
|
|
27
|
-
const { resolveLoginUrl, resolveEndpoint } = require('../core/env-manager');
|
|
27
|
+
const { resolveLoginUrl, resolveEndpoint, deriveBaseUrlFromUrl } = require('../core/env-manager');
|
|
28
28
|
|
|
29
29
|
function shellQuote(value) {
|
|
30
30
|
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
@@ -511,16 +511,7 @@ async function resolveCorpSelection(corpList, options = {}) {
|
|
|
511
511
|
}
|
|
512
512
|
|
|
513
513
|
function deriveAliworkBaseUrl(fallbackBaseUrl, finalUrl) {
|
|
514
|
-
|
|
515
|
-
const parsedUrl = new URL(finalUrl);
|
|
516
|
-
if (parsedUrl.hostname.endsWith('aliwork.com')) {
|
|
517
|
-
return parsedUrl.origin;
|
|
518
|
-
}
|
|
519
|
-
} catch {
|
|
520
|
-
// ignore
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
return fallbackBaseUrl;
|
|
514
|
+
return deriveBaseUrlFromUrl(fallbackBaseUrl, finalUrl);
|
|
524
515
|
}
|
|
525
516
|
|
|
526
517
|
/**
|
|
@@ -530,7 +521,8 @@ function deriveAliworkBaseUrl(fallbackBaseUrl, finalUrl) {
|
|
|
530
521
|
* @returns {Promise<{ cookieHeader: string, loginPageUrl: string }>}
|
|
531
522
|
*/
|
|
532
523
|
async function fetchInitialSession(baseUrl, options = {}) {
|
|
533
|
-
const loginPageUrl = options.loginUrl ||
|
|
524
|
+
const loginPageUrl = options.loginUrl ||
|
|
525
|
+
(options.baseUrl ? `${baseUrl}/workPlatform` : (resolveLoginUrl() || `${baseUrl}/workPlatform`));
|
|
534
526
|
const response = await fetchGetFollowRedirects(loginPageUrl, {
|
|
535
527
|
cookieHeader: options.cookieHeader || '',
|
|
536
528
|
});
|
|
@@ -1443,5 +1435,6 @@ module.exports = {
|
|
|
1443
1435
|
buildAgentQrResponseMarkdown,
|
|
1444
1436
|
buildNeedQrScanResult,
|
|
1445
1437
|
getTargetCorpId,
|
|
1438
|
+
deriveAliworkBaseUrl,
|
|
1446
1439
|
},
|
|
1447
1440
|
};
|
|
@@ -126,6 +126,10 @@ const COMMAND_GROUPS = [
|
|
|
126
126
|
commands: [
|
|
127
127
|
command('integration.create', ['integration', 'create'], 'integration create <appType> ...', 'help.cmd_integration'),
|
|
128
128
|
command('dws', ['dws'], 'dws <command> [args]', 'help.cmd_dws'),
|
|
129
|
+
command('dingtalk-link', ['dingtalk-link'], 'dingtalk-link <url> [--target fullScreen] [--legacy-scheme] [--json]', 'help.cmd_dingtalk_link', {
|
|
130
|
+
requiresLogin: false,
|
|
131
|
+
output: 'text|json',
|
|
132
|
+
}),
|
|
129
133
|
],
|
|
130
134
|
},
|
|
131
135
|
{
|
package/lib/core/doctor.js
CHANGED
|
@@ -286,8 +286,15 @@ class EnvironmentChecker {
|
|
|
286
286
|
async checkNetwork() {
|
|
287
287
|
try {
|
|
288
288
|
const https = require('https');
|
|
289
|
+
const http = require('http');
|
|
290
|
+
const { loadCookieData } = require('./utils');
|
|
291
|
+
const { resolveEndpoint } = require('./env-manager');
|
|
292
|
+
const cookieData = loadCookieData(this.projectRoot);
|
|
293
|
+
const baseUrl = resolveEndpoint(cookieData, this.projectRoot);
|
|
294
|
+
const parsedUrl = new URL(baseUrl);
|
|
295
|
+
const requestModule = parsedUrl.protocol === 'https:' ? https : http;
|
|
289
296
|
await new Promise((resolve, reject) => {
|
|
290
|
-
const request =
|
|
297
|
+
const request = requestModule.get(baseUrl, { timeout: 5000 }, (response) => {
|
|
291
298
|
resolve(response.statusCode);
|
|
292
299
|
});
|
|
293
300
|
request.on('error', reject);
|
|
@@ -298,18 +305,18 @@ class EnvironmentChecker {
|
|
|
298
305
|
});
|
|
299
306
|
return {
|
|
300
307
|
id: 'env-network',
|
|
301
|
-
label:
|
|
308
|
+
label: `网络连通性(${parsedUrl.hostname})`,
|
|
302
309
|
passed: true,
|
|
303
310
|
severity: Severity.INFO,
|
|
304
311
|
fixType: null,
|
|
305
312
|
};
|
|
306
|
-
} catch {
|
|
313
|
+
} catch (err) {
|
|
307
314
|
return {
|
|
308
315
|
id: 'env-network',
|
|
309
316
|
label: '网络连通性检测',
|
|
310
317
|
passed: false,
|
|
311
318
|
severity: Severity.WARNING,
|
|
312
|
-
message:
|
|
319
|
+
message: `无法连接当前宜搭地址,请检查网络或环境配置:${err.message}`,
|
|
313
320
|
fixType: null,
|
|
314
321
|
};
|
|
315
322
|
}
|
package/lib/core/env-manager.js
CHANGED
|
@@ -31,6 +31,8 @@ const { findProjectRoot } = require('./utils');
|
|
|
31
31
|
|
|
32
32
|
const DEFAULT_BASE_URL = 'https://www.aliwork.com';
|
|
33
33
|
const DEFAULT_LOGIN_URL = 'https://www.aliwork.com/workPlatform';
|
|
34
|
+
const ALIBABA_INTERNAL_BASE_URL = 'https://yida-group.alibaba-inc.com';
|
|
35
|
+
const ALIBABA_INTERNAL_LOGIN_URL = `${ALIBABA_INTERNAL_BASE_URL}/workPlatform`;
|
|
34
36
|
const ENVS_CONFIG_FILE = 'openyida-envs.json';
|
|
35
37
|
|
|
36
38
|
/** 默认公有云环境配置 */
|
|
@@ -41,6 +43,142 @@ const DEFAULT_PUBLIC_ENV = {
|
|
|
41
43
|
cookieFile: 'cookies-public.json',
|
|
42
44
|
};
|
|
43
45
|
|
|
46
|
+
/** 阿里内网宜搭环境配置 */
|
|
47
|
+
const DEFAULT_ALIBABA_INTERNAL_ENV = {
|
|
48
|
+
baseUrl: ALIBABA_INTERNAL_BASE_URL,
|
|
49
|
+
loginUrl: ALIBABA_INTERNAL_LOGIN_URL,
|
|
50
|
+
description: '阿里内网宜搭',
|
|
51
|
+
cookieFile: 'cookies-alibaba.json',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const BUILTIN_ENVIRONMENTS = {
|
|
55
|
+
public: DEFAULT_PUBLIC_ENV,
|
|
56
|
+
alibaba: DEFAULT_ALIBABA_INTERNAL_ENV,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const SHARED_COOKIE_DOMAINS = new Set([
|
|
60
|
+
'aliwork.com',
|
|
61
|
+
'alibaba-inc.com',
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
const KNOWN_YIDA_HOSTS = new Set([
|
|
65
|
+
'www.aliwork.com',
|
|
66
|
+
'yida-group.alibaba-inc.com',
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
function cloneBuiltinEnvironments() {
|
|
70
|
+
return Object.fromEntries(
|
|
71
|
+
Object.entries(BUILTIN_ENVIRONMENTS).map(([name, envConfig]) => [name, { ...envConfig }])
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function buildDefaultEnvsConfig() {
|
|
76
|
+
return {
|
|
77
|
+
current: 'public',
|
|
78
|
+
environments: cloneBuiltinEnvironments(),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function ensureBuiltinEnvironments(config) {
|
|
83
|
+
if (!config.environments) { config.environments = {}; }
|
|
84
|
+
for (const [envName, envConfig] of Object.entries(BUILTIN_ENVIRONMENTS)) {
|
|
85
|
+
if (!config.environments[envName]) {
|
|
86
|
+
config.environments[envName] = { ...envConfig };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return config;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeBaseUrl(value, fallback = null) {
|
|
93
|
+
if (!value) { return fallback; }
|
|
94
|
+
const trimmed = String(value).trim();
|
|
95
|
+
if (!trimmed) { return fallback; }
|
|
96
|
+
const withProtocol = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)
|
|
97
|
+
? trimmed
|
|
98
|
+
: `https://${trimmed}`;
|
|
99
|
+
try {
|
|
100
|
+
return new URL(withProtocol).origin.replace(/\/+$/, '');
|
|
101
|
+
} catch {
|
|
102
|
+
return fallback;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalizeHostname(value) {
|
|
107
|
+
if (!value) { return ''; }
|
|
108
|
+
const trimmed = String(value).trim().replace(/^\./, '').toLowerCase();
|
|
109
|
+
if (!trimmed) { return ''; }
|
|
110
|
+
try {
|
|
111
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
|
|
112
|
+
return new URL(trimmed).hostname.toLowerCase();
|
|
113
|
+
}
|
|
114
|
+
if (trimmed.includes('/')) {
|
|
115
|
+
return new URL(`https://${trimmed}`).hostname.toLowerCase();
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
return '';
|
|
119
|
+
}
|
|
120
|
+
return trimmed;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function isSharedCookieDomain(hostname) {
|
|
124
|
+
return SHARED_COOKIE_DOMAINS.has(normalizeHostname(hostname));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isYidaServiceHost(hostname) {
|
|
128
|
+
const host = normalizeHostname(hostname);
|
|
129
|
+
if (!host) { return false; }
|
|
130
|
+
if (KNOWN_YIDA_HOSTS.has(host)) { return true; }
|
|
131
|
+
if (host.endsWith('.aliwork.com') && host !== 'aliwork.com') { return true; }
|
|
132
|
+
if (host.endsWith('.alibaba-inc.com') && host !== 'alibaba-inc.com') {
|
|
133
|
+
return host.startsWith('yida-') || host.includes('.yida-') || host.includes('.yida.');
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function cookieDomainToBaseUrl(domain, fallbackUrl) {
|
|
139
|
+
const host = normalizeHostname(domain);
|
|
140
|
+
if (!host || isSharedCookieDomain(host)) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const fallbackOrigin = normalizeBaseUrl(fallbackUrl, null);
|
|
145
|
+
if (fallbackOrigin) {
|
|
146
|
+
const fallbackHost = normalizeHostname(fallbackOrigin);
|
|
147
|
+
if (host === fallbackHost) {
|
|
148
|
+
return fallbackOrigin;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (isYidaServiceHost(host)) {
|
|
153
|
+
return `https://${host}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function deriveBaseUrlFromCookies(cookies = [], fallbackUrl = DEFAULT_BASE_URL) {
|
|
160
|
+
const fallbackOrigin = normalizeBaseUrl(fallbackUrl, DEFAULT_BASE_URL);
|
|
161
|
+
const cookieList = Array.isArray(cookies) ? cookies : [];
|
|
162
|
+
const preferredCookieNames = ['yida_user_cookie', 'tianshu_csrf_token'];
|
|
163
|
+
|
|
164
|
+
for (const cookieName of preferredCookieNames) {
|
|
165
|
+
const cookie = cookieList.find((item) => item && item.name === cookieName && item.domain);
|
|
166
|
+
const baseUrl = cookie ? cookieDomainToBaseUrl(cookie.domain, fallbackOrigin) : null;
|
|
167
|
+
if (baseUrl) { return baseUrl; }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return fallbackOrigin;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function deriveBaseUrlFromUrl(fallbackBaseUrl, candidateUrl) {
|
|
174
|
+
const fallbackOrigin = normalizeBaseUrl(fallbackBaseUrl, DEFAULT_BASE_URL);
|
|
175
|
+
const candidateOrigin = normalizeBaseUrl(candidateUrl, null);
|
|
176
|
+
if (!candidateOrigin) { return fallbackOrigin; }
|
|
177
|
+
|
|
178
|
+
const candidateHost = normalizeHostname(candidateOrigin);
|
|
179
|
+
return isYidaServiceHost(candidateHost) ? candidateOrigin : fallbackOrigin;
|
|
180
|
+
}
|
|
181
|
+
|
|
44
182
|
// ── 配置文件读写 ──────────────────────────────────────
|
|
45
183
|
|
|
46
184
|
/**
|
|
@@ -54,26 +192,16 @@ function loadEnvsConfig(projectRoot) {
|
|
|
54
192
|
const configPath = path.join(root, '.cache', ENVS_CONFIG_FILE);
|
|
55
193
|
|
|
56
194
|
if (!fs.existsSync(configPath)) {
|
|
57
|
-
return
|
|
58
|
-
current: 'public',
|
|
59
|
-
environments: { public: { ...DEFAULT_PUBLIC_ENV } },
|
|
60
|
-
};
|
|
195
|
+
return buildDefaultEnvsConfig();
|
|
61
196
|
}
|
|
62
197
|
|
|
63
198
|
try {
|
|
64
199
|
const raw = fs.readFileSync(configPath, 'utf-8').trim();
|
|
65
200
|
const parsed = JSON.parse(raw);
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
if (!parsed.environments.public) {
|
|
69
|
-
parsed.environments.public = { ...DEFAULT_PUBLIC_ENV };
|
|
70
|
-
}
|
|
71
|
-
return parsed;
|
|
201
|
+
// 确保内置环境始终存在,已有同名环境不覆盖,保证用户配置优先
|
|
202
|
+
return ensureBuiltinEnvironments(parsed);
|
|
72
203
|
} catch {
|
|
73
|
-
return
|
|
74
|
-
current: 'public',
|
|
75
|
-
environments: { public: { ...DEFAULT_PUBLIC_ENV } },
|
|
76
|
-
};
|
|
204
|
+
return buildDefaultEnvsConfig();
|
|
77
205
|
}
|
|
78
206
|
}
|
|
79
207
|
|
|
@@ -162,7 +290,7 @@ function migrateOldCookieFile(projectRoot) {
|
|
|
162
290
|
function resolveEndpoint(cookieData, projectRoot) {
|
|
163
291
|
// 优先级 1:环境变量强制指定
|
|
164
292
|
if (process.env.OPENYIDA_ENDPOINT) {
|
|
165
|
-
return process.env.OPENYIDA_ENDPOINT
|
|
293
|
+
return normalizeBaseUrl(process.env.OPENYIDA_ENDPOINT, DEFAULT_BASE_URL);
|
|
166
294
|
}
|
|
167
295
|
|
|
168
296
|
// 优先级 2:当前激活环境配置
|
|
@@ -171,17 +299,17 @@ function resolveEndpoint(cookieData, projectRoot) {
|
|
|
171
299
|
// 这样可以兼容:用户没有配置多环境时,仍从 Cookie 中提取专属域名
|
|
172
300
|
const isDefaultPublic = envConfig.baseUrl === DEFAULT_BASE_URL;
|
|
173
301
|
if (!isDefaultPublic && envConfig.baseUrl) {
|
|
174
|
-
return envConfig.baseUrl
|
|
302
|
+
return normalizeBaseUrl(envConfig.baseUrl, DEFAULT_BASE_URL);
|
|
175
303
|
}
|
|
176
304
|
|
|
177
305
|
// 优先级 3:从 Cookie 历史提取(兼容专属域名)
|
|
178
306
|
if (cookieData && cookieData.base_url) {
|
|
179
|
-
return cookieData.base_url
|
|
307
|
+
return normalizeBaseUrl(cookieData.base_url, DEFAULT_BASE_URL);
|
|
180
308
|
}
|
|
181
309
|
|
|
182
310
|
// 优先级 4:环境配置(公有云默认)
|
|
183
311
|
if (envConfig.baseUrl) {
|
|
184
|
-
return envConfig.baseUrl
|
|
312
|
+
return normalizeBaseUrl(envConfig.baseUrl, DEFAULT_BASE_URL);
|
|
185
313
|
}
|
|
186
314
|
|
|
187
315
|
return DEFAULT_BASE_URL;
|
|
@@ -207,7 +335,10 @@ function resolveLoginUrl(projectRoot) {
|
|
|
207
335
|
module.exports = {
|
|
208
336
|
DEFAULT_BASE_URL,
|
|
209
337
|
DEFAULT_LOGIN_URL,
|
|
338
|
+
ALIBABA_INTERNAL_BASE_URL,
|
|
339
|
+
ALIBABA_INTERNAL_LOGIN_URL,
|
|
210
340
|
DEFAULT_PUBLIC_ENV,
|
|
341
|
+
DEFAULT_ALIBABA_INTERNAL_ENV,
|
|
211
342
|
loadEnvsConfig,
|
|
212
343
|
saveEnvsConfig,
|
|
213
344
|
getCurrentEnvConfig,
|
|
@@ -215,4 +346,9 @@ module.exports = {
|
|
|
215
346
|
migrateOldCookieFile,
|
|
216
347
|
resolveEndpoint,
|
|
217
348
|
resolveLoginUrl,
|
|
349
|
+
normalizeBaseUrl,
|
|
350
|
+
normalizeHostname,
|
|
351
|
+
isYidaServiceHost,
|
|
352
|
+
deriveBaseUrlFromCookies,
|
|
353
|
+
deriveBaseUrlFromUrl,
|
|
218
354
|
};
|
package/lib/core/locales/ar.js
CHANGED
|
@@ -68,6 +68,7 @@ module.exports = {
|
|
|
68
68
|
group_integration: 'التكامل & DingTalk',
|
|
69
69
|
cmd_integration: 'إنشاء تدفق أتمتة التكامل',
|
|
70
70
|
cmd_dws: 'DingTalk CLI (جهات الاتصال/التقويم/المهام/الموافقة إلخ)',
|
|
71
|
+
cmd_dingtalk_link: 'إنشاء روابط DingTalk AppLink / dingtalk:// القديمة',
|
|
71
72
|
group_utility: 'الأدوات',
|
|
72
73
|
cmd_commands: 'Output machine-readable command manifest',
|
|
73
74
|
cmd_a2a: 'Start local read-only A2A adapter or print Agent Card',
|
package/lib/core/locales/de.js
CHANGED
|
@@ -68,6 +68,7 @@ module.exports = {
|
|
|
68
68
|
group_integration: 'Integration & DingTalk',
|
|
69
69
|
cmd_integration: 'Integrations-Automatisierungsflow erstellen',
|
|
70
70
|
cmd_dws: 'DingTalk CLI (Kontakte/Kalender/Aufgaben/Genehmigung etc.)',
|
|
71
|
+
cmd_dingtalk_link: 'DingTalk AppLink / alte dingtalk:// Seitenlinks erzeugen',
|
|
71
72
|
group_utility: 'Werkzeuge',
|
|
72
73
|
cmd_commands: 'Maschinenlesbares Command Manifest ausgeben',
|
|
73
74
|
cmd_a2a: 'Lokalen schreibgeschützten A2A-Adapter starten oder Agent Card ausgeben',
|
package/lib/core/locales/en.js
CHANGED
|
@@ -71,6 +71,7 @@ module.exports = {
|
|
|
71
71
|
group_integration: 'Integration & DingTalk',
|
|
72
72
|
cmd_integration: 'Create integration automation flow',
|
|
73
73
|
cmd_dws: 'DingTalk CLI (contacts/calendar/todo/approval etc.)',
|
|
74
|
+
cmd_dingtalk_link: 'Generate DingTalk AppLink / legacy dingtalk:// page links',
|
|
74
75
|
group_utility: 'Utility',
|
|
75
76
|
cmd_commands: 'Output machine-readable command manifest',
|
|
76
77
|
cmd_a2a: 'Start local read-only A2A adapter or print Agent Card',
|
package/lib/core/locales/es.js
CHANGED
|
@@ -68,6 +68,7 @@ module.exports = {
|
|
|
68
68
|
group_integration: 'Integración & DingTalk',
|
|
69
69
|
cmd_integration: 'Crear flujo de automatización',
|
|
70
70
|
cmd_dws: 'DingTalk CLI (contactos/calendario/tareas/aprobación etc.)',
|
|
71
|
+
cmd_dingtalk_link: 'Generar enlaces DingTalk AppLink / dingtalk:// heredados',
|
|
71
72
|
group_utility: 'Utilidades',
|
|
72
73
|
cmd_commands: 'Emitir manifiesto de comandos legible por máquina',
|
|
73
74
|
cmd_a2a: 'Iniciar adaptador A2A local de solo lectura o imprimir Agent Card',
|
package/lib/core/locales/fr.js
CHANGED
|
@@ -68,6 +68,7 @@ module.exports = {
|
|
|
68
68
|
group_integration: 'Intégration & DingTalk',
|
|
69
69
|
cmd_integration: 'Créer un flux d\'automatisation',
|
|
70
70
|
cmd_dws: 'DingTalk CLI (contacts/calendrier/tâches/approbation etc.)',
|
|
71
|
+
cmd_dingtalk_link: 'Générer des liens DingTalk AppLink / dingtalk:// historiques',
|
|
71
72
|
group_utility: 'Utilitaires',
|
|
72
73
|
cmd_commands: 'Afficher le manifeste des commandes lisible par machine',
|
|
73
74
|
cmd_a2a: 'Démarrer l’adaptateur A2A local en lecture seule ou afficher l’Agent Card',
|
package/lib/core/locales/hi.js
CHANGED
|
@@ -68,6 +68,7 @@ module.exports = {
|
|
|
68
68
|
group_integration: 'एकीकरण & DingTalk',
|
|
69
69
|
cmd_integration: 'एकीकरण स्वचालन फ्लो बनाएं',
|
|
70
70
|
cmd_dws: 'DingTalk CLI (संपर्क/कैलेंडर/कार्य/अनुमोदन आदि)',
|
|
71
|
+
cmd_dingtalk_link: 'DingTalk AppLink / legacy dingtalk:// पेज लिंक बनाएं',
|
|
71
72
|
group_utility: 'उपकरण',
|
|
72
73
|
cmd_commands: 'Output machine-readable command manifest',
|
|
73
74
|
cmd_a2a: 'Start local read-only A2A adapter or print Agent Card',
|
package/lib/core/locales/ja.js
CHANGED
|
@@ -70,6 +70,7 @@ module.exports = {
|
|
|
70
70
|
group_integration: '統合 & DingTalk',
|
|
71
71
|
cmd_integration: '統合自動化フローを作成',
|
|
72
72
|
cmd_dws: 'DingTalk CLI(連絡先/カレンダー/ToDo/承認等)',
|
|
73
|
+
cmd_dingtalk_link: 'DingTalk AppLink / 旧 dingtalk:// ページリンクを生成',
|
|
73
74
|
group_utility: 'ユーティリティ',
|
|
74
75
|
cmd_commands: '機械可読コマンド manifest を出力',
|
|
75
76
|
cmd_a2a: 'ローカル読み取り専用 A2A Adapter を起動、または Agent Card を出力',
|
package/lib/core/locales/ko.js
CHANGED
|
@@ -68,6 +68,7 @@ module.exports = {
|
|
|
68
68
|
group_integration: '통합 & DingTalk',
|
|
69
69
|
cmd_integration: '통합 자동화 플로우 생성',
|
|
70
70
|
cmd_dws: 'DingTalk CLI (연락처/캘린더/할일/승인 등)',
|
|
71
|
+
cmd_dingtalk_link: 'DingTalk AppLink / 기존 dingtalk:// 페이지 링크 생성',
|
|
71
72
|
group_utility: '유틸리티',
|
|
72
73
|
cmd_commands: 'Output machine-readable command manifest',
|
|
73
74
|
cmd_a2a: 'Start local read-only A2A adapter or print Agent Card',
|
package/lib/core/locales/pt.js
CHANGED
|
@@ -68,6 +68,7 @@ module.exports = {
|
|
|
68
68
|
group_integration: 'Integração & DingTalk',
|
|
69
69
|
cmd_integration: 'Criar fluxo de automação',
|
|
70
70
|
cmd_dws: 'DingTalk CLI (contatos/calendário/tarefas/aprovação etc.)',
|
|
71
|
+
cmd_dingtalk_link: 'Gerar links DingTalk AppLink / dingtalk:// legados',
|
|
71
72
|
group_utility: 'Utilitários',
|
|
72
73
|
cmd_commands: 'Emitir manifesto de comandos legível por máquina',
|
|
73
74
|
cmd_a2a: 'Iniciar adaptador A2A local somente leitura ou imprimir Agent Card',
|
package/lib/core/locales/vi.js
CHANGED
|
@@ -68,6 +68,7 @@ module.exports = {
|
|
|
68
68
|
group_integration: 'Tích hợp & DingTalk',
|
|
69
69
|
cmd_integration: 'Tạo luồng tự động hóa tích hợp',
|
|
70
70
|
cmd_dws: 'DingTalk CLI (danh bạ/lịch/việc cần làm/phê duyệt v.v.)',
|
|
71
|
+
cmd_dingtalk_link: 'Tạo liên kết DingTalk AppLink / dingtalk:// cũ',
|
|
71
72
|
group_utility: 'Tiện ích',
|
|
72
73
|
cmd_commands: 'Output machine-readable command manifest',
|
|
73
74
|
cmd_a2a: 'Start local read-only A2A adapter or print Agent Card',
|
|
@@ -70,6 +70,7 @@ module.exports = {
|
|
|
70
70
|
group_integration: '整合 & 釘釘',
|
|
71
71
|
cmd_integration: '建立整合自動化邏輯流',
|
|
72
72
|
cmd_dws: '釘釘 CLI(通訊錄/日曆/待辦/審批等)',
|
|
73
|
+
cmd_dingtalk_link: '生成釘釘 AppLink / 相容 dingtalk:// 跳轉連結',
|
|
73
74
|
group_utility: '工具',
|
|
74
75
|
cmd_commands: '輸出機器可讀命令清單',
|
|
75
76
|
cmd_a2a: '啟動本機唯讀 A2A Adapter 或輸出 Agent Card',
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -71,6 +71,7 @@ module.exports = {
|
|
|
71
71
|
group_integration: '集成 & 钉钉',
|
|
72
72
|
cmd_integration: '创建集成自动化逻辑流',
|
|
73
73
|
cmd_dws: '钉钉 CLI(通讯录/日历/待办/审批等)',
|
|
74
|
+
cmd_dingtalk_link: '生成钉钉 AppLink / 兼容 dingtalk:// 跳转链接',
|
|
74
75
|
group_utility: '工具',
|
|
75
76
|
cmd_commands: '输出机器可读命令清单',
|
|
76
77
|
cmd_a2a: '启动本地只读 A2A Adapter 或输出 Agent Card',
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { warn } = require('../core/chalk');
|
|
4
|
+
|
|
5
|
+
const APPLINK_PAGE_LINK = 'https://applink.dingtalk.com/page/link';
|
|
6
|
+
const LEGACY_PAGE_LINK = 'dingtalk://dingtalkclient/page/link';
|
|
7
|
+
const DEFAULT_TARGET = 'fullScreen';
|
|
8
|
+
|
|
9
|
+
function parseArgs(args) {
|
|
10
|
+
const options = {
|
|
11
|
+
url: null,
|
|
12
|
+
target: DEFAULT_TARGET,
|
|
13
|
+
legacyScheme: false,
|
|
14
|
+
json: false,
|
|
15
|
+
help: false,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
19
|
+
const arg = args[i];
|
|
20
|
+
if (arg === '--help' || arg === '-h') {
|
|
21
|
+
options.help = true;
|
|
22
|
+
} else if (arg === '--url') {
|
|
23
|
+
options.url = args[++i];
|
|
24
|
+
} else if (arg === '--target') {
|
|
25
|
+
options.target = args[++i];
|
|
26
|
+
} else if (arg === '--legacy-scheme') {
|
|
27
|
+
options.legacyScheme = true;
|
|
28
|
+
} else if (arg === '--json') {
|
|
29
|
+
options.json = true;
|
|
30
|
+
} else if (!arg.startsWith('--') && !options.url) {
|
|
31
|
+
options.url = arg;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return options;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isDingTalkPageLink(value) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = new URL(value);
|
|
41
|
+
if (parsed.protocol === 'dingtalk:' && parsed.hostname === 'dingtalkclient') {
|
|
42
|
+
return parsed.pathname === '/page/link';
|
|
43
|
+
}
|
|
44
|
+
return parsed.protocol === 'https:' && parsed.hostname === 'applink.dingtalk.com' && parsed.pathname === '/page/link';
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function extractPageUrl(value) {
|
|
51
|
+
if (!isDingTalkPageLink(value)) {
|
|
52
|
+
return { pageUrl: value, target: null };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const parsed = new URL(value);
|
|
56
|
+
return {
|
|
57
|
+
pageUrl: parsed.searchParams.get('url') || '',
|
|
58
|
+
target: parsed.searchParams.get('target'),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function assertHttpUrl(value) {
|
|
63
|
+
let parsed;
|
|
64
|
+
try {
|
|
65
|
+
parsed = new URL(value);
|
|
66
|
+
} catch {
|
|
67
|
+
throw new Error('DingTalk page links require an absolute http(s) URL.');
|
|
68
|
+
}
|
|
69
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
|
70
|
+
throw new Error('DingTalk page links require an absolute http(s) URL.');
|
|
71
|
+
}
|
|
72
|
+
return parsed.toString();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function buildDingTalkPageLink(input) {
|
|
76
|
+
const options = {
|
|
77
|
+
url: input && input.url,
|
|
78
|
+
target: input && input.target,
|
|
79
|
+
legacyScheme: !!(input && input.legacyScheme),
|
|
80
|
+
};
|
|
81
|
+
const hasTargetOption = !!(input && Object.prototype.hasOwnProperty.call(input, 'target'));
|
|
82
|
+
|
|
83
|
+
if (!options.url) {
|
|
84
|
+
throw new Error('Missing URL. Usage: openyida dingtalk-link <url>');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const extracted = extractPageUrl(options.url);
|
|
88
|
+
const pageUrl = assertHttpUrl(extracted.pageUrl);
|
|
89
|
+
const target = hasTargetOption ? options.target : (extracted.target || DEFAULT_TARGET);
|
|
90
|
+
const base = options.legacyScheme ? LEGACY_PAGE_LINK : APPLINK_PAGE_LINK;
|
|
91
|
+
const link = new URL(base);
|
|
92
|
+
link.searchParams.set('url', pageUrl);
|
|
93
|
+
if (target) {
|
|
94
|
+
link.searchParams.set('target', target);
|
|
95
|
+
}
|
|
96
|
+
return link.toString();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function printHelp() {
|
|
100
|
+
console.log('Usage: openyida dingtalk-link <url> [--target fullScreen] [--legacy-scheme] [--json]');
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log('Generate DingTalk AppLink URLs for opening web pages in DingTalk.');
|
|
103
|
+
console.log('AppLink is the default because dingtalk:// may be claimed by DingTalk variants such as dedicated DingTalk clients.');
|
|
104
|
+
console.log('');
|
|
105
|
+
console.log('Examples:');
|
|
106
|
+
console.log(' openyida dingtalk-link https://attend.dingtalk.com/attend/index.html');
|
|
107
|
+
console.log(' openyida dingtalk-link "dingtalk://dingtalkclient/page/link?url=https%3A%2F%2Fexample.com"');
|
|
108
|
+
console.log(' openyida dingtalk-link https://example.com --legacy-scheme');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function run(args) {
|
|
112
|
+
const options = parseArgs(args);
|
|
113
|
+
if (options.help) {
|
|
114
|
+
printHelp();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const link = buildDingTalkPageLink(options);
|
|
120
|
+
if (options.json) {
|
|
121
|
+
console.log(JSON.stringify({
|
|
122
|
+
url: link,
|
|
123
|
+
kind: options.legacyScheme ? 'legacy-scheme' : 'applink',
|
|
124
|
+
target: options.target || null,
|
|
125
|
+
}, null, 2));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
console.log(link);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
warn(error.message);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = {
|
|
136
|
+
APPLINK_PAGE_LINK,
|
|
137
|
+
LEGACY_PAGE_LINK,
|
|
138
|
+
buildDingTalkPageLink,
|
|
139
|
+
extractPageUrl,
|
|
140
|
+
run,
|
|
141
|
+
};
|