yidaconnector 2026.6.11
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/LICENSE +21 -0
- package/README.md +383 -0
- package/bin/yida.js +670 -0
- package/lib/app/form-navigation.js +58 -0
- package/lib/app/get-schema.js +538 -0
- package/lib/auth/auth.js +294 -0
- package/lib/auth/cdp-browser-login.js +390 -0
- package/lib/auth/codex-login.js +71 -0
- package/lib/auth/login.js +475 -0
- package/lib/auth/org.js +363 -0
- package/lib/auth/qr-login.js +1563 -0
- package/lib/core/chalk.js +384 -0
- package/lib/core/check-update.js +82 -0
- package/lib/core/cli-error.js +39 -0
- package/lib/core/command-manifest.js +106 -0
- package/lib/core/env-cmd.js +545 -0
- package/lib/core/env-manager.js +601 -0
- package/lib/core/env.js +287 -0
- package/lib/core/i18n.js +177 -0
- package/lib/core/locales/ar.js +805 -0
- package/lib/core/locales/de.js +805 -0
- package/lib/core/locales/en.js +1623 -0
- package/lib/core/locales/es.js +805 -0
- package/lib/core/locales/fr.js +805 -0
- package/lib/core/locales/hi.js +805 -0
- package/lib/core/locales/ja.js +1197 -0
- package/lib/core/locales/ko.js +807 -0
- package/lib/core/locales/pt.js +805 -0
- package/lib/core/locales/vi.js +805 -0
- package/lib/core/locales/zh-HK.js +1233 -0
- package/lib/core/locales/zh.js +1584 -0
- package/lib/core/query-data.js +781 -0
- package/lib/core/redact.js +100 -0
- package/lib/core/utils.js +799 -0
- package/lib/core/yida-client.js +117 -0
- package/package.json +94 -0
- package/project/config.json +4 -0
- package/project/pages/src/demo-birthday-game.oyd.jsx +832 -0
- package/project/pages/src/demo-chip-insight.oyd.jsx +983 -0
- package/project/pages/src/demo-compat-smoke.oyd.jsx +58 -0
- package/project/pages/src/demo-crm-batch-entry.oyd.jsx +805 -0
- package/project/pages/src/demo-crm-dashboard.oyd.jsx +677 -0
- package/project/pages/src/demo-future-vision-2026.oyd.jsx +1102 -0
- package/project/pages/src/demo-ppt.oyd.jsx +1192 -0
- package/project/pages/src/demo-salary-calculator.oyd.jsx +904 -0
- package/project/pages/src/yidaconnector-knowledge-doc.oyd.jsx +1714 -0
- package/project/prd/demo-birthday-game.md +39 -0
- package/project/prd/demo-crm.md +463 -0
- package/project/prd/demo-dingtalk-ai-solution-center.md +425 -0
- package/project/prd/demo-future-vision-2026.md +78 -0
- package/project/prd/demo-salary-calculator.md +101 -0
- package/scripts/build-skills-package.js +406 -0
- package/scripts/check-syntax.js +59 -0
- package/scripts/demo-dws.sh +106 -0
- package/scripts/e2e-real/cleanup.js +67 -0
- package/scripts/e2e-real/fixtures/form-fields.json +18 -0
- package/scripts/e2e-real/full-runner.js +1566 -0
- package/scripts/e2e-real/runner.js +293 -0
- package/scripts/e2e-real/skill-coverage.js +115 -0
- package/scripts/generate-command-docs.js +109 -0
- package/scripts/nightly-smoke.js +134 -0
- package/scripts/postinstall.js +545 -0
- package/scripts/solution-center-runner.js +368 -0
- package/scripts/validate-ci.sh +50 -0
- package/scripts/validate-command-manifest.js +119 -0
- package/scripts/validate-package-size.js +78 -0
- package/scripts/validate-skills.js +247 -0
- package/scripts/validate-structure.js +66 -0
- package/yida-skills/SKILL.md +163 -0
- package/yida-skills/references/yida-api.md +1309 -0
- package/yida-skills/skills/large-file-write/SKILL.md +91 -0
- package/yida-skills/skills/large-file-write/references/write-patterns.md +149 -0
- package/yida-skills/skills/large-file-write/scripts/write.js +157 -0
- package/yida-skills/skills/yida-data-management/SKILL.md +252 -0
- package/yida-skills/skills/yida-data-management/references/api-matrix.md +49 -0
- package/yida-skills/skills/yida-data-management/references/data-format-guide.md +159 -0
- package/yida-skills/skills/yida-data-management/references/verified-endpoints.md +62 -0
- package/yida-skills/skills/yida-login/SKILL.md +159 -0
- package/yida-skills/skills/yida-logout/SKILL.md +67 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chalk.js - 公共终端样式工具模块
|
|
3
|
+
*
|
|
4
|
+
* 提供统一的 ANSI 颜色常量、图标、分隔线、spinner 动画、
|
|
5
|
+
* 表格渲染等终端输出工具,供所有 CLI 命令文件引用。
|
|
6
|
+
*
|
|
7
|
+
* 用法:
|
|
8
|
+
* const { c, icon, sep, banner, step, label, spinner, table } = require('./chalk');
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
// ── 全局静默开关 ───────────────────────────────────────
|
|
14
|
+
//
|
|
15
|
+
// YIDA_QUIET=1 时所有装饰性输出(banner/step/label/info/hint/listItem/
|
|
16
|
+
// table/result/spinner/success/warn/commandGroup/usage)变为 no-op;
|
|
17
|
+
// fail/error 仍写入 stderr 但去除装饰,保证错误信息可见。
|
|
18
|
+
// console.log 由各 command 用于输出 JSON 结果,本模块不做处理。
|
|
19
|
+
function isQuiet() {
|
|
20
|
+
return process.env.YIDA_QUIET === '1';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ── ANSI 颜色常量 ──────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
const c = {
|
|
26
|
+
reset: '\x1b[0m',
|
|
27
|
+
bold: '\x1b[1m',
|
|
28
|
+
dim: '\x1b[2m',
|
|
29
|
+
italic: '\x1b[3m',
|
|
30
|
+
underline: '\x1b[4m',
|
|
31
|
+
red: '\x1b[31m',
|
|
32
|
+
green: '\x1b[32m',
|
|
33
|
+
yellow: '\x1b[33m',
|
|
34
|
+
blue: '\x1b[34m',
|
|
35
|
+
magenta: '\x1b[35m',
|
|
36
|
+
cyan: '\x1b[36m',
|
|
37
|
+
white: '\x1b[37m',
|
|
38
|
+
gray: '\x1b[90m',
|
|
39
|
+
bgBlue: '\x1b[44m',
|
|
40
|
+
bgCyan: '\x1b[46m',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// ── 图标常量 ───────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
const icon = {
|
|
46
|
+
success: `${c.green}✔${c.reset}`,
|
|
47
|
+
fail: `${c.red}✖${c.reset}`,
|
|
48
|
+
warn: `${c.yellow}⚠${c.reset}`,
|
|
49
|
+
info: `${c.cyan}ℹ${c.reset}`,
|
|
50
|
+
arrow: `${c.cyan}→${c.reset}`,
|
|
51
|
+
bullet: `${c.dim}•${c.reset}`,
|
|
52
|
+
star: `${c.yellow}★${c.reset}`,
|
|
53
|
+
check: `${c.green}✓${c.reset}`,
|
|
54
|
+
cross: `${c.red}✗${c.reset}`,
|
|
55
|
+
dot: `${c.dim}·${c.reset}`,
|
|
56
|
+
play: `${c.green}▶${c.reset}`,
|
|
57
|
+
folder: `${c.cyan}📁${c.reset}`,
|
|
58
|
+
file: `${c.dim}📄${c.reset}`,
|
|
59
|
+
link: `${c.blue}🔗${c.reset}`,
|
|
60
|
+
key: `${c.yellow}🔑${c.reset}`,
|
|
61
|
+
lock: `${c.red}🔒${c.reset}`,
|
|
62
|
+
unlock: `${c.green}🔓${c.reset}`,
|
|
63
|
+
rocket: '🚀',
|
|
64
|
+
package: '📦',
|
|
65
|
+
gear: '⚙️',
|
|
66
|
+
sparkle: '✨',
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// ── 分隔线 ─────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 生成分隔线。
|
|
73
|
+
* @param {number} [width=60] - 分隔线宽度
|
|
74
|
+
* @returns {string}
|
|
75
|
+
*/
|
|
76
|
+
function sep(width = 60) {
|
|
77
|
+
return `${c.dim}${'─'.repeat(width)}${c.reset}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── Banner 标题 ────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 打印命令标题 banner(替代旧的 '='.repeat(55) 风格)。
|
|
84
|
+
* @param {string} title - 标题文字
|
|
85
|
+
* @param {object} [options]
|
|
86
|
+
* @param {string} [options.subtitle] - 副标题
|
|
87
|
+
* @param {boolean} [options.stderr=true] - 是否输出到 stderr
|
|
88
|
+
*/
|
|
89
|
+
function banner(title, options = {}) {
|
|
90
|
+
if (isQuiet()) {return;}
|
|
91
|
+
const { subtitle, stderr = true } = options;
|
|
92
|
+
const out = stderr ? process.stderr : process.stdout;
|
|
93
|
+
out.write('\n');
|
|
94
|
+
out.write(` ${c.bold}${c.cyan}${title}${c.reset}\n`);
|
|
95
|
+
if (subtitle) {
|
|
96
|
+
out.write(` ${c.dim}${subtitle}${c.reset}\n`);
|
|
97
|
+
}
|
|
98
|
+
out.write(` ${sep()}\n`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── 步骤输出 ───────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 打印步骤信息。
|
|
105
|
+
* @param {number} stepNumber - 步骤编号
|
|
106
|
+
* @param {string} message - 步骤描述
|
|
107
|
+
* @param {boolean} [stderr=true] - 是否输出到 stderr
|
|
108
|
+
*/
|
|
109
|
+
function step(stepNumber, message, stderr = true) {
|
|
110
|
+
if (isQuiet()) {return;}
|
|
111
|
+
const out = stderr ? process.stderr : process.stdout;
|
|
112
|
+
out.write(`\n ${c.cyan}[${stepNumber}]${c.reset} ${c.bold}${message}${c.reset}\n`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── 标签值对输出 ───────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 打印标签-值对(如 " 地址:https://...")。
|
|
119
|
+
* @param {string} labelText - 标签文字
|
|
120
|
+
* @param {string} value - 值
|
|
121
|
+
* @param {object} [options]
|
|
122
|
+
* @param {string} [options.indent=' '] - 缩进
|
|
123
|
+
* @param {boolean} [options.stderr=true] - 是否输出到 stderr
|
|
124
|
+
*/
|
|
125
|
+
function label(labelText, value, options = {}) {
|
|
126
|
+
if (isQuiet()) {return;}
|
|
127
|
+
const { indent = ' ', stderr = true } = options;
|
|
128
|
+
const out = stderr ? process.stderr : process.stdout;
|
|
129
|
+
out.write(`${indent}${c.dim}${labelText}${c.reset} ${value}\n`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── 状态消息 ───────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 打印成功消息。
|
|
136
|
+
* @param {string} message
|
|
137
|
+
* @param {boolean} [stderr=true]
|
|
138
|
+
*/
|
|
139
|
+
function success(message, stderr = true) {
|
|
140
|
+
if (isQuiet()) {return;}
|
|
141
|
+
if (stderr) {
|
|
142
|
+
console.error(` ${icon.success} ${message}`);
|
|
143
|
+
} else {
|
|
144
|
+
process.stdout.write(` ${icon.success} ${message}\n`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 打印失败消息。
|
|
150
|
+
* @param {string} message
|
|
151
|
+
* @param {boolean} [stderr=true]
|
|
152
|
+
*/
|
|
153
|
+
function fail(message, stderr = true) {
|
|
154
|
+
// fail 在 quiet 模式下仍写 stderr,但去除装饰,保证错误可见
|
|
155
|
+
if (isQuiet()) {
|
|
156
|
+
process.stderr.write(`${message}\n`);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (stderr) {
|
|
160
|
+
console.error(` ${icon.fail} ${c.red}${message}${c.reset}`);
|
|
161
|
+
} else {
|
|
162
|
+
process.stdout.write(` ${icon.fail} ${c.red}${message}${c.reset}\n`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 打印警告消息。
|
|
168
|
+
* @param {string} message
|
|
169
|
+
* @param {boolean} [stderr=true]
|
|
170
|
+
*/
|
|
171
|
+
function warn(message, stderr = true) {
|
|
172
|
+
if (isQuiet()) {return;}
|
|
173
|
+
if (stderr) {
|
|
174
|
+
console.error(` ${icon.warn} ${c.yellow}${message}${c.reset}`);
|
|
175
|
+
} else {
|
|
176
|
+
process.stdout.write(` ${icon.warn} ${c.yellow}${message}${c.reset}\n`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 打印信息消息。
|
|
182
|
+
* @param {string} message
|
|
183
|
+
* @param {boolean} [stderr=true]
|
|
184
|
+
*/
|
|
185
|
+
function info(message, stderr = true) {
|
|
186
|
+
if (isQuiet()) {return;}
|
|
187
|
+
if (stderr) {
|
|
188
|
+
console.error(` ${icon.info} ${message}`);
|
|
189
|
+
} else {
|
|
190
|
+
process.stdout.write(` ${icon.info} ${message}\n`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 打印暗淡的提示文字。
|
|
196
|
+
* @param {string} message
|
|
197
|
+
* @param {boolean} [stderr=true]
|
|
198
|
+
*/
|
|
199
|
+
function hint(message, stderr = true) {
|
|
200
|
+
if (isQuiet()) {return;}
|
|
201
|
+
if (stderr) {
|
|
202
|
+
console.error(` ${c.dim}${message}${c.reset}`);
|
|
203
|
+
} else {
|
|
204
|
+
process.stdout.write(` ${c.dim}${message}${c.reset}\n`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ── Spinner 动画 ───────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 创建 spinner 动画。
|
|
214
|
+
* @param {string} message - 加载提示文字
|
|
215
|
+
* @returns {{ succeed: function, fail: function, update: function }}
|
|
216
|
+
*/
|
|
217
|
+
function spinner(message) {
|
|
218
|
+
if (isQuiet()) {
|
|
219
|
+
return { succeed() {}, fail() {}, update() {} };
|
|
220
|
+
}
|
|
221
|
+
let frameIndex = 0;
|
|
222
|
+
const timer = setInterval(() => {
|
|
223
|
+
process.stderr.write(`\r ${c.cyan}${SPINNER_FRAMES[frameIndex]}${c.reset} ${message}`);
|
|
224
|
+
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
225
|
+
}, 80);
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
succeed(text) {
|
|
229
|
+
clearInterval(timer);
|
|
230
|
+
process.stderr.write(`\r ${icon.success} ${text}\n`);
|
|
231
|
+
},
|
|
232
|
+
fail(text) {
|
|
233
|
+
clearInterval(timer);
|
|
234
|
+
process.stderr.write(`\r ${icon.fail} ${c.red}${text}${c.reset}\n`);
|
|
235
|
+
},
|
|
236
|
+
update(text) {
|
|
237
|
+
message = text;
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ── 表格渲染 ───────────────────────────────────────────
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 渲染键值对表格(仿 openclaw 风格)。
|
|
246
|
+
* @param {Array<[string, string]>} rows - [标签, 值] 数组
|
|
247
|
+
* @param {object} [options]
|
|
248
|
+
* @param {boolean} [options.stderr=true]
|
|
249
|
+
* @param {string} [options.indent=' ']
|
|
250
|
+
*/
|
|
251
|
+
function table(rows, options = {}) {
|
|
252
|
+
if (isQuiet()) {return;}
|
|
253
|
+
const { stderr = true, indent = ' ' } = options;
|
|
254
|
+
const out = stderr ? process.stderr : process.stdout;
|
|
255
|
+
const labelWidth = Math.max(...rows.map(([labelText]) => labelText.length)) + 2;
|
|
256
|
+
|
|
257
|
+
for (const [labelText, value] of rows) {
|
|
258
|
+
out.write(`${indent}${c.dim}${labelText.padEnd(labelWidth)}${c.reset}${value}\n`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 渲染命令分组列表(用于 help 输出)。
|
|
264
|
+
* @param {string} groupTitle - 分组标题
|
|
265
|
+
* @param {Array<[string, string]>} commands - [命令, 描述] 数组
|
|
266
|
+
* @param {boolean} [stderr=false]
|
|
267
|
+
*/
|
|
268
|
+
function commandGroup(groupTitle, commands, stderr = false) {
|
|
269
|
+
if (isQuiet()) {return;}
|
|
270
|
+
const out = stderr ? process.stderr : process.stdout;
|
|
271
|
+
out.write(`\n ${c.bold}${c.cyan}${groupTitle}${c.reset}\n`);
|
|
272
|
+
const maxCmdLen = Math.max(...commands.map(([cmd]) => cmd.length));
|
|
273
|
+
const padWidth = Math.min(maxCmdLen + 2, 50);
|
|
274
|
+
for (const [cmd, desc] of commands) {
|
|
275
|
+
out.write(` ${c.green}${cmd.padEnd(padWidth)}${c.reset}${c.dim}${desc}${c.reset}\n`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ── 列表项输出 ─────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 打印列表项。
|
|
283
|
+
* @param {string} text - 列表项文字
|
|
284
|
+
* @param {object} [options]
|
|
285
|
+
* @param {string} [options.indent=' '] - 缩进
|
|
286
|
+
* @param {string} [options.marker] - 自定义标记(默认使用 bullet)
|
|
287
|
+
* @param {boolean} [options.stderr=true]
|
|
288
|
+
*/
|
|
289
|
+
function listItem(text, options = {}) {
|
|
290
|
+
if (isQuiet()) {return;}
|
|
291
|
+
const { indent = ' ', marker = icon.bullet, stderr = true } = options;
|
|
292
|
+
const out = stderr ? process.stderr : process.stdout;
|
|
293
|
+
out.write(`${indent}${marker} ${text}\n`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ── 错误输出 ───────────────────────────────────────────
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* 打印错误信息并退出。
|
|
300
|
+
* @param {string} message - 错误消息
|
|
301
|
+
* @param {object} [options]
|
|
302
|
+
* @param {string} [options.hint] - 提示信息
|
|
303
|
+
* @param {boolean} [options.exit=true] - 是否退出进程
|
|
304
|
+
*/
|
|
305
|
+
function error(message, options = {}) {
|
|
306
|
+
const { hint: hintText, exit: shouldExit = true } = options;
|
|
307
|
+
// error 在 quiet 模式下仍输出,但去除装饰,保证错误可见
|
|
308
|
+
if (isQuiet()) {
|
|
309
|
+
process.stderr.write(`${message}\n`);
|
|
310
|
+
if (hintText) {
|
|
311
|
+
process.stderr.write(`${hintText}\n`);
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
process.stderr.write(`\n ${icon.fail} ${c.red}${message}${c.reset}\n`);
|
|
315
|
+
if (hintText) {
|
|
316
|
+
process.stderr.write(` ${c.dim}${hintText}${c.reset}\n`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (shouldExit) {
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── 用法提示 ───────────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* 打印命令用法提示。
|
|
328
|
+
* @param {string} usage - 用法字符串
|
|
329
|
+
* @param {string} [example] - 示例
|
|
330
|
+
*/
|
|
331
|
+
function usage(usageText, example) {
|
|
332
|
+
if (isQuiet()) {return;}
|
|
333
|
+
process.stderr.write(`\n ${c.yellow}用法:${c.reset} ${usageText}\n`);
|
|
334
|
+
if (example) {
|
|
335
|
+
process.stderr.write(` ${c.dim}示例:${c.reset} ${example}\n`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ── 结果摘要 ───────────────────────────────────────────
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* 打印操作结果摘要框。
|
|
343
|
+
* @param {boolean} isSuccess - 是否成功
|
|
344
|
+
* @param {string} title - 标题
|
|
345
|
+
* @param {Array<[string, string]>} [details] - 详情键值对
|
|
346
|
+
* @param {boolean} [stderr=true]
|
|
347
|
+
*/
|
|
348
|
+
function result(isSuccess, title, details, stderr = true) {
|
|
349
|
+
if (isQuiet()) {return;}
|
|
350
|
+
const out = stderr ? process.stderr : process.stdout;
|
|
351
|
+
out.write('\n');
|
|
352
|
+
out.write(` ${sep()}\n`);
|
|
353
|
+
if (isSuccess) {
|
|
354
|
+
out.write(` ${icon.success} ${c.green}${c.bold}${title}${c.reset}\n`);
|
|
355
|
+
} else {
|
|
356
|
+
out.write(` ${icon.fail} ${c.red}${c.bold}${title}${c.reset}\n`);
|
|
357
|
+
}
|
|
358
|
+
if (details && details.length > 0) {
|
|
359
|
+
out.write('\n');
|
|
360
|
+
table(details, { stderr, indent: ' ' });
|
|
361
|
+
}
|
|
362
|
+
out.write(` ${sep()}\n\n`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
module.exports = {
|
|
366
|
+
c,
|
|
367
|
+
icon,
|
|
368
|
+
sep,
|
|
369
|
+
banner,
|
|
370
|
+
step,
|
|
371
|
+
label,
|
|
372
|
+
success,
|
|
373
|
+
fail,
|
|
374
|
+
warn,
|
|
375
|
+
info,
|
|
376
|
+
hint,
|
|
377
|
+
spinner,
|
|
378
|
+
table,
|
|
379
|
+
commandGroup,
|
|
380
|
+
listItem,
|
|
381
|
+
error,
|
|
382
|
+
usage,
|
|
383
|
+
result,
|
|
384
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* check-update.js - yidaconnector 版本更新检查
|
|
3
|
+
*
|
|
4
|
+
* 向 npm registry 查询最新版本,有新版本时打印提示。
|
|
5
|
+
* 全程异步,不阻塞主命令流程。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const https = require('https');
|
|
11
|
+
const { t } = require('./i18n');
|
|
12
|
+
|
|
13
|
+
const REGISTRY_URL = 'https://registry.npmjs.org/yidaconnector/latest';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 从 npm registry 获取最新版本号。
|
|
17
|
+
* @returns {Promise<string|null>}
|
|
18
|
+
*/
|
|
19
|
+
function fetchLatestVersion() {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const req = https.get(REGISTRY_URL, { timeout: 5000 }, (res) => {
|
|
22
|
+
let data = '';
|
|
23
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
24
|
+
res.on('end', () => {
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(data);
|
|
27
|
+
resolve(parsed.version || null);
|
|
28
|
+
} catch {
|
|
29
|
+
resolve(null);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
req.on('error', () => resolve(null));
|
|
34
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
35
|
+
req.on('socket', (socket) => {
|
|
36
|
+
if (socket && typeof socket.unref === 'function') {
|
|
37
|
+
socket.unref();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
if (typeof req.unref === 'function') {
|
|
41
|
+
req.unref();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 比较版本号,返回 latestVersion 是否比 currentVersion 更新。
|
|
48
|
+
* 支持 semver 格式(major.minor.patch),pre-release 标签(如 -beta.1)会被忽略,
|
|
49
|
+
* 仅比较数字部分。
|
|
50
|
+
*/
|
|
51
|
+
function isNewer(currentVersion, latestVersion) {
|
|
52
|
+
// 截取 '-' 前的纯数字版本部分,兼容 pre-release 格式(如 1.2.3-beta.1)
|
|
53
|
+
const parseStableVersion = (v) => (v || '').split('-')[0];
|
|
54
|
+
const parseParts = (v) => parseStableVersion(v).split('.').map((n) => parseInt(n, 10) || 0);
|
|
55
|
+
const [cMajor, cMinor, cPatch] = parseParts(currentVersion);
|
|
56
|
+
const [lMajor, lMinor, lPatch] = parseParts(latestVersion);
|
|
57
|
+
|
|
58
|
+
if (lMajor !== cMajor) {return lMajor > cMajor;}
|
|
59
|
+
if (lMinor !== cMinor) {return lMinor > cMinor;}
|
|
60
|
+
return lPatch > cPatch;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 检查是否有新版本,有则打印提示。
|
|
65
|
+
* @param {string} currentVersion - 当前版本号(来自 package.json)
|
|
66
|
+
*/
|
|
67
|
+
async function checkUpdate(currentVersion) {
|
|
68
|
+
try {
|
|
69
|
+
const latestVersion = await fetchLatestVersion();
|
|
70
|
+
|
|
71
|
+
if (latestVersion && isNewer(currentVersion, latestVersion)) {
|
|
72
|
+
process.nextTick(() => {
|
|
73
|
+
const { c } = require('./chalk');
|
|
74
|
+
process.stderr.write(`\n ${c.yellow}⚠${c.reset} ${t('check_update.new_version', latestVersion, currentVersion)}\n`);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
// 版本检查失败静默忽略,不影响主流程
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = { checkUpdate, isNewer, fetchLatestVersion };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { redactSensitive } = require('./redact');
|
|
4
|
+
|
|
5
|
+
class CliError extends Error {
|
|
6
|
+
constructor(message, options = {}) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'CliError';
|
|
9
|
+
this.code = options.code || 'CLI_ERROR';
|
|
10
|
+
this.exitCode = options.exitCode || 1;
|
|
11
|
+
this.details = options.details;
|
|
12
|
+
this.usage = options.usage;
|
|
13
|
+
this.isCliError = true;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isCliError(error) {
|
|
18
|
+
return !!(error && error.isCliError);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function toErrorPayload(error) {
|
|
22
|
+
const payload = {
|
|
23
|
+
success: false,
|
|
24
|
+
errorCode: isCliError(error) ? error.code : 'UNEXPECTED_ERROR',
|
|
25
|
+
errorMsg: error && error.message ? error.message : String(error),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (isCliError(error) && error.details !== undefined) {
|
|
29
|
+
payload.details = redactSensitive(error.details);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return payload;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
CliError,
|
|
37
|
+
isCliError,
|
|
38
|
+
toErrorPayload,
|
|
39
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function command(id, path, usage, descriptionKey, options = {}) {
|
|
4
|
+
return {
|
|
5
|
+
id,
|
|
6
|
+
path,
|
|
7
|
+
command: path[0],
|
|
8
|
+
name: path.join(' '),
|
|
9
|
+
usage,
|
|
10
|
+
descriptionKey,
|
|
11
|
+
requiresLogin: options.requiresLogin !== false,
|
|
12
|
+
output: options.output || 'text',
|
|
13
|
+
aliases: options.aliases || [],
|
|
14
|
+
examples: options.examples || [],
|
|
15
|
+
hidden: options.hidden === true,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const COMMAND_GROUPS = [
|
|
20
|
+
{
|
|
21
|
+
id: 'auth',
|
|
22
|
+
titleKey: 'help.group_auth',
|
|
23
|
+
commands: [
|
|
24
|
+
command('login', ['login'], 'login [target-url] [--qr|--agent-qr|--codex|--browser] [--env <name>|--intl|--overseas|--global|--yidaapps|--alibaba] [--corp-id <corpId>]', 'help.cmd_login', {
|
|
25
|
+
requiresLogin: false,
|
|
26
|
+
output: 'json',
|
|
27
|
+
}),
|
|
28
|
+
command('logout', ['logout'], 'logout', 'help.cmd_logout', { requiresLogin: false }),
|
|
29
|
+
command('auth', ['auth'], 'auth <status|login|refresh|logout>', 'help.cmd_auth', { requiresLogin: false }),
|
|
30
|
+
command('org', ['org'], 'org <list|switch>', 'help.cmd_org'),
|
|
31
|
+
command('env', ['env'], 'env [--json]', 'help.cmd_env', {
|
|
32
|
+
requiresLogin: false,
|
|
33
|
+
output: 'text|json',
|
|
34
|
+
}),
|
|
35
|
+
command('env-management', ['env'], 'env <setup|list|show|switch|add|remove>', 'help.cmd_env_management', {
|
|
36
|
+
requiresLogin: false,
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'data',
|
|
42
|
+
titleKey: 'help.group_data',
|
|
43
|
+
commands: [
|
|
44
|
+
command('data', ['data'], 'data <action> <resource> [args]', 'help.cmd_data'),
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'utility',
|
|
49
|
+
titleKey: 'help.group_utility',
|
|
50
|
+
commands: [
|
|
51
|
+
command('commands', ['commands'], 'commands [--json]', 'help.cmd_commands', {
|
|
52
|
+
requiresLogin: false,
|
|
53
|
+
output: 'json',
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
function flattenCommandManifest(groups = COMMAND_GROUPS) {
|
|
60
|
+
return groups.flatMap(group => group.commands.map(entry => ({ ...entry, group: group.id })));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function localizeCommand(entry, translate) {
|
|
64
|
+
return {
|
|
65
|
+
id: entry.id,
|
|
66
|
+
name: entry.name,
|
|
67
|
+
path: entry.path,
|
|
68
|
+
command: entry.command,
|
|
69
|
+
usage: `yidaconnector ${entry.usage}`,
|
|
70
|
+
raw_usage: entry.usage,
|
|
71
|
+
description: translate(entry.descriptionKey),
|
|
72
|
+
description_key: entry.descriptionKey,
|
|
73
|
+
group: entry.group,
|
|
74
|
+
requires_login: entry.requiresLogin,
|
|
75
|
+
output: entry.output,
|
|
76
|
+
aliases: entry.aliases,
|
|
77
|
+
examples: entry.examples,
|
|
78
|
+
hidden: entry.hidden,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildCommandManifest(options = {}) {
|
|
83
|
+
const translate = typeof options.t === 'function' ? options.t : key => key;
|
|
84
|
+
const commands = flattenCommandManifest();
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
schema_version: 1,
|
|
88
|
+
name: 'yidaconnector',
|
|
89
|
+
version: options.version || null,
|
|
90
|
+
aliases: ['yida'],
|
|
91
|
+
command_prefix: 'yidaconnector',
|
|
92
|
+
groups: COMMAND_GROUPS.map(group => ({
|
|
93
|
+
id: group.id,
|
|
94
|
+
title: translate(group.titleKey),
|
|
95
|
+
title_key: group.titleKey,
|
|
96
|
+
commands: group.commands.map(entry => entry.id),
|
|
97
|
+
})),
|
|
98
|
+
commands: commands.map(entry => localizeCommand(entry, translate)),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
COMMAND_GROUPS,
|
|
104
|
+
buildCommandManifest,
|
|
105
|
+
flattenCommandManifest,
|
|
106
|
+
};
|