openyida 2026.9.22 → 2026.9.23-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/README.md +8 -1
- package/bin/yida.js +13 -1
- package/lib/auth/oauth-device.js +278 -0
- package/lib/auth/token-auth.js +36 -18
- package/lib/core/command-manifest.js +1 -1
- package/lib/core/locales/en.js +2 -2
- package/lib/core/locales/zh.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,6 +74,13 @@ openyida login
|
|
|
74
74
|
|
|
75
75
|
OpenYida login defaults to OAuth token mode. It opens the DingTalk OAuth authorization page, receives the local loopback callback, exchanges `code` / `authCode` with the Yida server, and stores `access_token` / `refresh_token` in the user auth store when available. If the user auth store is not writable, OpenYida explicitly falls back to the current project cache and reports that narrower persistence scope.
|
|
76
76
|
|
|
77
|
+
In a sandbox, headless shell, or CI dashboard where the loopback callback cannot complete, use the device authorization flow. Open the displayed verification URL on any device and enter the user code. `--env-hint` optionally pins the intended environment profile at authorization time:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
openyida login --device
|
|
81
|
+
openyida login --device --env-hint pre
|
|
82
|
+
```
|
|
83
|
+
|
|
77
84
|
When the user names a target Yida entry URL, pass it to the login command so OpenYida can select the matching environment and auth profile. For example:
|
|
78
85
|
|
|
79
86
|
```bash
|
|
@@ -383,7 +390,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
383
390
|
|
|
384
391
|
| Command | Description |
|
|
385
392
|
|---------|-------------|
|
|
386
|
-
| `openyida login [target-url] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]` | Login with OAuth token mode |
|
|
393
|
+
| `openyida login [target-url] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser] [--device] [--env-hint <name>]` | Login with OAuth token mode |
|
|
387
394
|
| `openyida logout` | Logout / unbind current project auth |
|
|
388
395
|
| `openyida auth <status\|login\|refresh\|logout\|profiles\|profile switch>` | Token login state and profile management |
|
|
389
396
|
| `openyida org <list\|switch> [--json] [--corp-id <corpId>]` | Organization management (list / switch existing profiles first) |
|
package/bin/yida.js
CHANGED
|
@@ -279,6 +279,7 @@ function getFirstPositionalArg(cliArgs, startIndex = 0) {
|
|
|
279
279
|
'--login-url',
|
|
280
280
|
'--profile',
|
|
281
281
|
'--user-id',
|
|
282
|
+
'--env-hint',
|
|
282
283
|
]);
|
|
283
284
|
for (let index = startIndex; index < cliArgs.length; index++) {
|
|
284
285
|
const arg = cliArgs[index];
|
|
@@ -386,6 +387,7 @@ function applyLoginEnvironmentFlags(cliArgs, options = {}) {
|
|
|
386
387
|
'--client-id',
|
|
387
388
|
'--profile',
|
|
388
389
|
'--user-id',
|
|
390
|
+
'--env-hint',
|
|
389
391
|
]);
|
|
390
392
|
const targetUrlFlags = new Set([
|
|
391
393
|
'--endpoint',
|
|
@@ -537,13 +539,23 @@ function printLoginHelp() {
|
|
|
537
539
|
}
|
|
538
540
|
|
|
539
541
|
function buildTokenLoginOptions(loginArgs) {
|
|
542
|
+
const device = loginArgs.includes('--device');
|
|
540
543
|
return {
|
|
541
544
|
clientId: getArgValue(loginArgs, '--client-id'),
|
|
542
545
|
corpId: getArgValue(loginArgs, '--corp-id'),
|
|
543
546
|
userId: getArgValue(loginArgs, '--user-id'),
|
|
544
547
|
authProfile: getArgValue(loginArgs, '--profile'),
|
|
545
548
|
quiet: process.env.YIDA_QUIET === '1' || loginArgs.includes('--quiet'),
|
|
546
|
-
noBrowser: loginArgs.includes('--no-browser'),
|
|
549
|
+
noBrowser: device || loginArgs.includes('--no-browser'),
|
|
550
|
+
device,
|
|
551
|
+
envHint: getArgValue(loginArgs, '--env-hint'),
|
|
552
|
+
// Machine-readable progress events for agent runtimes (sandbox browsers,
|
|
553
|
+
// CI dashboards). Emits one JSON line per state transition.
|
|
554
|
+
onDeviceState: (state) => {
|
|
555
|
+
if (device) {
|
|
556
|
+
process.stdout.write(JSON.stringify({ type: 'device_login_state', ...state }) + '\n');
|
|
557
|
+
}
|
|
558
|
+
},
|
|
547
559
|
};
|
|
548
560
|
}
|
|
549
561
|
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* oauth-device.js - Device Authorization Grant (RFC 8628) flow.
|
|
3
|
+
*
|
|
4
|
+
* Enables headless / sandbox login: the CLI requests a device code from the
|
|
5
|
+
* auth service, the user completes authorization on ANY device (phone,
|
|
6
|
+
* another browser, CI dashboard), and the CLI polls until tokens are issued.
|
|
7
|
+
*
|
|
8
|
+
* The /device/* endpoints use a unified camelCase contract (deviceCode,
|
|
9
|
+
* userCode, verificationUri, grantType, accessToken, ...) matching the
|
|
10
|
+
* tianshu CliAuthRpc device endpoints. Legacy CLI token endpoints
|
|
11
|
+
* (/dingtalk/token, /refresh, /status) keep their snake_case contract and are
|
|
12
|
+
* not handled here.
|
|
13
|
+
*
|
|
14
|
+
* Reuses the same token normalization / profile persistence pipeline as the
|
|
15
|
+
* loopback flow in oauth-loopback.js.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const { requestJson } = require('./token-auth');
|
|
19
|
+
|
|
20
|
+
const DEVICE_CODE_PATH = '/device/code';
|
|
21
|
+
const DEVICE_TOKEN_PATH = '/device/token';
|
|
22
|
+
const DEFAULT_DEVICE_TIMEOUT_MS = 10 * 60 * 1000; // device codes are usually valid 10 minutes
|
|
23
|
+
const DEFAULT_POLL_INTERVAL_MS = 5 * 1000;
|
|
24
|
+
const SLOW_DOWN_EXTRA_DELAY_MS = 5 * 1000;
|
|
25
|
+
|
|
26
|
+
const DEVICE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
|
|
27
|
+
|
|
28
|
+
function createDeviceTimeoutError() {
|
|
29
|
+
const timeout = new Error('device authorization timed out; device code may still be pending');
|
|
30
|
+
timeout.code = 'device_timeout';
|
|
31
|
+
return timeout;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function assertBeforeDeadline(deadline) {
|
|
35
|
+
if (Date.now() >= deadline) {
|
|
36
|
+
throw createDeviceTimeoutError();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function positiveNumber(value, fallback) {
|
|
41
|
+
const parsed = Number(value);
|
|
42
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function errorDescription(payload) {
|
|
46
|
+
return payload.errorDescription || payload.error_description || payload.errorMsg
|
|
47
|
+
|| payload.error_msg || payload.message || payload.raw || '';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Request a device code from the auth service.
|
|
52
|
+
*
|
|
53
|
+
* @param {object} options
|
|
54
|
+
* @param {string} options.authBaseUrl - e.g. https://yida-group.alibaba-inc.com/openapi/cli/v1/auth
|
|
55
|
+
* @param {string} options.clientId
|
|
56
|
+
* @param {string} [options.scope] - defaults handled by caller
|
|
57
|
+
* @param {string} [options.envHint] - environment name to pin the token audience
|
|
58
|
+
* @param {string} [options.timeoutMs] - overall flow timeout
|
|
59
|
+
* @returns {Promise<{deviceCode, userCode, verificationUri,
|
|
60
|
+
* verificationUriComplete, expiresIn, interval}>}
|
|
61
|
+
*/
|
|
62
|
+
async function requestDeviceCode(options = {}) {
|
|
63
|
+
const { authBaseUrl, clientId } = options;
|
|
64
|
+
if (!authBaseUrl || !clientId) {
|
|
65
|
+
throw new Error('device code flow requires authBaseUrl and clientId');
|
|
66
|
+
}
|
|
67
|
+
const body = { clientId };
|
|
68
|
+
if (options.scope) { body.scope = options.scope; }
|
|
69
|
+
if (options.envHint) { body.envHint = options.envHint; }
|
|
70
|
+
|
|
71
|
+
const response = await requestJson(
|
|
72
|
+
'POST',
|
|
73
|
+
`${authBaseUrl}${DEVICE_CODE_PATH}`,
|
|
74
|
+
body
|
|
75
|
+
);
|
|
76
|
+
const payload = unwrapPayload(response);
|
|
77
|
+
if (!payload.deviceCode || !payload.userCode || !payload.verificationUri) {
|
|
78
|
+
const message = payload.message || payload.errorDescription || payload.errorMsg
|
|
79
|
+
|| 'auth service did not return a usable device code';
|
|
80
|
+
const error = new Error(message);
|
|
81
|
+
error.payload = payload;
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
return payload;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Poll the token endpoint until the device flow completes.
|
|
89
|
+
*
|
|
90
|
+
* @param {object} options
|
|
91
|
+
* @param {string} options.authBaseUrl
|
|
92
|
+
* @param {string} options.clientId
|
|
93
|
+
* @param {string} options.deviceCode
|
|
94
|
+
* @param {number} [options.intervalMs] - from /device/code response
|
|
95
|
+
* @param {number} [options.timeoutMs] - overall wall clock budget
|
|
96
|
+
* @param {(state: object) => void} [options.onState] - progress callback for agents
|
|
97
|
+
* @returns {Promise<object>} token payload (accessToken, refreshToken, ...)
|
|
98
|
+
*/
|
|
99
|
+
async function pollDeviceToken(options = {}) {
|
|
100
|
+
const { authBaseUrl, clientId, deviceCode } = options;
|
|
101
|
+
if (!authBaseUrl || !clientId || !deviceCode) {
|
|
102
|
+
throw new Error('device token polling requires authBaseUrl, clientId and deviceCode');
|
|
103
|
+
}
|
|
104
|
+
const intervalMs = Math.max(1, positiveNumber(options.intervalMs, DEFAULT_POLL_INTERVAL_MS));
|
|
105
|
+
const deadline = Date.now() + positiveNumber(options.timeoutMs, DEFAULT_DEVICE_TIMEOUT_MS);
|
|
106
|
+
let currentInterval = intervalMs;
|
|
107
|
+
|
|
108
|
+
// First poll immediately, then honor the interval.
|
|
109
|
+
for (;;) {
|
|
110
|
+
assertBeforeDeadline(deadline);
|
|
111
|
+
let response;
|
|
112
|
+
try {
|
|
113
|
+
response = await requestJson('POST', `${authBaseUrl}${DEVICE_TOKEN_PATH}`, {
|
|
114
|
+
grantType: DEVICE_GRANT_TYPE,
|
|
115
|
+
deviceCode: deviceCode,
|
|
116
|
+
clientId: clientId,
|
|
117
|
+
});
|
|
118
|
+
} catch (error) {
|
|
119
|
+
const payload = error.payload || {};
|
|
120
|
+
const errorCode = payload.error || payload.errorCode || payload.error_msg || payload.errorMsg;
|
|
121
|
+
if (errorCode === 'authorization_pending') {
|
|
122
|
+
await waitFor(deadline, currentInterval, options);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (errorCode === 'slow_down') {
|
|
126
|
+
currentInterval += SLOW_DOWN_EXTRA_DELAY_MS;
|
|
127
|
+
if (options.onState) {
|
|
128
|
+
options.onState({ state: 'slow_down', intervalMs: currentInterval });
|
|
129
|
+
}
|
|
130
|
+
await waitFor(deadline, currentInterval, options);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (errorCode === 'expired_token') {
|
|
134
|
+
const expired = new Error('device code expired; run login --device again');
|
|
135
|
+
expired.code = 'device_code_expired';
|
|
136
|
+
throw expired;
|
|
137
|
+
}
|
|
138
|
+
if (errorCode === 'access_denied') {
|
|
139
|
+
const denied = new Error('authorization was denied on the verification page');
|
|
140
|
+
denied.code = 'device_access_denied';
|
|
141
|
+
throw denied;
|
|
142
|
+
}
|
|
143
|
+
if (errorCode) {
|
|
144
|
+
// RFC 8628 only permits polling to continue for
|
|
145
|
+
// authorization_pending and slow_down. Preserve the server's
|
|
146
|
+
// permanent OAuth error so callers can diagnose it immediately.
|
|
147
|
+
error.code = String(errorCode);
|
|
148
|
+
const description = errorDescription(payload);
|
|
149
|
+
if (description) { error.message = String(description); }
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
if (error.statusCode === 400 || error.statusCode === 429) {
|
|
153
|
+
// Unstructured 400/429 bodies can be produced by gateways. Retry
|
|
154
|
+
// those transient shapes, while the OAuth errors above remain
|
|
155
|
+
// terminal unless the RFC explicitly permits continued polling.
|
|
156
|
+
if (options.onState) {
|
|
157
|
+
options.onState({
|
|
158
|
+
state: 'poll_retry',
|
|
159
|
+
statusCode: error.statusCode,
|
|
160
|
+
body: String(errorDescription(payload)).slice(0, 200),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
await waitFor(deadline, currentInterval, options);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const payload = unwrapPayload(response);
|
|
170
|
+
if (!payload.accessToken && !payload.access_token) {
|
|
171
|
+
// 2xx without a token is unexpected; surface raw payload for diagnosis.
|
|
172
|
+
const invalid = new Error('auth service returned 2xx without accessToken');
|
|
173
|
+
invalid.payload = payload;
|
|
174
|
+
throw invalid;
|
|
175
|
+
}
|
|
176
|
+
return payload;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function waitFor(deadline, intervalMs, options) {
|
|
181
|
+
const remaining = deadline - Date.now();
|
|
182
|
+
if (remaining <= 0) {
|
|
183
|
+
throw createDeviceTimeoutError();
|
|
184
|
+
}
|
|
185
|
+
if (options && options.onState) {
|
|
186
|
+
options.onState({ state: 'pending', nextPollMs: Math.min(intervalMs, remaining) });
|
|
187
|
+
}
|
|
188
|
+
return new Promise((resolve) => setTimeout(resolve, Math.min(intervalMs, remaining)));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function unwrapPayload(response) {
|
|
192
|
+
if (response && typeof response === 'object' && response.content && typeof response.content === 'object') {
|
|
193
|
+
return response.content;
|
|
194
|
+
}
|
|
195
|
+
return response || {};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Run the full device code flow and return a token payload for
|
|
200
|
+
* normalizeTokenResponse(). Designed to be called from tokenLogin().
|
|
201
|
+
*
|
|
202
|
+
* @param {object} options - same option bag as tokenLogin plus:
|
|
203
|
+
* options.authBaseUrl resolved auth base (with /openapi/cli/v1/auth prefix)
|
|
204
|
+
* options.envHint environment name to pin audience
|
|
205
|
+
* options.quiet suppress human-oriented stderr
|
|
206
|
+
* options.onState optional machine-readable progress callback
|
|
207
|
+
*/
|
|
208
|
+
async function runDeviceCodeFlow(options = {}) {
|
|
209
|
+
const code = await requestDeviceCode(options);
|
|
210
|
+
|
|
211
|
+
// The server may return verification URIs as absolute paths (e.g.
|
|
212
|
+
// /openapi/cli/v1/auth/device/verify); resolve them against the business
|
|
213
|
+
// base URL so users and agent hosts get clickable links.
|
|
214
|
+
const resolveUrl = (value) => {
|
|
215
|
+
if (!value || /^https?:\/\//i.test(value)) {
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
const origin = new URL(options.baseUrl || options.authBaseUrl).origin;
|
|
220
|
+
return new URL(value, origin).toString();
|
|
221
|
+
} catch {
|
|
222
|
+
return value;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const verificationUri = resolveUrl(code.verificationUri);
|
|
226
|
+
const verificationUriComplete = resolveUrl(code.verificationUriComplete);
|
|
227
|
+
|
|
228
|
+
const intro = [
|
|
229
|
+
'',
|
|
230
|
+
'Device code login:',
|
|
231
|
+
` 1. Open ${verificationUri}`,
|
|
232
|
+
` 2. Enter code: ${code.userCode}`,
|
|
233
|
+
'',
|
|
234
|
+
];
|
|
235
|
+
if (verificationUriComplete) {
|
|
236
|
+
intro.push(` Or open directly: ${verificationUriComplete}`, '');
|
|
237
|
+
}
|
|
238
|
+
if (!options.quiet) {
|
|
239
|
+
process.stderr.write(intro.join('\n') + '\n');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (options.onState) {
|
|
243
|
+
options.onState({
|
|
244
|
+
state: 'awaiting_verification',
|
|
245
|
+
verification_uri: verificationUri,
|
|
246
|
+
verification_uri_complete: verificationUriComplete,
|
|
247
|
+
user_code: code.userCode,
|
|
248
|
+
expires_in: code.expiresIn,
|
|
249
|
+
interval: code.interval,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const tokenPayload = await pollDeviceToken({
|
|
254
|
+
authBaseUrl: options.authBaseUrl,
|
|
255
|
+
clientId: options.clientId,
|
|
256
|
+
deviceCode: code.deviceCode,
|
|
257
|
+
intervalMs: (code.interval || 5) * 1000,
|
|
258
|
+
timeoutMs: Math.min(
|
|
259
|
+
positiveNumber(code.expiresIn, 600) * 1000,
|
|
260
|
+
positiveNumber(options.timeoutMs, Number.POSITIVE_INFINITY)
|
|
261
|
+
),
|
|
262
|
+
onState: options.onState,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (!options.quiet) {
|
|
266
|
+
process.stderr.write('Device authorization completed.\n');
|
|
267
|
+
}
|
|
268
|
+
return tokenPayload;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
module.exports = {
|
|
272
|
+
runDeviceCodeFlow,
|
|
273
|
+
requestDeviceCode,
|
|
274
|
+
pollDeviceToken,
|
|
275
|
+
DEVICE_CODE_PATH,
|
|
276
|
+
DEVICE_TOKEN_PATH,
|
|
277
|
+
DEVICE_GRANT_TYPE,
|
|
278
|
+
};
|
package/lib/auth/token-auth.js
CHANGED
|
@@ -302,25 +302,43 @@ async function tokenLogin(options = {}) {
|
|
|
302
302
|
const baseUrl = resolveTokenBaseUrl(options);
|
|
303
303
|
const authBaseUrl = appendPath(baseUrl, DEFAULT_AUTH_PATH_PREFIX);
|
|
304
304
|
const clientId = options.clientId || process.env.OPENYIDA_DINGTALK_CLIENT_ID || DINGTALK_OAUTH_CLIENT_ID;
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
305
|
+
const scope = options.scope || process.env.OPENYIDA_DINGTALK_SCOPE || 'openid corpid';
|
|
306
|
+
|
|
307
|
+
let response;
|
|
308
|
+
if (options.device) {
|
|
309
|
+
// Device Authorization Grant (RFC 8628): sandbox / headless login.
|
|
310
|
+
const { runDeviceCodeFlow } = require('./oauth-device');
|
|
311
|
+
response = await runDeviceCodeFlow({
|
|
312
|
+
authBaseUrl,
|
|
313
|
+
clientId,
|
|
314
|
+
scope,
|
|
315
|
+
envHint: options.envHint,
|
|
316
|
+
quiet: options.quiet,
|
|
317
|
+
onState: options.onDeviceState,
|
|
318
|
+
timeoutMs: options.timeoutMs || env.OPENYIDA_OAUTH_TIMEOUT_MS,
|
|
319
|
+
});
|
|
320
|
+
requireOkResponse(response);
|
|
321
|
+
} else {
|
|
322
|
+
const callback = await runDingtalkLoopback({
|
|
323
|
+
clientId,
|
|
324
|
+
loginOrigin: resolveDingtalkLoginOrigin(options),
|
|
325
|
+
scope,
|
|
326
|
+
prompt: options.prompt,
|
|
327
|
+
port: options.port,
|
|
328
|
+
quiet: options.quiet,
|
|
329
|
+
noBrowser: options.noBrowser,
|
|
330
|
+
timeoutMs: options.timeoutMs,
|
|
331
|
+
});
|
|
315
332
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
333
|
+
response = await requestJson('POST', appendPath(authBaseUrl, '/dingtalk/token'), {
|
|
334
|
+
code: callback.code,
|
|
335
|
+
authCode: callback.authCode,
|
|
336
|
+
redirectUri: callback.redirectUri,
|
|
337
|
+
state: callback.state,
|
|
338
|
+
clientId,
|
|
339
|
+
});
|
|
340
|
+
requireOkResponse(response);
|
|
341
|
+
}
|
|
324
342
|
|
|
325
343
|
const normalized = normalizeTokenResponse(response, baseUrl, clientId);
|
|
326
344
|
if (!normalized.access_token) {
|
|
@@ -1034,7 +1034,7 @@ const COMMAND_GROUPS = [
|
|
|
1034
1034
|
id: 'auth',
|
|
1035
1035
|
titleKey: 'help.group_auth',
|
|
1036
1036
|
commands: [
|
|
1037
|
-
command('login', ['login'], 'login [target-url] [--env <name>|--intl|--overseas|--global|--yidaapps|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]', 'help.cmd_login', {
|
|
1037
|
+
command('login', ['login'], 'login [target-url] [--env <name>|--intl|--overseas|--global|--yidaapps|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser] [--device] [--env-hint <name>]', 'help.cmd_login', {
|
|
1038
1038
|
requiresLogin: false,
|
|
1039
1039
|
output: 'json',
|
|
1040
1040
|
}),
|
package/lib/core/locales/en.js
CHANGED
|
@@ -373,8 +373,8 @@ Examples:
|
|
|
373
373
|
save_permission_usage: 'Usage: openyida save-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--data-permission <json>] [--action-permission <json>]',
|
|
374
374
|
save_permission_example: "Example: openyida save-permission APP_XXX FORM-XXX --data-permission '{\"role\":\"DEFAULT\",\"dataRange\":\"SELF\"}'",
|
|
375
375
|
exec_failed: '\n❌ Execution failed: {0}',
|
|
376
|
-
login_usage: 'Usage: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
|
|
377
|
-
login_example: 'Examples:\n openyida login # Automatically open the browser via OAuth loopback login\n openyida login --no-browser # Let the caller handle the authorization URL\n openyida login --check-only --json # Check token auth status only\n openyida login --intl # Login against the international environment\n OPENYIDA_NO_BROWSER=1 openyida login # Suppress auto-opening the browser with an environment variable\n openyida auth login # Login alias',
|
|
376
|
+
login_usage: 'Usage: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--device] [--env-hint <name>] [--check-only] [--json] [--client-id <clientId>]',
|
|
377
|
+
login_example: 'Examples:\n openyida login # Automatically open the browser via OAuth loopback login\n openyida login --device # Use device authorization in a headless environment\n openyida login --device --env-hint pre # Pin device authorization to an environment profile\n openyida login --no-browser # Let the caller handle the authorization URL\n openyida login --check-only --json # Check token auth status only\n openyida login --intl # Login against the international environment\n OPENYIDA_NO_BROWSER=1 openyida login # Suppress auto-opening the browser with an environment variable\n openyida auth login # Login alias',
|
|
378
378
|
login_unsupported_option: 'Removed legacy login option is no longer supported: {0}. Use token/OAuth login; env token mode reads only OPENYIDA_* tokens.',
|
|
379
379
|
auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch>',
|
|
380
380
|
auth_example: 'Examples:\n openyida auth status # View login status\n openyida auth profiles # List existing login profiles\n openyida auth profile switch <auth_profile> # Switch current project to an existing profile\n openyida auth login # Add a profile when the target does not exist\n openyida auth refresh # Refresh login session\n openyida auth logout # Unbind current project auth\n openyida auth logout --profile <auth_profile> # Delete a shared profile explicitly',
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -373,8 +373,8 @@ openyida - 宜搭命令行工具
|
|
|
373
373
|
save_permission_usage: '用法: openyida save-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--data-permission <json>] [--action-permission <json>]',
|
|
374
374
|
save_permission_example: "示例: openyida save-permission APP_XXX FORM-XXX --data-permission '{\"role\":\"DEFAULT\",\"dataRange\":\"SELF\"}'",
|
|
375
375
|
exec_failed: '\n❌ 执行失败: {0}',
|
|
376
|
-
login_usage: '用法: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
|
|
377
|
-
login_example: '示例:\n openyida login # 通过 OAuth loopback 自动打开浏览器登录\n openyida login --no-browser # 不自动打开浏览器,由调用方接管授权链接\n openyida login --check-only --json # 只检查 token 登录态\n openyida login --intl # 使用国际站环境登录\n OPENYIDA_NO_BROWSER=1 openyida login # 通过环境变量抑制自动打开浏览器\n openyida auth login # 登录入口别名',
|
|
376
|
+
login_usage: '用法: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--device] [--env-hint <name>] [--check-only] [--json] [--client-id <clientId>]',
|
|
377
|
+
login_example: '示例:\n openyida login # 通过 OAuth loopback 自动打开浏览器登录\n openyida login --device # 在无浏览器或沙箱环境使用设备授权登录\n openyida login --device --env-hint pre # 将设备授权限定到指定环境 profile\n openyida login --no-browser # 不自动打开浏览器,由调用方接管授权链接\n openyida login --check-only --json # 只检查 token 登录态\n openyida login --intl # 使用国际站环境登录\n OPENYIDA_NO_BROWSER=1 openyida login # 通过环境变量抑制自动打开浏览器\n openyida auth login # 登录入口别名',
|
|
378
378
|
login_unsupported_option: '已删除的旧登录参数不再支持: {0}。请使用 token/OAuth 登录;env token 模式下只读取 OPENYIDA_* token。',
|
|
379
379
|
first_run_title: ' 🤖 OpenYida - AI 问答模式已开启! ',
|
|
380
380
|
first_run_welcome: ' {0}欢迎首次使用 OpenYida!{1} 以下是快速上手指南:',
|