feat(merge): 以官方 main 为基底融合 PR#530 多维表格与本地定制

- 多维表格(canvas-batch-table-node/use-canvas-batch-table/canvas-batch-table 等)以 PR#530 实现为主
- 保留官方 main 全部新功能:banner/画布版本历史/提示词编辑/方舟 Agent Plan/支付导出
- 移植本地定制:ddcat 多参考图校验、万有引力 lxmone 连接错误提示、Description v20(v24)、自建插件(a6api/metaso-h3/antigravity-proxy/lxmone)、seccomp、Windows 脚本
- 迁移版本升至 v24(官方 v20-23 banner/版本历史 + 我们的 Description 列)
- 修复官方缺失的 markdownPlainText import
This commit is contained in:
Codex
2026-09-18 14:09:50 +08:00
parent 4eaa28b807
commit 9b9a7f4a72
63 changed files with 9087 additions and 892 deletions

105
scripts/launch-canvas.ps1 Normal file
View File

@@ -0,0 +1,105 @@
# 影策画布一键启动器
# 启动后端(8080) + 前端(3000) + cloudflared 隧道(公网 canvas.yingce.cc.cd),打开浏览器
[CmdletBinding()]
param()
$ErrorActionPreference = "Stop"
$repoRoot = "D:\yingce\open-ai-canvas-main"
$backendDir = Join-Path $repoRoot "backend"
$webDir = Join-Path $repoRoot "web"
$dataDir = Join-Path $repoRoot ".local\project-workbench-debug"
$goBuildCache = Join-Path $repoRoot ".local\cache\go-build"
$goModuleCache = Join-Path $repoRoot ".local\cache\go-mod"
$cloudflaredExe = "C:\Program Files (x86)\cloudflared\cloudflared.exe"
$bunExe = "C:\Users\Administrator\AppData\Roaming\npm\node_modules\bun\bin\bun.exe"
if (-not (Test-Path -LiteralPath $bunExe)) { $bunExe = "bun" }
$localUrl = "http://localhost:3000"
$healthUrl = "http://127.0.0.1:3000/api/health"
function Test-PortListen([int]$Port) {
try {
return $null -ne (Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction Stop | Select-Object -First 1)
} catch {
return $false
}
}
function Test-Http([string]$Url) {
try {
$r = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 3
return $r.StatusCode -ge 200 -and $r.StatusCode -lt 400
} catch {
return $false
}
}
# 0) 已全部就绪则直接打开
if ((Test-PortListen 8080) -and (Test-PortListen 3000)) {
Write-Host "影策画布已在运行,正在打开..." -ForegroundColor Green
Start-Process $localUrl
exit 0
}
# 1) 后端:8080
if (-not (Test-PortListen 8080)) {
Write-Host "启动后端 (8080)..." -ForegroundColor Cyan
New-Item -ItemType Directory -Force -Path $dataDir, $goBuildCache, $goModuleCache | Out-Null
$env:CANVAS_BACKEND_ADDR = "127.0.0.1:8080"
$env:CANVAS_BACKEND_DATA_DIR = $dataDir
$env:CANVAS_PUBLIC_BASE_URL = "https://canvas.yingce.cc.cd"
$env:GOPROXY = "https://goproxy.cn,direct"
$env:GOCACHE = $goBuildCache
$env:GOMODCACHE = $goModuleCache
Start-Process -FilePath "go" -ArgumentList "run", "./cmd/server" `
-WorkingDirectory $backendDir `
-RedirectStandardOutput (Join-Path $repoRoot ".local\backend.stdout.log") `
-RedirectStandardError (Join-Path $repoRoot ".local\backend.stderr.log") `
-WindowStyle Hidden | Out-Null
}
# 2) 前端:3000
if (-not (Test-PortListen 3000)) {
Write-Host "启动前端 (3000)..." -ForegroundColor Cyan
$viteBinary = Join-Path $webDir "node_modules\.bin\vite"
if (-not (Test-Path -LiteralPath $viteBinary)) {
Write-Host "安装前端依赖..." -ForegroundColor Yellow
Push-Location $webDir
try { & bun install --frozen-lockfile } finally { Pop-Location }
}
$env:VITE_API_PROXY_TARGET = "http://127.0.0.1:8080"
Start-Process -FilePath $bunExe -ArgumentList "run", "dev" `
-WorkingDirectory $webDir `
-RedirectStandardOutput (Join-Path $repoRoot ".local\web.stdout.log") `
-RedirectStandardError (Join-Path $repoRoot ".local\web.stderr.log") `
-WindowStyle Hidden | Out-Null
}
# 3) cloudflared 隧道(公网 canvas.yingce.cc.cd)
if (-not (Get-Process -Name cloudflared -ErrorAction SilentlyContinue)) {
if (Test-Path -LiteralPath $cloudflaredExe) {
Write-Host "启动公网隧道 cloudflared..." -ForegroundColor Cyan
Start-Process -FilePath $cloudflaredExe -ArgumentList "--config", "C:\Users\Administrator\.cloudflared\config.yml", "tunnel", "run" -WindowStyle Hidden | Out-Null
} else {
Write-Host "未找到 cloudflared,跳过公网隧道(本地仍可访问)" -ForegroundColor Yellow
}
} else {
Write-Host "公网隧道 cloudflared 已在运行" -ForegroundColor DarkGray
}
# 4) 等待健康检查并打开浏览器
$deadline = (Get-Date).AddMinutes(3)
do {
Start-Sleep -Seconds 2
if (Test-Http $healthUrl) {
Write-Host "影策画布已启动,正在打开浏览器..." -ForegroundColor Green
Start-Sleep -Seconds 1
Start-Process $localUrl
Write-Host "本地: http://localhost:3000" -ForegroundColor Green
Write-Host "公网: https://canvas.yingce.cc.cd" -ForegroundColor Green
exit 0
}
} while ((Get-Date) -lt $deadline)
Write-Host "启动超时,请查看日志:.local\backend.stderr.log / .local\web.stderr.log" -ForegroundColor Red

56
scripts/start-canvas.ps1 Normal file
View File

@@ -0,0 +1,56 @@
[CmdletBinding()]
param()
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$localStarter = Join-Path $PSScriptRoot "start-local.ps1"
$healthUrl = "http://127.0.0.1:3000/api/health"
$canvasUrl = "http://localhost:3000/canvas"
function Test-CanvasHttp([string]$Url) {
try {
$response = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 3
return $response.StatusCode -ge 200 -and $response.StatusCode -lt 400
} catch {
return $false
}
}
function Test-ListeningPort([int]$Port) {
try {
return $null -ne (Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction Stop | Select-Object -First 1)
} catch {
return $false
}
}
if (Test-CanvasHttp $healthUrl) {
Write-Host "影策画布已经在运行,正在打开..." -ForegroundColor Green
Start-Process $canvasUrl
exit 0
}
$occupiedPorts = @(3000, 8080) | Where-Object { Test-ListeningPort $_ }
if ($occupiedPorts.Count -gt 0) {
throw "端口 $($occupiedPorts -join ', ') 已被其他程序占用,无法安全启动影策。"
}
if (-not (Test-Path -LiteralPath $localStarter)) {
throw "未找到项目启动脚本:$localStarter"
}
Write-Host "正在启动影策前端和后端,请稍候..." -ForegroundColor Cyan
& $localStarter
$deadline = (Get-Date).AddMinutes(3)
do {
Start-Sleep -Seconds 2
if (Test-CanvasHttp $healthUrl) {
Write-Host "影策画布已启动,正在打开浏览器..." -ForegroundColor Green
Start-Process $canvasUrl
exit 0
}
} while ((Get-Date) -lt $deadline)
throw "影策服务已拉起,但 3 分钟内未通过健康检查。请查看打开的前端、后端窗口。"

View File

@@ -3,6 +3,9 @@ param()
$ErrorActionPreference = "Stop"
. (Join-Path $PSScriptRoot "windows-proxy.ps1")
Import-CanvasWindowsProxy
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$backendDir = Join-Path $repoRoot "backend"
$webDir = Join-Path $repoRoot "web"

35
scripts/windows-proxy.ps1 Normal file
View File

@@ -0,0 +1,35 @@
# Go uses proxy environment variables, not Windows Internet Settings.
# Inherit the user's enabled static proxy only when no explicit proxy was set.
function Import-CanvasWindowsProxy {
if ($env:HTTPS_PROXY -or $env:HTTP_PROXY -or $env:ALL_PROXY) { return }
$settings = Get-ItemProperty -LiteralPath 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' -ErrorAction SilentlyContinue
if (-not $settings -or $settings.ProxyEnable -ne 1 -or -not $settings.ProxyServer) { return }
$proxies = @{}
foreach ($entry in ($settings.ProxyServer -split ';')) {
if ($entry -match '^\s*(https?)=(.+)$') {
$proxies[$Matches[1]] = $Matches[2].Trim()
} elseif ($entry -notmatch '=') {
$proxies['http'] = $entry.Trim()
$proxies['https'] = $entry.Trim()
}
}
foreach ($scheme in @('http', 'https')) {
$address = $proxies[$scheme]
if (-not $address) { continue }
if ($address -notmatch '^[a-z]+://') { $address = "http://$address" }
$parsed = $null
if (-not [Uri]::TryCreate($address, [UriKind]::Absolute, [ref]$parsed) -or $parsed.Scheme -notin @('http', 'https')) {
throw 'Windows proxy address is invalid. Check system proxy settings.'
}
[Environment]::SetEnvironmentVariable("$($scheme.ToUpper())_PROXY", $address, 'Process')
}
if ($env:HTTP_PROXY -or $env:HTTPS_PROXY) {
$bypass = @($env:NO_PROXY, 'localhost', '127.0.0.1', '::1')
foreach ($entry in ($settings.ProxyOverride -split ';')) {
$entry = $entry.Trim()
if ($entry -and $entry -ne '<local>') { $bypass += $entry }
}
$env:NO_PROXY = ($bypass | Where-Object { $_ } | Select-Object -Unique) -join ','
Write-Host 'Backend network: using the enabled Windows proxy.' -ForegroundColor Cyan
}
}