Merge remote-tracking branch 'origin/master' into editor-agent-refactored
# Conflicts: # docs/README.md # src/components/image-editor/EditorAgentConversation/EditorAgentConversationPanelView.tsx # src/components/image-editor/EditorAgentConversation/useEditorAgentConversation.ts # src/components/image-editor/EditorAgentConversationPanelView.test.tsx # src/components/image-editor/ImageCanvasStageView.tsx
This commit is contained in:
@@ -1385,47 +1385,7 @@ const desktopCapabilityFlowContracts = [
|
||||
],
|
||||
},
|
||||
];
|
||||
const h5NativeAppRouteFlowContracts = [
|
||||
{
|
||||
route: '/child-motion-demo',
|
||||
label: 'child motion demo',
|
||||
files: [
|
||||
'src/components/platform-entry/PlatformEntryFlowShellImpl.tsx',
|
||||
'src/routing/appRoutes.tsx',
|
||||
'src/routing/appRoutes.test.ts',
|
||||
'src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx',
|
||||
],
|
||||
snippets: [
|
||||
[
|
||||
'src/components/platform-entry/PlatformEntryFlowShellImpl.tsx',
|
||||
"navigateHostNativePage('/child-motion-demo')",
|
||||
],
|
||||
[
|
||||
'src/components/platform-entry/PlatformEntryFlowShellImpl.tsx',
|
||||
"window.location.assign('/child-motion-demo')",
|
||||
],
|
||||
['src/routing/appRoutes.tsx', "normalizedPath === '/child-motion-demo'"],
|
||||
['src/routing/appRoutes.test.ts', "matchAppRoute('/child-motion-demo')"],
|
||||
[
|
||||
'src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx',
|
||||
"request.method === 'navigation.openNativePage'",
|
||||
],
|
||||
[
|
||||
'src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx',
|
||||
"url === '/child-motion-demo'",
|
||||
],
|
||||
],
|
||||
tests: [
|
||||
'src/routing/appRoutes.test.ts',
|
||||
],
|
||||
targetedTests: [
|
||||
{
|
||||
filePath: 'src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx',
|
||||
name: 'native app opens child motion demo through host navigation bridge',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const h5NativeAppRouteFlowContracts = [];
|
||||
const expectedWechatHostBridgeFiles = [
|
||||
'dispatch.js',
|
||||
'payment.js',
|
||||
@@ -1732,8 +1692,6 @@ const h5HostBridgeTests = [
|
||||
'src/services/clipboard.test.ts',
|
||||
'src/services/appTitle.test.ts',
|
||||
];
|
||||
const h5PlatformHostBridgeIntegrationTest =
|
||||
'native app jump hop draft completion sends host notification and badge from platform shell';
|
||||
const h5NativeAppRouteFlowTestSteps = h5NativeAppRouteFlowContracts.flatMap(
|
||||
(contract) =>
|
||||
(contract.targetedTests ?? []).map((test) => ({
|
||||
@@ -1764,18 +1722,6 @@ const steps = [
|
||||
command: npmCommand,
|
||||
args: ['run', 'test', '--', ...h5HostBridgeTests],
|
||||
},
|
||||
{
|
||||
label: 'h5-platform-host-bridge-integration',
|
||||
command: npmCommand,
|
||||
args: [
|
||||
'run',
|
||||
'test',
|
||||
'--',
|
||||
'src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx',
|
||||
'-t',
|
||||
h5PlatformHostBridgeIntegrationTest,
|
||||
],
|
||||
},
|
||||
...h5NativeAppRouteFlowTestSteps,
|
||||
{
|
||||
label: 'wechat-shell-tests',
|
||||
@@ -1807,16 +1753,16 @@ const steps = [
|
||||
command: npmCommand,
|
||||
args: ['run', 'mobile-shell:export'],
|
||||
},
|
||||
{
|
||||
label: 'desktop-shell-typecheck',
|
||||
command: npmCommand,
|
||||
args: ['run', 'desktop-shell:typecheck'],
|
||||
},
|
||||
{
|
||||
label: 'desktop-shell-test',
|
||||
command: npmCommand,
|
||||
args: ['run', 'desktop-shell:test'],
|
||||
},
|
||||
{
|
||||
label: 'desktop-shell-typecheck',
|
||||
command: npmCommand,
|
||||
args: ['run', 'desktop-shell:typecheck'],
|
||||
},
|
||||
{
|
||||
label: 'desktop-shell-release-build-smoke',
|
||||
command: npmCommand,
|
||||
@@ -2439,7 +2385,6 @@ function assertH5HostBridgePayloadBoundaries() {
|
||||
'HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS',
|
||||
'HOST_BRIDGE_USER_INTERACTION_TIMEOUT_MS',
|
||||
'HOST_BRIDGE_SCANNER_TIMEOUT_MS',
|
||||
'HOST_BRIDGE_APP_TITLE_MAX_LENGTH',
|
||||
]) {
|
||||
if (!h5HostBridgeSource.includes(`${sharedBoundary},`)) {
|
||||
throw new Error(
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
import {
|
||||
chmodSync,
|
||||
copyFileSync,
|
||||
existsSync,
|
||||
symlinkSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
symlinkSync,
|
||||
writeFileSync,
|
||||
} from 'node:fs';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import {
|
||||
existsSync,
|
||||
mkdtempSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
symlinkSync,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { createHash, randomBytes } from 'node:crypto';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
import net from 'node:net';
|
||||
import tls from 'node:tls';
|
||||
import { createHash, randomBytes } from 'node:crypto';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import nodePath from 'node:path';
|
||||
import tls from 'node:tls';
|
||||
|
||||
const DEFAULT_TIMEOUT_MS = 5000;
|
||||
const DEFAULT_SPACETIME_DATABASE = 'genarrative-prod';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { accessSync, constants, readFileSync } from 'node:fs';
|
||||
import net from 'node:net';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { userInfo } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import {
|
||||
chmodSync,
|
||||
existsSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
realpathSync,
|
||||
readFileSync,
|
||||
realpathSync,
|
||||
rmSync,
|
||||
statSync,
|
||||
symlinkSync,
|
||||
|
||||
@@ -4,8 +4,8 @@ import { spawnSync } from 'node:child_process';
|
||||
import {
|
||||
chmodSync,
|
||||
existsSync,
|
||||
mkdtempSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
|
||||
@@ -13,11 +13,56 @@ const checks = [
|
||||
includes: 'npm run check:rustfmt',
|
||||
reason: 'API 生产构建必须在编译前执行 Rust 格式检查。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-api-build',
|
||||
includes: 'npm run check:server-rs-ddd',
|
||||
reason: 'API 生产构建必须执行 server-rs DDD/schema/runtime-access 门禁。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-api-build',
|
||||
includes: 'cargo check -p api-server --all-targets --manifest-path server-rs/Cargo.toml',
|
||||
reason: 'API 生产构建必须执行 api-server 全 target 编译检查。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-stdb-module-build',
|
||||
includes: 'npm run check:rustfmt',
|
||||
reason: 'Stdb module 生产构建必须在编译前执行 Rust 格式检查。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-stdb-module-build',
|
||||
includes: 'npm run check:server-rs-ddd',
|
||||
reason: 'Stdb module 生产构建必须执行 DDD/schema/runtime-access 门禁。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-stdb-module-build',
|
||||
includes: 'npm run check:admin-account-procedures',
|
||||
reason: 'Stdb module 生产构建必须执行管理员 procedure smoke。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-web-build',
|
||||
includes: 'npm run check:production-ops',
|
||||
reason: 'Web 生产构建必须执行生产运维静态门禁。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-web-build',
|
||||
includes: 'npm run lint:eslint',
|
||||
reason: 'Web 生产构建必须执行 ESLint 门禁。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-web-build',
|
||||
includes: 'npm run typecheck',
|
||||
reason: 'Web 生产构建必须执行主站类型检查。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-web-build',
|
||||
includes: 'npm run admin-web:typecheck',
|
||||
reason: 'Web 生产构建必须执行后台类型检查。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-web-build',
|
||||
includes: 'npm run test',
|
||||
reason: 'Web 生产构建必须执行当前维护范围内的 Vitest。',
|
||||
},
|
||||
{
|
||||
file: 'server-rs/crates/spacetime-module/src/migration.rs',
|
||||
includes:
|
||||
|
||||
@@ -481,9 +481,9 @@ function getChangedFiles(baseRef) {
|
||||
tryGit(['ls-files', '--others', '--exclude-standard', '-z', bindingsRoot]) ?? '';
|
||||
return new Set(
|
||||
[
|
||||
...diffOutput.split(/\u0000/u),
|
||||
...untrackedModuleOutput.split(/\u0000/u),
|
||||
...untrackedBindingsOutput.split(/\u0000/u),
|
||||
...diffOutput.split(String.fromCharCode(0)),
|
||||
...untrackedModuleOutput.split(String.fromCharCode(0)),
|
||||
...untrackedBindingsOutput.split(String.fromCharCode(0)),
|
||||
]
|
||||
.map(normalizePath)
|
||||
.filter(Boolean),
|
||||
@@ -575,7 +575,7 @@ function compareTables(baseTables, currentTables) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const [accessor, table] of currentTables) {
|
||||
for (const accessor of currentTables.keys()) {
|
||||
if (!baseTables.has(accessor)) {
|
||||
schemaChanged = true;
|
||||
}
|
||||
|
||||
@@ -180,10 +180,6 @@ function hasExternalPlatformMarker(line) {
|
||||
return externalPlatformPatterns.some((pattern) => pattern.test(line));
|
||||
}
|
||||
|
||||
function formatHits(hits) {
|
||||
return hits.map((hit) => ` - ${hit.file}:${hit.lineNumber} ${hit.text}`);
|
||||
}
|
||||
|
||||
const documentFiles = collectFiles(documentTargets);
|
||||
const visualNovelFiles = collectFiles(visualNovelImplementationTargets);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import {
|
||||
chownSync,
|
||||
chmodSync,
|
||||
chownSync,
|
||||
lstatSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import {
|
||||
chownSync,
|
||||
chmodSync,
|
||||
chownSync,
|
||||
lstatSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { execFileSync, spawnSync } from 'node:child_process';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import {
|
||||
accessSync,
|
||||
@@ -15,9 +16,8 @@ import {
|
||||
rmSync,
|
||||
statSync,
|
||||
} from 'node:fs';
|
||||
import { execFileSync, spawnSync } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
import { userInfo } from 'node:os';
|
||||
import path from 'node:path';
|
||||
|
||||
const DEFAULT_SERVICE_USER = 'genarrative';
|
||||
const DEFAULT_SERVICE_GROUP = 'genarrative';
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
import {
|
||||
callSpacetimeProcedureViaCli,
|
||||
ensureProcedureOk,
|
||||
validateSpacetimeDatabaseName,
|
||||
} from '../spacetime-migration-common.mjs';
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
const PROCEDURE = 'rotate_editor_generation_runtime_service_identity_and_return';
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from 'node:fs';
|
||||
import {createServer} from 'node:net';
|
||||
import {hostname, userInfo} from 'node:os';
|
||||
import {dirname, resolve} from 'node:path';
|
||||
import {createServer} from 'node:net';
|
||||
|
||||
const LINUX_DEV_PORT_RANGE_REGISTRY_ROOT = '/var/tmp/genarrative-dev-port-ranges';
|
||||
const LINUX_DEV_PORT_RANGE_POOL_START = 10000;
|
||||
@@ -308,7 +308,7 @@ function nextLinuxPortRangeCandidate(registry) {
|
||||
async function withLinuxPortRangeRegistryLock(lockPath, action) {
|
||||
ensureWorldWritableDirectory(dirname(lockPath));
|
||||
|
||||
while (true) {
|
||||
for (;;) {
|
||||
try {
|
||||
writeFileSync(
|
||||
lockPath,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {createServer} from 'node:net';
|
||||
import {mkdtempSync, readFileSync, rmSync} from 'node:fs';
|
||||
import {createServer} from 'node:net';
|
||||
import {tmpdir} from 'node:os';
|
||||
import {join} from 'node:path';
|
||||
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
import {
|
||||
findAvailablePort,
|
||||
mapDevPortsToPortRange,
|
||||
@@ -23,6 +25,27 @@ function reservePort(port) {
|
||||
});
|
||||
}
|
||||
|
||||
async function reserveConsecutivePorts() {
|
||||
for (let attempt = 0; attempt < 20; attempt += 1) {
|
||||
const firstServer = await reservePort(0);
|
||||
const firstPort = firstServer.address().port;
|
||||
|
||||
if (firstPort === 65535) {
|
||||
await new Promise((resolve) => firstServer.close(resolve));
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const secondServer = await reservePort(firstPort + 1);
|
||||
return {firstServer, secondServer, firstPort};
|
||||
} catch {
|
||||
await new Promise((resolve) => firstServer.close(resolve));
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('无法为测试保留两个连续端口');
|
||||
}
|
||||
|
||||
describe('dev stack port utils', () => {
|
||||
it('解析端口段并映射到四个 dev 端口', () => {
|
||||
expect(parsePortRangeSpec('10000-10099')).toEqual({
|
||||
@@ -39,9 +62,7 @@ describe('dev stack port utils', () => {
|
||||
});
|
||||
|
||||
it('使用端口可用性检查为被占用端口寻找后续可用端口', async () => {
|
||||
const firstServer = await reservePort(0);
|
||||
const firstPort = firstServer.address().port;
|
||||
const secondServer = await reservePort(firstPort + 1);
|
||||
const {firstServer, secondServer, firstPort} = await reserveConsecutivePorts();
|
||||
|
||||
try {
|
||||
const availablePort = await findAvailablePort({
|
||||
|
||||
+2
-1
@@ -921,7 +921,8 @@ function readLinuxApiServerProcessSnapshot(pid) {
|
||||
if (
|
||||
error?.code === 'ENOENT' ||
|
||||
error?.code === 'EACCES' ||
|
||||
error?.code === 'EPERM'
|
||||
error?.code === 'EPERM' ||
|
||||
error?.code === 'ESRCH'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -945,6 +945,7 @@ spacetimedb tool version 2.6.0; spacetimedb-lib version 2.6.0;
|
||||
});
|
||||
|
||||
test('本地 API identity 不跟随符号链接记录', async () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const tempDir = mkdtempSync(join(tmpdir(), 'genarrative-api-identity-'));
|
||||
try {
|
||||
const { explicitOptions, options } = parseArgs(
|
||||
@@ -988,6 +989,9 @@ spacetimedb tool version 2.6.0; spacetimedb-lib version 2.6.0;
|
||||
identity: 'linked-identity',
|
||||
token: 'linked-token',
|
||||
});
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('本地 API identity 记录不可用,将重新创建'),
|
||||
);
|
||||
} finally {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
@@ -1165,6 +1169,7 @@ spacetimedb tool version 2.6.0; spacetimedb-lib version 2.6.0;
|
||||
});
|
||||
|
||||
test('不复用权限宽松、格式非法或空的本地运行服务 bootstrap secret 记录', () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const tempDir = mkdtempSync(
|
||||
join(tmpdir(), 'genarrative-runtime-bootstrap-secret-'),
|
||||
);
|
||||
@@ -1234,6 +1239,11 @@ spacetimedb tool version 2.6.0; spacetimedb-lib version 2.6.0;
|
||||
if (process.platform !== 'win32') {
|
||||
expect(statSync(secretPath).mode & 0o777).toBe(0o600);
|
||||
}
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
'本地运行服务 bootstrap secret 记录不可用,将重新生成',
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Buffer} from 'node:buffer';
|
||||
import {existsSync, mkdirSync, readFileSync, writeFileSync} from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {spawnSync} from 'node:child_process';
|
||||
import { mkdirSync, readFileSync, writeFileSync} from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import {mergeApiServerEnv} from './dev-utils.mjs';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { execFile } from 'node:child_process';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { dirname } from 'node:path';
|
||||
|
||||
const STATUS_RANK = {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { rebindOrphanWorkOwnersInMigration } from './rebind-orphan-work-owners.mjs';
|
||||
|
||||
const placeholderUserId = 'wx-openid-placeholder';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { repairAuthPublicUserCodesInMigration } from './repair-auth-public-user-codes.mjs';
|
||||
|
||||
function table(name: string, rows: unknown[]) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {existsSync} from 'node:fs';
|
||||
import {spawn} from 'node:child_process';
|
||||
import {existsSync} from 'node:fs';
|
||||
|
||||
const [, , scriptPath, ...scriptArgs] = process.argv;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user