dgxay-code-cli 0.0.0-stage → 1.0.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/LICENSE +674 -0
- package/README-zh.md +200 -0
- package/README.md +182 -2
- package/bin/dgxay.js +42 -0
- package/package.json +47 -4
- package/src/cli/ansi.js +509 -0
- package/src/cli/approval.js +186 -0
- package/src/cli/args.js +162 -0
- package/src/cli/command-table.js +52 -0
- package/src/cli/editor.js +574 -0
- package/src/cli/index.js +228 -0
- package/src/cli/print.js +148 -0
- package/src/cli/render.js +324 -0
- package/src/cli/repl-state.js +85 -0
- package/src/cli/repl.js +1167 -0
- package/src/cli/slash.js +74 -0
- package/src/cli/ui.js +52 -0
- package/src/commands/_shared.js +20 -0
- package/src/commands/chat.js +212 -0
- package/src/commands/config.js +363 -0
- package/src/commands/context.js +43 -0
- package/src/commands/doctor.js +192 -0
- package/src/commands/export.js +102 -0
- package/src/commands/mcp.js +257 -0
- package/src/commands/model.js +223 -0
- package/src/commands/oauth.js +246 -0
- package/src/commands/permission.js +108 -0
- package/src/commands/provider.js +332 -0
- package/src/commands/run.js +172 -0
- package/src/commands/skill.js +94 -0
- package/src/commands/workspace.js +100 -0
- package/src/core/agent.js +419 -0
- package/src/core/llm.js +709 -0
- package/src/core/mcp.js +271 -0
- package/src/core/oauth.js +737 -0
- package/src/core/prompt.js +269 -0
- package/src/core/sessions.js +208 -0
- package/src/core/tools.js +391 -0
- package/src/services/config.js +182 -0
- package/src/services/input.js +91 -0
- package/src/services/session-bus.js +104 -0
- package/src/services/settings.js +340 -0
- package/src/services/skills.js +125 -0
- package/src/services/terminal.js +124 -0
- package/src/services/version.js +25 -0
package/src/cli/args.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 轻量参数解析:支持 --flag、--flag value、--flag=value、-abc 组合、-- 终止符。
|
|
5
|
+
*
|
|
6
|
+
* 每个命令声明自己的选项:{ name, alias, type: 'string'|'boolean'|'number'|'list', desc, values }
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
function camel(name) {
|
|
10
|
+
return String(name).replace(/-([a-z])/g, (m, c) => c.toUpperCase());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function parseArgs(argv, spec = [], parseOptions = {}) {
|
|
14
|
+
const allowUnknown = Boolean(parseOptions.allowUnknown);
|
|
15
|
+
const options = {};
|
|
16
|
+
const positionals = [];
|
|
17
|
+
const lookup = new Map();
|
|
18
|
+
for (const item of spec) {
|
|
19
|
+
lookup.set(`--${item.name}`, item);
|
|
20
|
+
if (item.alias) {
|
|
21
|
+
for (const alias of [].concat(item.alias)) lookup.set(alias.length === 1 ? `-${alias}` : alias, item);
|
|
22
|
+
}
|
|
23
|
+
if (item.type === 'list') options[camel(item.name)] = [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const rest = argv.slice();
|
|
27
|
+
while (rest.length) {
|
|
28
|
+
const token = rest.shift();
|
|
29
|
+
if (token === '--') {
|
|
30
|
+
positionals.push(...rest);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
// -h / -v 是约定俗成的通用开关;但命令自己声明了同名短选项时以命令为准
|
|
34
|
+
const takesHelp = !lookup.has('-h');
|
|
35
|
+
const takesVersion = !lookup.has('-v');
|
|
36
|
+
if ((token === '-h' || token === '--help') && takesHelp) {
|
|
37
|
+
options.help = true;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if ((token === '-v' || token === '--version') && (takesVersion || token === '--version')) {
|
|
41
|
+
options.version = true;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (token.startsWith('--')) {
|
|
46
|
+
const eq = token.indexOf('=');
|
|
47
|
+
const name = eq === -1 ? token : token.slice(0, eq);
|
|
48
|
+
const inline = eq === -1 ? undefined : token.slice(eq + 1);
|
|
49
|
+
const item = lookup.get(name);
|
|
50
|
+
if (!item) {
|
|
51
|
+
if (allowUnknown) {
|
|
52
|
+
// 全局解析阶段:不认识的选项原样保留,交给子命令自己解析
|
|
53
|
+
positionals.push(token);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const error = new Error(`未知选项:${name}`);
|
|
57
|
+
error.userError = true;
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
const key = camel(item.name);
|
|
61
|
+
if (item.type === 'boolean') {
|
|
62
|
+
options[key] = inline === undefined ? true : !/^(false|0|no)$/i.test(inline);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
let value = inline;
|
|
66
|
+
if (value === undefined) {
|
|
67
|
+
value = rest.shift();
|
|
68
|
+
if (value === undefined) {
|
|
69
|
+
const error = new Error(`选项 ${name} 需要一个值`);
|
|
70
|
+
error.userError = true;
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (item.type === 'list') options[key] = options[key].concat([value]);
|
|
75
|
+
else if (item.type === 'number') options[key] = Number(value);
|
|
76
|
+
else options[key] = value;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 任意 -x / -abc 组合短选项(未知的仍然报错,避免把 -5 这类输入吞掉)
|
|
81
|
+
if (/^-[A-Za-z]/.test(token)) {
|
|
82
|
+
// 组合短选项:-abc,只有最后一位可以带值
|
|
83
|
+
const chars = token.slice(1).split('');
|
|
84
|
+
for (let i = 0; i < chars.length; i += 1) {
|
|
85
|
+
const item = lookup.get(`-${chars[i]}`);
|
|
86
|
+
if (!item) {
|
|
87
|
+
if (allowUnknown) {
|
|
88
|
+
positionals.push(token);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
const error = new Error(`未知选项:-${chars[i]}`);
|
|
92
|
+
error.userError = true;
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
const key = camel(item.name);
|
|
96
|
+
if (item.type === 'boolean') {
|
|
97
|
+
options[key] = true;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const value = i === chars.length - 1 ? rest.shift() : chars.slice(i + 1).join('');
|
|
101
|
+
if (value === undefined) {
|
|
102
|
+
const error = new Error(`选项 -${chars[i]} 需要一个值`);
|
|
103
|
+
error.userError = true;
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
if (item.type === 'list') options[key] = options[key].concat([value]);
|
|
107
|
+
else if (item.type === 'number') options[key] = Number(value);
|
|
108
|
+
else options[key] = value;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
positionals.push(token);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { options, positionals };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 多级子命令解析:第一个位置参数是动作名,剩余参数才按 spec 解析选项。
|
|
122
|
+
* 返回 { action, rest, options, positionals }
|
|
123
|
+
*/
|
|
124
|
+
function actionFirst(argv, spec = []) {
|
|
125
|
+
const list = argv.slice();
|
|
126
|
+
const actionSpec = {};
|
|
127
|
+
for (const item of Array.isArray(spec) ? spec : []) {
|
|
128
|
+
if (item && item.name) actionSpec[item.name] = item;
|
|
129
|
+
}
|
|
130
|
+
const defaultAction = spec && spec.action && typeof spec.action === 'string' ? spec.action : '';
|
|
131
|
+
const actionIndex = list.findIndex((token) => !token.startsWith('-'));
|
|
132
|
+
const action = actionIndex === -1 ? defaultAction : list[actionIndex];
|
|
133
|
+
if (actionIndex !== -1) list.splice(actionIndex, 1);
|
|
134
|
+
const parsed = parseArgs(list, Object.values(actionSpec));
|
|
135
|
+
return { action, rest: parsed.positionals, options: parsed.options, positionals: parsed.positionals };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 简单选择器:把用户输入按名称或序号解析成列表中的一项。
|
|
140
|
+
* 序号 1 表示第一项(与 /chat list 的输出保持一致)。
|
|
141
|
+
*/
|
|
142
|
+
function pick(list, ref, label = '项目') {
|
|
143
|
+
if (!list.length) return null;
|
|
144
|
+
if (!ref) return list[0];
|
|
145
|
+
const text = String(ref).trim();
|
|
146
|
+
const index = Number(text);
|
|
147
|
+
if (Number.isInteger(index) && index >= 1 && index <= list.length) return list[index - 1];
|
|
148
|
+
const exact = list.find((item) => item.id === text || String(item.name || '').toLowerCase() === text.toLowerCase());
|
|
149
|
+
if (exact) return exact;
|
|
150
|
+
const partial = list.filter(
|
|
151
|
+
(item) => String(item.id).startsWith(text) || String(item.name || '').toLowerCase().startsWith(text.toLowerCase())
|
|
152
|
+
);
|
|
153
|
+
if (partial.length === 1) return partial[0];
|
|
154
|
+
if (partial.length > 1) {
|
|
155
|
+
const error = new Error(`${label}名称不唯一:「${text}」匹配到 ${partial.length} 项`);
|
|
156
|
+
error.userError = true;
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = { parseArgs, actionFirst, pick, camel };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 子命令注册表。每个命令:{ name, aliases, summary, usage, options, run(ctx) }
|
|
5
|
+
* ctx = { argv, io, config, print, error, exitCode }
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const chat = require('../commands/chat');
|
|
9
|
+
const config = require('../commands/config');
|
|
10
|
+
const provider = require('../commands/provider');
|
|
11
|
+
const model = require('../commands/model');
|
|
12
|
+
const oauth = require('../commands/oauth');
|
|
13
|
+
const mcp = require('../commands/mcp');
|
|
14
|
+
const skill = require('../commands/skill');
|
|
15
|
+
const workspace = require('../commands/workspace');
|
|
16
|
+
const permission = require('../commands/permission');
|
|
17
|
+
const context = require('../commands/context');
|
|
18
|
+
const run = require('../commands/run');
|
|
19
|
+
const exportCmd = require('../commands/export');
|
|
20
|
+
const doctor = require('../commands/doctor');
|
|
21
|
+
|
|
22
|
+
const COMMANDS = [
|
|
23
|
+
run,
|
|
24
|
+
chat,
|
|
25
|
+
config,
|
|
26
|
+
provider,
|
|
27
|
+
model,
|
|
28
|
+
oauth,
|
|
29
|
+
mcp,
|
|
30
|
+
skill,
|
|
31
|
+
workspace,
|
|
32
|
+
permission,
|
|
33
|
+
context,
|
|
34
|
+
exportCmd,
|
|
35
|
+
doctor
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function findCommand(name) {
|
|
39
|
+
const target = String(name || '').toLowerCase();
|
|
40
|
+
return (
|
|
41
|
+
COMMANDS.find((cmd) => cmd.name === target) ||
|
|
42
|
+
COMMANDS.find((cmd) => (cmd.aliases || []).includes(target)) ||
|
|
43
|
+
null
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 汇总所有子命令的选项(用于全局解析后分发给命令自身) */
|
|
48
|
+
function specFor(command) {
|
|
49
|
+
return command?.options || [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { COMMANDS, findCommand, specFor };
|