dgxay-code-cli 1.0.0 → 1.1.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-zh.md +50 -8
- package/README.md +50 -9
- package/package.json +1 -1
- package/src/cli/ansi.js +38 -8
- package/src/cli/approval.js +11 -11
- package/src/cli/editor.js +456 -202
- package/src/cli/i18n.js +276 -0
- package/src/cli/index.js +34 -34
- package/src/cli/print.js +5 -5
- package/src/cli/render.js +147 -76
- package/src/cli/repl.js +205 -141
- package/src/cli/slash.js +29 -28
- package/src/cli/ui.js +8 -8
- package/src/commands/chat.js +45 -45
- package/src/commands/config.js +81 -81
- package/src/commands/context.js +5 -5
- package/src/commands/doctor.js +34 -34
- package/src/commands/export.js +13 -13
- package/src/commands/mcp.js +51 -51
- package/src/commands/model.js +38 -38
- package/src/commands/oauth.js +61 -61
- package/src/commands/permission.js +19 -19
- package/src/commands/provider.js +70 -70
- package/src/commands/run.js +20 -20
- package/src/commands/skill.js +15 -15
- package/src/commands/workspace.js +18 -18
- package/src/core/agent.js +19 -18
- package/src/core/llm.js +10 -10
- package/src/core/mcp.js +5 -5
- package/src/core/oauth.js +36 -36
- package/src/core/prompt.js +46 -46
- package/src/core/sessions.js +7 -7
- package/src/core/tools.js +24 -24
- package/src/services/input.js +1 -1
- package/src/services/settings.js +28 -28
- package/src/services/skills.js +1 -1
- package/src/services/terminal.js +3 -3
package/src/commands/oauth.js
CHANGED
|
@@ -19,17 +19,17 @@ const { ask } = require('../services/input');
|
|
|
19
19
|
|
|
20
20
|
const options = [
|
|
21
21
|
{ name: 'client-id', type: 'string', valueName: 'id', desc: 'OAuth Client ID' },
|
|
22
|
-
{ name: 'client-secret', type: 'string', valueName: 'secret', desc: 'OAuth Client Secret
|
|
23
|
-
{ name: 'authorize-url', type: 'string', valueName: 'url', desc: '
|
|
24
|
-
{ name: 'token-url', type: 'string', valueName: 'url', desc: '
|
|
25
|
-
{ name: 'scopes', type: 'string', valueName: 'text', desc: '
|
|
26
|
-
{ name: 'code', type: 'string', valueName: 'code', desc: '
|
|
27
|
-
{ name: 'no-browser', type: 'boolean', desc: '
|
|
28
|
-
{ name: 'yes', alias: 'y', type: 'boolean', desc: '
|
|
22
|
+
{ name: 'client-secret', type: 'string', valueName: 'secret', desc: 'OAuth Client Secret (optional)' },
|
|
23
|
+
{ name: 'authorize-url', type: 'string', valueName: 'url', desc: 'Authorize endpoint' },
|
|
24
|
+
{ name: 'token-url', type: 'string', valueName: 'url', desc: 'Token endpoint' },
|
|
25
|
+
{ name: 'scopes', type: 'string', valueName: 'text', desc: 'Permission scope' },
|
|
26
|
+
{ name: 'code', type: 'string', valueName: 'code', desc: 'Manual flow: submit the auth code directly (code#state)' },
|
|
27
|
+
{ name: 'no-browser', type: 'boolean', desc: 'Do not open the browser, only print the authorize link' },
|
|
28
|
+
{ name: 'yes', alias: 'y', type: 'boolean', desc: 'Skip confirmation' }
|
|
29
29
|
];
|
|
30
30
|
|
|
31
31
|
function formatExpiry(ts) {
|
|
32
|
-
if (!ts) return '
|
|
32
|
+
if (!ts) return 'never expires';
|
|
33
33
|
const date = new Date(ts);
|
|
34
34
|
const pad = (value) => String(value).padStart(2, '0');
|
|
35
35
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(
|
|
@@ -40,10 +40,10 @@ function formatExpiry(ts) {
|
|
|
40
40
|
function list() {
|
|
41
41
|
const providers = oauth.listProviders();
|
|
42
42
|
print.blank();
|
|
43
|
-
print.heading('
|
|
43
|
+
print.heading('Account connections');
|
|
44
44
|
const rows = providers.map((item) => {
|
|
45
45
|
const connection = item.connection;
|
|
46
|
-
const status = !connection ? '
|
|
46
|
+
const status = !connection ? 'not connected' : connection.expired ? 'expired' : 'connected';
|
|
47
47
|
const tone = !connection ? (value) => value : connection.expired ? COLORS.yellow : COLORS.green;
|
|
48
48
|
return [
|
|
49
49
|
item.id,
|
|
@@ -52,24 +52,24 @@ function list() {
|
|
|
52
52
|
tone(status),
|
|
53
53
|
connection?.account || '',
|
|
54
54
|
connection ? formatExpiry(connection.expiresAt) : '',
|
|
55
|
-
item.redirectMode === 'manual' ? '
|
|
55
|
+
item.redirectMode === 'manual' ? 'manual auth code' : 'system browser'
|
|
56
56
|
];
|
|
57
57
|
});
|
|
58
58
|
print.table(
|
|
59
59
|
[
|
|
60
60
|
{ title: 'ID', max: 10 },
|
|
61
|
-
{ title: '
|
|
62
|
-
{ title: '
|
|
63
|
-
{ title: '
|
|
64
|
-
{ title: '
|
|
65
|
-
{ title: '
|
|
66
|
-
{ title: '
|
|
61
|
+
{ title: 'Platform', max: 10 },
|
|
62
|
+
{ title: 'Account type', max: 24 },
|
|
63
|
+
{ title: 'Status', max: 8 },
|
|
64
|
+
{ title: 'Account', max: 28 },
|
|
65
|
+
{ title: 'Expires', max: 18 },
|
|
66
|
+
{ title: 'Auth method', max: 12 }
|
|
67
67
|
],
|
|
68
68
|
rows
|
|
69
69
|
);
|
|
70
70
|
print.blank();
|
|
71
|
-
print.hint('dgxay oauth connect
|
|
72
|
-
print.hint('
|
|
71
|
+
print.hint('dgxay oauth connect <platform> to connect · dgxay oauth disconnect <platform> to disconnect');
|
|
72
|
+
print.hint('After connecting, enable OAuth on a provider: dgxay provider edit <name> --oauth-provider <platform>');
|
|
73
73
|
print.blank();
|
|
74
74
|
return 0;
|
|
75
75
|
}
|
|
@@ -77,14 +77,14 @@ function list() {
|
|
|
77
77
|
async function connect(id, opts) {
|
|
78
78
|
const def = oauth.PROVIDERS[id];
|
|
79
79
|
if (!def) {
|
|
80
|
-
print.fail(
|
|
80
|
+
print.fail(`Unsupported platform: ${id} (choose openai / anthropic / xai / google)`);
|
|
81
81
|
return 1;
|
|
82
82
|
}
|
|
83
83
|
const current = oauth.listProviders().find((item) => item.id === id);
|
|
84
84
|
if (current.connection && !current.connection.expired && !opts.yes && process.stdin.isTTY) {
|
|
85
|
-
const answer = await ask(`${def.name}
|
|
85
|
+
const answer = await ask(`${def.name} is already connected (${current.connection.account}). Re-authorizing overwrites the existing token. Continue? [y/N] `);
|
|
86
86
|
if (!/^y/i.test(String(answer || '').trim())) {
|
|
87
|
-
print.hint('
|
|
87
|
+
print.hint('Cancelled');
|
|
88
88
|
return 1;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -97,56 +97,56 @@ async function connect(id, opts) {
|
|
|
97
97
|
} catch (err) {
|
|
98
98
|
print.fail(err.message);
|
|
99
99
|
if (/Client ID/i.test(err.message)) {
|
|
100
|
-
print.hint(`Google
|
|
100
|
+
print.hint(`Google requires a desktop-app OAuth client first: dgxay oauth client google --client-id <ID>`);
|
|
101
101
|
}
|
|
102
102
|
return 1;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
print.blank();
|
|
106
|
-
print.heading(
|
|
106
|
+
print.heading(`Connect ${def.name}`);
|
|
107
107
|
print.hint(def.hint);
|
|
108
108
|
print.blank();
|
|
109
|
-
if (flow.browserOpened) print.ok('
|
|
109
|
+
if (flow.browserOpened) print.ok('Opened the system browser; complete authorization in the browser');
|
|
110
110
|
else {
|
|
111
|
-
print.warn('
|
|
111
|
+
print.warn('Could not open the browser automatically; open the link below manually:');
|
|
112
112
|
}
|
|
113
113
|
print.blank();
|
|
114
114
|
process.stdout.write(`${COLORS.cyan(flow.authUrl)}\n`);
|
|
115
115
|
print.blank();
|
|
116
116
|
|
|
117
117
|
if (flow.mode === 'manual') {
|
|
118
|
-
print.write('
|
|
118
|
+
print.write('After authorizing in the browser, copy the auth code (like code#state) and paste it here:');
|
|
119
119
|
let code = opts.code;
|
|
120
120
|
if (!code) {
|
|
121
121
|
if (!process.stdin.isTTY) {
|
|
122
|
-
print.fail('
|
|
122
|
+
print.fail('In non-interactive environments use --code <auth code>');
|
|
123
123
|
return 1;
|
|
124
124
|
}
|
|
125
|
-
code = await ask('
|
|
125
|
+
code = await ask('Auth code: ');
|
|
126
126
|
}
|
|
127
127
|
if (!code) {
|
|
128
|
-
print.fail('
|
|
128
|
+
print.fail('Auth code is empty');
|
|
129
129
|
return 1;
|
|
130
130
|
}
|
|
131
131
|
try {
|
|
132
132
|
const connection = await oauth.complete(flow.id, code);
|
|
133
|
-
print.ok(`${def.name}
|
|
133
|
+
print.ok(`${def.name} account connected: ${connection.account}`);
|
|
134
134
|
return 0;
|
|
135
135
|
} catch (err) {
|
|
136
|
-
print.fail(err.message || '
|
|
136
|
+
print.fail(err.message || 'Authorization failed');
|
|
137
137
|
return 1;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
process.stdout.write(`${COLORS.faint('
|
|
141
|
+
process.stdout.write(`${COLORS.faint('Waiting for the browser callback to finish authorization... (Ctrl+C to cancel)')}\n`);
|
|
142
142
|
try {
|
|
143
143
|
const connection = await oauth.wait(flow.id);
|
|
144
144
|
print.blank();
|
|
145
|
-
print.ok(`${def.name}
|
|
146
|
-
print.hint('
|
|
145
|
+
print.ok(`${def.name} account connected: ${connection.account}`);
|
|
146
|
+
print.hint('Enable it on a provider: dgxay provider edit <name> --oauth-provider ' + id);
|
|
147
147
|
return 0;
|
|
148
148
|
} catch (err) {
|
|
149
|
-
print.fail(err.message || '
|
|
149
|
+
print.fail(err.message || 'Authorization failed');
|
|
150
150
|
return 1;
|
|
151
151
|
}
|
|
152
152
|
}
|
|
@@ -154,24 +154,24 @@ async function connect(id, opts) {
|
|
|
154
154
|
function disconnect(id, opts) {
|
|
155
155
|
const def = oauth.PROVIDERS[id];
|
|
156
156
|
if (!def) {
|
|
157
|
-
print.fail(
|
|
157
|
+
print.fail(`Unsupported platform: ${id}`);
|
|
158
158
|
return 1;
|
|
159
159
|
}
|
|
160
160
|
const current = oauth.listProviders().find((item) => item.id === id);
|
|
161
161
|
if (!current?.connection) {
|
|
162
|
-
print.warn(`${def.name}
|
|
162
|
+
print.warn(`${def.name} is not connected`);
|
|
163
163
|
return 1;
|
|
164
164
|
}
|
|
165
165
|
oauth.disconnect(id);
|
|
166
|
-
print.ok(
|
|
167
|
-
print.hint('
|
|
166
|
+
print.ok(`Disconnected ${def.name} account (${current.connection.account})`);
|
|
167
|
+
print.hint('Providers using this account cannot make requests until you reconnect or switch to an API Key');
|
|
168
168
|
return 0;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
function saveClient(id, opts) {
|
|
172
172
|
const def = oauth.PROVIDERS[id];
|
|
173
173
|
if (!def) {
|
|
174
|
-
print.fail(
|
|
174
|
+
print.fail(`Unsupported platform: ${id}`);
|
|
175
175
|
return 1;
|
|
176
176
|
}
|
|
177
177
|
const patch = {};
|
|
@@ -181,24 +181,24 @@ function saveClient(id, opts) {
|
|
|
181
181
|
if (!Object.keys(patch).length) {
|
|
182
182
|
const current = oauth.listProviders().find((item) => item.id === id);
|
|
183
183
|
print.blank();
|
|
184
|
-
print.heading(`${def.name} OAuth
|
|
184
|
+
print.heading(`${def.name} OAuth client`);
|
|
185
185
|
print.keyValue([
|
|
186
|
-
{ label: 'Client ID', value: current.clientId ? `${current.clientId.slice(0, 8)}…` : '
|
|
187
|
-
{ label: 'Client Secret', value: current.hasSecret ? '
|
|
188
|
-
{ label: '
|
|
189
|
-
{ label: '
|
|
190
|
-
{ label: '
|
|
191
|
-
{ label: '
|
|
186
|
+
{ label: 'Client ID', value: current.clientId ? `${current.clientId.slice(0, 8)}…` : '(empty)' },
|
|
187
|
+
{ label: 'Client Secret', value: current.hasSecret ? 'set' : 'not set' },
|
|
188
|
+
{ label: 'Authorize endpoint', value: current.authorizeUrl },
|
|
189
|
+
{ label: 'Token endpoint', value: current.tokenUrl },
|
|
190
|
+
{ label: 'Permission scope', value: current.scopes },
|
|
191
|
+
{ label: 'Redirect URI', value: current.redirectUri }
|
|
192
192
|
]);
|
|
193
193
|
print.blank();
|
|
194
|
-
print.hint(
|
|
194
|
+
print.hint(`Modify: dgxay oauth client ${id} --client-id <ID> [--client-secret <Secret>]`);
|
|
195
195
|
print.blank();
|
|
196
196
|
return 0;
|
|
197
197
|
}
|
|
198
198
|
try {
|
|
199
199
|
const saved = oauth.saveClient(id, patch);
|
|
200
|
-
print.ok(
|
|
201
|
-
print.hint(
|
|
200
|
+
print.ok(`Saved OAuth client config for ${def.name}`);
|
|
201
|
+
print.hint(`You can connect now: dgxay oauth connect ${saved.id}`);
|
|
202
202
|
return 0;
|
|
203
203
|
} catch (err) {
|
|
204
204
|
print.fail(err.message);
|
|
@@ -213,33 +213,33 @@ async function run({ argv }) {
|
|
|
213
213
|
|
|
214
214
|
if (action === 'list' || action === 'ls') return list();
|
|
215
215
|
if (action === 'connect' || action === 'login') {
|
|
216
|
-
if (!target) throw Object.assign(new Error('
|
|
216
|
+
if (!target) throw Object.assign(new Error('Usage: dgxay oauth connect <openai|anthropic|xai|google>'), { userError: true });
|
|
217
217
|
return connect(target, opts);
|
|
218
218
|
}
|
|
219
219
|
if (action === 'disconnect' || action === 'logout') {
|
|
220
|
-
if (!target) throw Object.assign(new Error('
|
|
220
|
+
if (!target) throw Object.assign(new Error('Usage: dgxay oauth disconnect <platform>'), { userError: true });
|
|
221
221
|
return disconnect(target, opts);
|
|
222
222
|
}
|
|
223
223
|
if (action === 'client') {
|
|
224
|
-
if (!target) throw Object.assign(new Error('
|
|
224
|
+
if (!target) throw Object.assign(new Error('Usage: dgxay oauth client <platform> [--client-id ...]'), { userError: true });
|
|
225
225
|
return saveClient(target, opts);
|
|
226
226
|
}
|
|
227
|
-
print.fail(
|
|
228
|
-
print.hint('
|
|
227
|
+
print.fail(`Unknown subcommand: ${action}`);
|
|
228
|
+
print.hint('Available: list / connect / disconnect / client');
|
|
229
229
|
return 1;
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
module.exports = {
|
|
233
233
|
name: 'oauth',
|
|
234
234
|
aliases: ['account'],
|
|
235
|
-
summary: 'OAuth
|
|
235
|
+
summary: 'OAuth account connections',
|
|
236
236
|
usage: 'dgxay oauth <list|connect|disconnect|client>',
|
|
237
237
|
options,
|
|
238
238
|
subcommands: [
|
|
239
|
-
['list', '
|
|
240
|
-
['connect
|
|
241
|
-
['disconnect
|
|
242
|
-
['client
|
|
239
|
+
['list', 'View connection status per platform'],
|
|
240
|
+
['connect <platform>', 'Connect (system browser + PKCE)'],
|
|
241
|
+
['disconnect <platform>', 'Disconnect and delete the local token'],
|
|
242
|
+
['client <platform>', 'View / modify OAuth client config']
|
|
243
243
|
],
|
|
244
244
|
examples: ['dgxay oauth list', 'dgxay oauth connect anthropic', 'dgxay oauth client google --client-id xxx.apps.googleusercontent.com'],
|
|
245
245
|
run
|
|
@@ -25,23 +25,23 @@ function overview() {
|
|
|
25
25
|
const available = settings.availablePermissions(config);
|
|
26
26
|
const reviewer = resolveReviewer(config, config.activeProviderId);
|
|
27
27
|
print.blank();
|
|
28
|
-
print.heading('
|
|
28
|
+
print.heading('Permission');
|
|
29
29
|
for (const [key, meta] of Object.entries(settings.PERMISSIONS)) {
|
|
30
30
|
const usable = available.includes(key);
|
|
31
31
|
const active = current === key;
|
|
32
32
|
print.write(
|
|
33
|
-
` ${active ? COLORS.accent('❯') : ' '} ${COLORS.accent(key.padEnd(8))} ${usable ? meta.label : COLORS.faint(`${meta.label}
|
|
33
|
+
` ${active ? COLORS.accent('❯') : ' '} ${COLORS.accent(key.padEnd(8))} ${usable ? meta.label : COLORS.faint(`${meta.label} (unavailable)`)}`
|
|
34
34
|
);
|
|
35
35
|
print.write(` ${COLORS.faint(meta.desc)}`);
|
|
36
36
|
}
|
|
37
37
|
print.blank();
|
|
38
38
|
if (reviewer) {
|
|
39
|
-
print.hint(
|
|
39
|
+
print.hint(`Review model: ${reviewer.provider.name} / ${reviewer.modelId} (from: ${reviewer.source === 'own' ? 'current provider' : 'default provider'})`);
|
|
40
40
|
} else {
|
|
41
|
-
print.hint('
|
|
41
|
+
print.hint('No review model configured, so auto review is unavailable. Set one with dgxay provider review <provider> --model <model-id>');
|
|
42
42
|
}
|
|
43
43
|
if (config.ui?.permission === 'auto' && !available.includes('auto')) {
|
|
44
|
-
print.warn('
|
|
44
|
+
print.warn('Auto review from config is no longer valid; running with normal permission');
|
|
45
45
|
}
|
|
46
46
|
print.blank();
|
|
47
47
|
print.hint('dgxay permission set <none|normal|auto|full>');
|
|
@@ -58,20 +58,20 @@ async function run({ argv, runtime }) {
|
|
|
58
58
|
|
|
59
59
|
if (action === 'set' || action === 'use') {
|
|
60
60
|
const level = rest[0];
|
|
61
|
-
if (!level) throw Object.assign(new Error('
|
|
61
|
+
if (!level) throw Object.assign(new Error('Usage: dgxay permission set <none|normal|auto|full>'), { userError: true });
|
|
62
62
|
if (!settings.PERMISSIONS[level]) {
|
|
63
|
-
print.fail('
|
|
63
|
+
print.fail('Permission must be one of none / normal / auto / full');
|
|
64
64
|
return 1;
|
|
65
65
|
}
|
|
66
66
|
if (level === 'auto' && !settings.hasReviewModel(config)) {
|
|
67
|
-
print.fail('
|
|
68
|
-
print.hint('
|
|
67
|
+
print.fail('Auto review unavailable: neither the current nor the default provider has a valid review model');
|
|
68
|
+
print.hint('Run first: dgxay provider review <provider> --model <model-id>');
|
|
69
69
|
return 1;
|
|
70
70
|
}
|
|
71
71
|
settings.setPermission(level);
|
|
72
|
-
print.ok(
|
|
72
|
+
print.ok(`Permission set to: ${settings.PERMISSIONS[level].label}`);
|
|
73
73
|
if (level === 'full') {
|
|
74
|
-
print.warn('
|
|
74
|
+
print.warn('With full access the AI can run any command and read or write any file without asking');
|
|
75
75
|
}
|
|
76
76
|
void runtime;
|
|
77
77
|
return 0;
|
|
@@ -80,28 +80,28 @@ async function run({ argv, runtime }) {
|
|
|
80
80
|
if (settings.PERMISSIONS[action]) {
|
|
81
81
|
const level = action;
|
|
82
82
|
if (level === 'auto' && !settings.hasReviewModel(config)) {
|
|
83
|
-
print.fail('
|
|
83
|
+
print.fail('Auto review unavailable: configure a review model first');
|
|
84
84
|
return 1;
|
|
85
85
|
}
|
|
86
86
|
settings.setPermission(level);
|
|
87
|
-
print.ok(
|
|
87
|
+
print.ok(`Permission set to: ${settings.PERMISSIONS[level].label}`);
|
|
88
88
|
return 0;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
print.fail(
|
|
92
|
-
print.hint('
|
|
91
|
+
print.fail(`Unknown usage: ${action}`);
|
|
92
|
+
print.hint('Available: list / set <level>');
|
|
93
93
|
return 1;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
module.exports = {
|
|
97
97
|
name: 'permission',
|
|
98
98
|
aliases: ['perm'],
|
|
99
|
-
summary: '
|
|
100
|
-
usage: 'dgxay permission [list|show|set
|
|
99
|
+
summary: 'Four permission levels: show and switch',
|
|
100
|
+
usage: 'dgxay permission [list|show|set <level>]',
|
|
101
101
|
options,
|
|
102
102
|
subcommands: [
|
|
103
|
-
['list', '
|
|
104
|
-
['set
|
|
103
|
+
['list', 'Show permissions and availability'],
|
|
104
|
+
['set <level>', 'Switch permission (writes config)']
|
|
105
105
|
],
|
|
106
106
|
examples: ['dgxay permission', 'dgxay permission set normal', 'dgxay permission set auto'],
|
|
107
107
|
run
|