openyida 2026.8.3 → 2026.8.4

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 CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  OpenYida connects AI coding agents with Yida's low-code platform, so developers can create apps, forms, workflows, custom pages, reports, integrations, and deployment configuration from a normal chat-driven development workflow.
10
10
 
11
- [Quick Start](#quick-start) · [Capabilities](#capabilities) · [Full Capability List](./docs/capabilities.md) · [CLI Reference](#cli-reference) · [Examples](#examples) · [Contributing](./CONTRIBUTING.md) · [Changelog](./CHANGELOG.md)
11
+ [Quick Start](#quick-start) · [Help Center](https://xmtrf1.aliwork.com/o/openyida/help) · [Capabilities](#capabilities) · [Full Capability List](./docs/capabilities.md) · [CLI Reference](#cli-reference) · [Examples](#examples) · [Contributing](./CONTRIBUTING.md) · [Changelog](./CHANGELOG.md)
12
12
 
13
13
  [![npm version](https://img.shields.io/npm/v/openyida?color=brightgreen&label=npm)](https://www.npmjs.com/package/openyida)
14
14
  [![npm downloads](https://img.shields.io/npm/dm/openyida?color=blue)](https://www.npmjs.com/package/openyida)
@@ -16,7 +16,7 @@ OpenYida connects AI coding agents with Yida's low-code platform, so developers
16
16
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
17
17
  [![Node.js >=18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
18
18
 
19
- **Documentation:** [English](https://xmtrf1.aliwork.com/o/openyida/help) · [简体中文](https://xmtrf1.aliwork.com/o/openyida/help) · [繁體中文](https://xmtrf1.aliwork.com/o/openyida/help) · [日本語](https://xmtrf1.aliwork.com/o/openyida/help) · [한국어](https://xmtrf1.aliwork.com/o/openyida/help) · [Français](https://xmtrf1.aliwork.com/o/openyida/help) · [Deutsch](https://xmtrf1.aliwork.com/o/openyida/help) · [Español](https://xmtrf1.aliwork.com/o/openyida/help) · [Português](https://xmtrf1.aliwork.com/o/openyida/help) · [Tiếng Việt](https://xmtrf1.aliwork.com/o/openyida/help) · [हिन्दी](https://xmtrf1.aliwork.com/o/openyida/help) · [العربية](https://xmtrf1.aliwork.com/o/openyida/help)
19
+ **Help Center:** [English](https://xmtrf1.aliwork.com/o/openyida/help) · [简体中文](https://xmtrf1.aliwork.com/o/openyida/help) · [繁體中文](https://xmtrf1.aliwork.com/o/openyida/help) · [日本語](https://xmtrf1.aliwork.com/o/openyida/help) · [한국어](https://xmtrf1.aliwork.com/o/openyida/help) · [Français](https://xmtrf1.aliwork.com/o/openyida/help) · [Deutsch](https://xmtrf1.aliwork.com/o/openyida/help) · [Español](https://xmtrf1.aliwork.com/o/openyida/help) · [Português](https://xmtrf1.aliwork.com/o/openyida/help) · [Tiếng Việt](https://xmtrf1.aliwork.com/o/openyida/help) · [हिन्दी](https://xmtrf1.aliwork.com/o/openyida/help) · [العربية](https://xmtrf1.aliwork.com/o/openyida/help)
20
20
 
21
21
  [English README](./README.md) · [简体中文 README](./README_zhCN.md)
22
22
 
package/bin/yida.js CHANGED
@@ -35,11 +35,15 @@ function isAgentEnvironment(env) {
35
35
  env.QODER_IDE ||
36
36
  env.QODER_AGENT ||
37
37
  env.QODERCLI_INTEGRATION_MODE ||
38
+ env.QWENWORK_INTEGRATION_MODE ||
39
+ env.QWENWORKCN_INTEGRATION_MODE ||
38
40
  env.CURSOR_TRACE_ID ||
39
41
  env.AGENT_WORK_ROOT ||
40
42
  env.OPENYIDA_AGENT_MODE ||
41
43
  (env.__CFBundleIdentifier || '').toLowerCase().includes('codex') ||
42
- (env.__CFBundleIdentifier || '').toLowerCase().includes('qoder')
44
+ (env.__CFBundleIdentifier || '').toLowerCase().includes('qoder') ||
45
+ (env.__CFBundleIdentifier || '').toLowerCase().includes('qwenwork') ||
46
+ (env.__CFBundleIdentifier || '').toLowerCase().includes('qwen-work')
43
47
  );
44
48
  }
45
49
 
@@ -287,55 +287,128 @@ function resolveBrowserLauncher(url, platform = process.platform, openMode = 'le
287
287
  return { command: 'xdg-open', args: [url] };
288
288
  }
289
289
 
290
- function launchBrowserLauncher(launcher, onError) {
290
+ function isQwenWorkEnvironment(env = process.env) {
291
+ const bundleId = String(env.__CFBundleIdentifier || '').toLowerCase();
292
+ const product = String(env.QODER_WORK_INTEGRATION_PRODUCT || '').toLowerCase();
293
+ const qoderConfigDir = String(env.QODER_CONFIG_DIR || env.QODERCN_CONFIG_DIR || '');
294
+ return !!(
295
+ env.QWENWORK_INTEGRATION_MODE ||
296
+ env.QWENWORKCN_INTEGRATION_MODE ||
297
+ product.includes('qwenwork') ||
298
+ product.includes('qwen-work') ||
299
+ qoderConfigDir.includes('.qwenworkcn') ||
300
+ bundleId.includes('qwenwork') ||
301
+ bundleId.includes('qwen-work')
302
+ );
303
+ }
304
+
305
+ function browserLaunchFailureMessage(launcher, error) {
306
+ const detail = error && error.message ? ` (${error.message})` : '';
307
+ return `OpenYida could not auto-open the browser via ${launcher.command}${detail}. Please use the login URL above.`;
308
+ }
309
+
310
+ function launchBrowserLauncher(launcher, onError, deps = {}) {
311
+ const spawnImpl = deps.spawn || spawn;
312
+ let handledFailure = false;
313
+ const notifyFailure = (error) => {
314
+ if (handledFailure) {
315
+ return false;
316
+ }
317
+ handledFailure = true;
318
+ if (onError) {
319
+ return onError(error) === true;
320
+ }
321
+ return false;
322
+ };
323
+
291
324
  try {
292
- const child = spawn(launcher.command, launcher.args, {
325
+ const child = spawnImpl(launcher.command, launcher.args, {
293
326
  detached: true,
294
327
  stdio: 'ignore',
295
328
  });
296
- child.unref();
297
- if (onError) {
298
- child.once('error', onError);
329
+ if (child && typeof child.unref === 'function') {
330
+ child.unref();
299
331
  }
300
- return true;
301
- } catch {
302
332
  if (onError) {
303
- onError();
333
+ child.once('error', notifyFailure);
334
+ child.once('exit', (code, signal) => {
335
+ if (code && code !== 0) {
336
+ notifyFailure(new Error(`browser launcher exited with code ${code}`));
337
+ } else if (signal) {
338
+ notifyFailure(new Error(`browser launcher exited with signal ${signal}`));
339
+ }
340
+ });
304
341
  }
305
- return false;
342
+ return true;
343
+ } catch (error) {
344
+ return notifyFailure(error);
306
345
  }
307
346
  }
308
347
 
309
- function openBrowser(url) {
310
- if (process.env.OPENYIDA_NO_BROWSER === '1') {
348
+ function openBrowser(url, options = {}) {
349
+ const env = options.env || process.env;
350
+ if (env.OPENYIDA_NO_BROWSER === '1') {
311
351
  return false;
312
352
  }
313
353
 
314
- const platform = process.platform;
354
+ const platform = options.platform || process.platform;
355
+ const detectBrowser = options.detectDefaultBrowser || detectDefaultBrowser;
356
+ const launchDeps = { spawn: options.spawn };
357
+ const warnLaunchFailure = (launcher) => (error) => {
358
+ process.stderr.write(`${browserLaunchFailureMessage(launcher, error)}\n`);
359
+ };
315
360
  const legacyLauncher = resolveBrowserLauncher(url, platform, 'legacy');
316
- const runLegacy = () => launchBrowserLauncher(legacyLauncher);
361
+ const runLegacy = (onError = warnLaunchFailure(legacyLauncher)) =>
362
+ launchBrowserLauncher(legacyLauncher, onError, launchDeps);
317
363
 
318
364
  let browser = null;
319
365
  try {
320
- browser = detectDefaultBrowser(platform);
366
+ browser = detectBrowser(platform);
321
367
  } catch {
322
368
  browser = null;
323
369
  }
324
370
 
371
+ const hasNewWindowFallback = browser && SUPPORTED_BROWSER_FAMILIES.has(browser.family);
372
+ const newWindowLauncher = hasNewWindowFallback
373
+ ? resolveBrowserLauncher(url, platform, 'newWindow', browser)
374
+ : null;
375
+
376
+ // QwenWork runs inside a desktop shell. Prefer the OS default-browser opener
377
+ // first so the login page is visible outside the host app; use new-window
378
+ // launch only as a fallback when the system opener itself fails.
379
+ if (isQwenWorkEnvironment(env)) {
380
+ let fallbackUsed = false;
381
+ const runNewWindowFallback = (error) => {
382
+ if (fallbackUsed) {
383
+ return false;
384
+ }
385
+ fallbackUsed = true;
386
+ if (newWindowLauncher) {
387
+ return launchBrowserLauncher(
388
+ newWindowLauncher,
389
+ warnLaunchFailure(newWindowLauncher),
390
+ launchDeps
391
+ );
392
+ }
393
+ warnLaunchFailure(legacyLauncher)(error);
394
+ return false;
395
+ };
396
+ return runLegacy(runNewWindowFallback);
397
+ }
398
+
325
399
  // Default browser detected and supported → open a real new window in that
326
400
  // browser, falling back to the plain default-browser open if launch errors.
327
- if (browser && SUPPORTED_BROWSER_FAMILIES.has(browser.family)) {
328
- const newWindowLauncher = resolveBrowserLauncher(url, platform, 'newWindow', browser);
401
+ if (newWindowLauncher) {
329
402
  let fallbackUsed = false;
330
- const runFallback = () => {
403
+ const runFallback = (error) => {
331
404
  if (fallbackUsed) {
332
405
  return false;
333
406
  }
334
407
  fallbackUsed = true;
335
- return runLegacy();
408
+ return runLegacy(warnLaunchFailure(legacyLauncher)) || warnLaunchFailure(newWindowLauncher)(error);
336
409
  };
337
410
 
338
- const launched = launchBrowserLauncher(newWindowLauncher, runFallback);
411
+ const launched = launchBrowserLauncher(newWindowLauncher, runFallback, launchDeps);
339
412
  if (!launched) {
340
413
  return runFallback();
341
414
  }
@@ -506,6 +579,9 @@ module.exports = {
506
579
  buildDingtalkOAuthUrl,
507
580
  resolveBrowserLauncher,
508
581
  detectDefaultBrowser,
582
+ openBrowser,
583
+ isQwenWorkEnvironment,
584
+ launchBrowserLauncher,
509
585
  classifyMacBundleId,
510
586
  classifyWindowsProgId,
511
587
  classifyLinuxDesktop,
@@ -557,7 +557,7 @@ function sanitizeQrLoginStart(result = {}, loginSessionId) {
557
557
  qrImageOpened: !!result.qr_image_opened,
558
558
  pollable: true,
559
559
  pollIntervalMs: 1000,
560
- message: result.message || 'Scan the QR code with DingTalk, then poll login status.',
560
+ message: result.message || 'Complete login in the browser, then poll login status.',
561
561
  };
562
562
  }
563
563
 
@@ -623,7 +623,7 @@ async function defaultStartLogin() {
623
623
  status: 'token_login_required',
624
624
  auth_mode: 'token',
625
625
  can_auto_use: false,
626
- message: 'Token-only auth does not support bridge QR login. Run openyida login first.',
626
+ message: 'Token-only auth does not support bridge-managed login. Run openyida login first.',
627
627
  };
628
628
  }
629
629
 
@@ -632,7 +632,7 @@ async function defaultPollLogin() {
632
632
  status: 'token_login_required',
633
633
  auth_mode: 'token',
634
634
  can_auto_use: false,
635
- message: 'Token-only auth does not support bridge QR polling. Run openyida login first.',
635
+ message: 'Token-only auth does not support bridge login polling. Run openyida login first.',
636
636
  };
637
637
  }
638
638
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "2026.8.3",
3
+ "version": "2026.8.4",
4
4
  "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
6
  "openyida": "bin/yida.js",