chore: merge master into dev/4.11.x

This commit is contained in:
Junyan Qin
2026-08-14 16:04:52 +08:00
121 changed files with 9173 additions and 5846 deletions
@@ -34,12 +34,26 @@ test('normalizes only single-line text fields in a dynamic form save snapshot',
{ name: 'multiline', type: 'text', default: '' },
{ name: 'string-list', type: 'array[string]', default: [] },
{ name: 'count', type: 'integer', default: 0 },
{
name: 'model',
type: 'model-fallback-selector',
default: { primary: '', fallbacks: [], reasoning: {} },
},
];
const values = {
'single-line': '\t hello world \n',
multiline: ' keep multiline whitespace \n',
'string-list': [' first ', ' second '],
count: 3,
model: {
primary: 'primary-model',
fallbacks: ['fallback-model'],
reasoning: {
'primary-model': 'high',
'fallback-model': 'provider_default',
'removed-model': 'medium',
},
},
};
assert.deepEqual(normalizeDynamicFormValuesForSave(specs, values), {
@@ -47,5 +61,12 @@ test('normalizes only single-line text fields in a dynamic form save snapshot',
multiline: ' keep multiline whitespace \n',
'string-list': [' first ', ' second '],
count: 3,
model: {
primary: 'primary-model',
fallbacks: ['fallback-model'],
reasoning: {
'primary-model': 'high',
},
},
});
});
@@ -0,0 +1,47 @@
import assert from 'node:assert/strict';
import { readdirSync, readFileSync } from 'node:fs';
import test from 'node:test';
const localeDir = new URL('../../src/i18n/locales/', import.meta.url);
const localeFiles = readdirSync(localeDir).filter((name) =>
name.endsWith('.ts'),
);
const deprecatedAccountCopy = [
/Initialize with Space/i,
/Login with Space/i,
/Logging in with Space/i,
/Space login/i,
/Space accounts?/i,
/Bind Space Account/i,
/Authorize with Space/i,
/通过 Space 登录/,
/使用 Space 登录/,
/Space 登录/,
/Space 账户/,
/Space 帳戶/,
/绑定 Space/,
/綁定 Space/,
/Space アカウント/,
/Space でログイン/,
/cuenta de Space/i,
/cuentas de Space/i,
/cuenta Space/i,
/tài khoản Space/i,
/บัญชี Space/,
/аккаунт(?:ов|а)? Space/i,
/аккаунт Space/i,
];
test('user-facing account authentication copy uses LangBot Account terminology', () => {
const violations = [];
for (const file of localeFiles) {
const source = readFileSync(new URL(file, localeDir), 'utf8');
for (const pattern of deprecatedAccountCopy) {
if (pattern.test(source)) violations.push(`${file}: ${pattern}`);
}
}
assert.deepEqual(violations, []);
});
@@ -0,0 +1,73 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import test from 'node:test';
const root = process.cwd();
const dialogPath = path.join(
root,
'src/app/home/components/qrcode-login/QrCodeLoginDialog.tsx',
);
const localeDir = path.join(root, 'src/i18n/locales');
const dialogSource = fs.readFileSync(dialogPath, 'utf8');
test('QR credential exchanges preserve the active Workspace scope', () => {
assert.match(dialogSource, /getActiveWorkspaceUuid/);
assert.match(
dialogSource,
/sessionWorkspaceUuidRef\.current = workspaceUuid/,
);
assert.match(
dialogSource,
/const workspaceUuid = sessionWorkspaceUuidRef\.current/,
);
assert.match(dialogSource, /sessionApiBaseRef\.current = cfg\.apiBase/);
assert.match(
dialogSource,
/`\$\{baseUrlRef\.current\}\$\{sessionApiBaseRef\.current\}\/\$\{sessionIdRef\.current\}`/,
);
assert.match(dialogSource, /'X-Workspace-Id': workspaceUuid/);
const workspaceHeaderUses = dialogSource.match(
/'X-Workspace-Id': workspaceUuid/g,
);
assert.equal(
workspaceHeaderUses?.length,
4,
'start, poll, expiry cleanup, and dialog cleanup must all retain Workspace scope',
);
});
test('WeChat QR login never reuses Feishu progress copy', () => {
const weixinConfig = dialogSource.match(
/weixin:\s*\{[\s\S]*?apiBase:\s*'\/api\/v1\/platform\/adapters\/weixin\/login'/,
)?.[0];
assert.ok(weixinConfig, 'WeChat platform config is missing');
assert.match(weixinConfig, /connectingKey:\s*'weixin\.connecting'/);
assert.match(weixinConfig, /waitingKey:\s*'weixin\.waitingForScan'/);
assert.match(weixinConfig, /retryKey:\s*'weixin\.retry'/);
assert.doesNotMatch(weixinConfig, /feishu\./);
for (const locale of [
'en-US.ts',
'es-ES.ts',
'ja-JP.ts',
'ru-RU.ts',
'th-TH.ts',
'vi-VN.ts',
'zh-Hans.ts',
'zh-Hant.ts',
]) {
const source = fs.readFileSync(path.join(localeDir, locale), 'utf8');
const block = source.match(/weixin:\s*\{[\s\S]*?\n\s*\},/)?.[0];
assert.ok(block, `${locale} is missing the WeChat locale block`);
for (const key of ['connecting', 'waitingForScan', 'retry']) {
assert.match(
block,
new RegExp(`\\b${key}:`),
`${locale} is missing weixin.${key}`,
);
}
}
});