@coze-arch/cli 0.0.36-alpha.dd2af3 → 0.0.36-alpha.e6ca88

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.
Files changed (83) hide show
  1. package/README.md +65 -114
  2. package/docs/deploy.md +2 -1
  3. package/lib/__templates__/native-static/.coze +2 -0
  4. package/lib/__templates__/native-static/scripts/serve.ps1 +7 -0
  5. package/lib/__templates__/nextjs/.coze +5 -0
  6. package/lib/__templates__/nextjs/scripts/build.ps1 +16 -0
  7. package/lib/__templates__/nextjs/scripts/dev.ps1 +63 -0
  8. package/lib/__templates__/nextjs/scripts/dev.sh +80 -8
  9. package/lib/__templates__/nextjs/scripts/prepare.ps1 +18 -0
  10. package/lib/__templates__/nextjs/scripts/start.ps1 +11 -0
  11. package/lib/__templates__/nextjs/scripts/validate.ps1 +8 -0
  12. package/lib/__templates__/nuxt-vue/.coze +5 -0
  13. package/lib/__templates__/nuxt-vue/scripts/build.ps1 +12 -0
  14. package/lib/__templates__/nuxt-vue/scripts/dev.ps1 +63 -0
  15. package/lib/__templates__/nuxt-vue/scripts/dev.sh +82 -1
  16. package/lib/__templates__/nuxt-vue/scripts/prepare.ps1 +12 -0
  17. package/lib/__templates__/nuxt-vue/scripts/start.ps1 +11 -0
  18. package/lib/__templates__/nuxt-vue/scripts/validate.ps1 +8 -0
  19. package/lib/__templates__/phaser/.coze +5 -0
  20. package/lib/__templates__/phaser/README.md +8 -0
  21. package/lib/__templates__/phaser/scripts/build.ps1 +12 -0
  22. package/lib/__templates__/phaser/scripts/dev.ps1 +49 -0
  23. package/lib/__templates__/phaser/scripts/prepare.ps1 +8 -0
  24. package/lib/__templates__/phaser/scripts/start.ps1 +49 -0
  25. package/lib/__templates__/phaser/scripts/validate.ps1 +7 -0
  26. package/lib/__templates__/phaser/src/global.d.ts +3 -0
  27. package/lib/__templates__/phaser/src/scene/BootScene.ts +4 -0
  28. package/lib/__templates__/phaser/src/scene/DevLoadingScene.ts +3 -0
  29. package/lib/__templates__/phaser/src/utils/mark-editable.ts +8 -1
  30. package/lib/__templates__/phaser-static/.coze +15 -0
  31. package/lib/__templates__/phaser-static/README.md +40 -0
  32. package/lib/__templates__/phaser-static/_gitignore +6 -0
  33. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0001.png +0 -0
  34. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0002.png +0 -0
  35. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0003.png +0 -0
  36. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0004.png +0 -0
  37. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0005.png +0 -0
  38. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0006.png +0 -0
  39. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0007.png +0 -0
  40. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0008.png +0 -0
  41. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0009.png +0 -0
  42. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0010.png +0 -0
  43. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0011.png +0 -0
  44. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0012.png +0 -0
  45. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0013.png +0 -0
  46. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0014.png +0 -0
  47. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0015.png +0 -0
  48. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0016.png +0 -0
  49. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0017.png +0 -0
  50. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0018.png +0 -0
  51. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0019.png +0 -0
  52. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0020.png +0 -0
  53. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0021.png +0 -0
  54. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0022.png +0 -0
  55. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0023.png +0 -0
  56. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0024.png +0 -0
  57. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/manifest.json +11 -0
  58. package/lib/__templates__/phaser-static/debug-runtime/flow-graph.json +20 -0
  59. package/lib/__templates__/phaser-static/index.html +17 -0
  60. package/lib/__templates__/phaser-static/scripts/serve.ps1 +7 -0
  61. package/lib/__templates__/phaser-static/src/assets.js +15 -0
  62. package/lib/__templates__/phaser-static/src/main.js +26 -0
  63. package/lib/__templates__/phaser-static/src/phaser.js +7 -0
  64. package/lib/__templates__/phaser-static/src/scene/BootScene.js +205 -0
  65. package/lib/__templates__/phaser-static/src/scene/DevLoadingScene.js +67 -0
  66. package/lib/__templates__/phaser-static/src/style.css +28 -0
  67. package/lib/__templates__/phaser-static/src/utils/asset-loader.js +267 -0
  68. package/lib/__templates__/phaser-static/src/utils/index.js +2 -0
  69. package/lib/__templates__/phaser-static/src/utils/mark-editable.js +3 -0
  70. package/lib/__templates__/phaser-static/template.config.js +35 -0
  71. package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +53 -22
  72. package/lib/__templates__/templates.json +18 -0
  73. package/lib/__templates__/vite/.coze +5 -0
  74. package/lib/__templates__/vite/scripts/build.ps1 +16 -0
  75. package/lib/__templates__/vite/scripts/dev.ps1 +63 -0
  76. package/lib/__templates__/vite/scripts/dev.sh +82 -1
  77. package/lib/__templates__/vite/scripts/prepare.ps1 +18 -0
  78. package/lib/__templates__/vite/scripts/start.ps1 +11 -0
  79. package/lib/__templates__/vite/scripts/validate.ps1 +8 -0
  80. package/lib/cli.js +186 -155
  81. package/lib/deploy/package-project.js +22 -4
  82. package/lib/deploy/package-project.py +18 -3
  83. package/package.json +1 -1
package/README.md CHANGED
@@ -1,154 +1,105 @@
1
- # @coze-coding/cli
1
+ # @coze-arch/cli
2
2
 
3
- Coze Coding 的项目模板引擎,提供基于前端技术栈的项目初始化能力。
4
-
5
- ## 特性
6
-
7
- - 🚀 **快速初始化**: 一键创建预配置的项目模板
8
- - 🤖 **AI 优先设计**: 专为 AI Agent 消费设计,提供清晰的错误信息和自描述能力
9
- - 📦 **多模板支持**: 支持 React、Next.js、HTML 等多种技术栈
10
- - 🔧 **类型安全**: 基于 JSON Schema 的参数校验和 TypeScript 类型生成
11
- - ⚡ **命令代理**: 统一的 dev/build/start 命令,抹平不同技术栈差异
3
+ Coze Coding 的项目模板与本地运行 CLI。发布包名为 `@coze-arch/cli`,命令为
4
+ `coze-dev`。
12
5
 
13
6
  ## 安装
14
7
 
15
8
  ```bash
16
- rush update
17
-
9
+ npm install -g @coze-arch/cli
10
+ coze-dev --help
18
11
  ```
19
12
 
20
- ## 使用
21
-
22
- ### 部署应用
13
+ 也可以不全局安装:
23
14
 
24
15
  ```bash
25
- coze-dev deploy auth login
26
- coze-dev deploy code deploy --source-root . --yes --wait
16
+ npx @coze-arch/cli --help
27
17
  ```
28
18
 
29
- 完整命令、参数和示例见 [部署命令文档](./docs/deploy.md)。部署命令与其它
30
- `coze-dev` 能力共享 `~/.coze/cli/config.json` 登录态,也支持通过
31
- `COZE_API_TOKEN` 提供固定运行时凭证。
32
-
33
- ### 初始化项目
19
+ ## 初始化项目
34
20
 
35
21
  ```bash
36
- coze-coding init --template react --app-name my-app --use-typescript --use-tailwind
22
+ coze-dev init my-app --template <template> --app-name my-app
23
+ coze-dev init --help
37
24
  ```
38
25
 
39
- ### 查看可用模板
26
+ `init` 默认会渲染模板、安装依赖、初始化 Git、创建初始提交并启动开发服务。按需使用
27
+ `--skip-install`、`--skip-git`、`--skip-commit`、`--skip-dev` 跳过这些步骤。模板名称和
28
+ 参数以 `coze-dev init --help` 的实时输出为准;未声明的参数会以 kebab-case 转为
29
+ camelCase 后传给模板。
40
30
 
41
- ```bash
42
- coze-coding init --help
43
- ```
31
+ ## 在项目中运行
44
32
 
45
- ### 开发命令
33
+ 初始化产物根目录的 `.coze` 是统一命令契约:
46
34
 
47
35
  ```bash
48
- # 启动开发服务器
49
- coze-coding dev
50
-
51
- # 构建生产版本
52
- coze-coding build
53
-
54
- # 启动生产服务器
55
- coze-coding start
36
+ coze-dev dev # 读取 dev.run
37
+ coze-dev build # 读取 deploy.build
38
+ coze-dev start # 读取 deploy.run
39
+ coze-dev validate # 读取 dev.validate
56
40
  ```
57
41
 
58
- ## 模板参数
42
+ `dev` 和 `build` 在执行前会尝试应用模板补丁;补丁失败会记录调试信息,但不会阻止主命令。
43
+ 子进程日志默认写入 `~/.coze-logs/dev.log`,可用 `--log-file <path>` 覆盖。
59
44
 
60
- 每个模板都有自己的参数配置,通过 CLI 参数透传。参数名使用 kebab-case 格式,会自动转换为 camelCase 传入模板配置。
45
+ ## Windows WSL
61
46
 
62
- 例如:
63
- - `--app-name` → `appName`
64
- - `--use-typescript` → `useTypeScript`
65
- - `--port` → `port`
47
+ 原生 Windows(`process.platform === "win32"`)优先使用 `.coze` 中非空的 Windows
48
+ 覆盖命令;未配置时回退默认命令。WSL 被识别为 Linux,始终执行默认命令。
66
49
 
67
- ## 开发模板
50
+ | CLI 命令或场景 | 默认字段 | Windows 覆盖字段 |
51
+ | --- | --- | --- |
52
+ | `coze-dev dev` | `dev.run` | `dev.run_win` |
53
+ | `coze-dev build` | `deploy.build` | `deploy.build_win` |
54
+ | `coze-dev start` | `deploy.run` | `deploy.run_win` |
55
+ | `coze-dev validate` | `dev.validate` | `dev.validate_win` |
56
+ | `coze-dev deploy` 的本地打包 | `dev.pack` | `dev.pack_win` |
68
57
 
69
- ### 模板结构
58
+ 已支持 Windows 的内置 Web 与 Phaser 模板将覆盖命令映射到 PowerShell 脚本,例如:
70
59
 
71
- ```
72
- template-name/
73
- ├── template.config.ts # 模板配置(开发时)
74
- ├── template.config.js # 模板配置(生产时)
75
- ├── .coze # 项目配置文件
76
- └── src/ # 源代码(支持 EJS 模板)
77
- ```
60
+ ```toml
61
+ [dev]
62
+ run = ["bash", "./scripts/dev.sh"]
63
+ run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/dev.ps1"]
78
64
 
79
- ### 参数定义
80
-
81
- 使用 JSON Schema 定义模板参数:
82
-
83
- ```typescript
84
- import type { JSONSchemaType } from 'ajv';
85
-
86
- export interface TemplateParams {
87
- appName: string;
88
- useTypeScript: boolean;
89
- port: number;
90
- }
91
-
92
- export const paramsSchema: JSONSchemaType<TemplateParams> = {
93
- type: 'object',
94
- properties: {
95
- appName: {
96
- type: 'string',
97
- minLength: 1,
98
- pattern: '^[a-z0-9-]+$',
99
- cliParam: '--app-name',
100
- description: 'Application name'
101
- },
102
- useTypeScript: {
103
- type: 'boolean',
104
- default: true,
105
- cliParam: '--use-typescript',
106
- description: 'Enable TypeScript'
107
- },
108
- port: {
109
- type: 'number',
110
- default: 3000,
111
- minimum: 1024,
112
- maximum: 65535,
113
- cliParam: '--port',
114
- description: 'Development server port'
115
- }
116
- },
117
- required: ['appName'],
118
- additionalProperties: false
119
- };
65
+ [deploy]
66
+ build = ["bash", "./scripts/build.sh"]
67
+ build_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/build.ps1"]
120
68
  ```
121
69
 
122
- ### 生命周期钩子
70
+ ## 部署
123
71
 
124
- ```typescript
125
- export const onBeforeRender = (context) => {
126
- // 渲染前处理
127
- return context;
128
- };
72
+ `coze-dev deploy` 内置于同一 CLI,登录态默认保存在 `~/.coze/cli/config.json`:
129
73
 
130
- export const onAfterRender = (context, outputPath) => {
131
- // 渲染后处理
132
- };
74
+ ```bash
75
+ coze-dev deploy auth login
76
+ coze-dev deploy doctor
77
+ coze-dev deploy code deploy --source-root . --yes --wait
133
78
  ```
134
79
 
135
- ## 编码规范
80
+ 完整的应用、环境变量、Pages 和小程序命令说明见 [部署文档](./docs/deploy.md)。
81
+ `dev.pack_win` 是自定义模板可选的 Windows 打包覆盖;当前 Expo、Taro 和 Pi Agent 模板
82
+ 不承诺 Windows 支持。
136
83
 
137
- 本项目严格遵循函数式编程风格:
84
+ ## 模板开发
138
85
 
139
- - 使用纯函数和不可变数据
140
- - ✅ 函数组合和高阶函数
141
- - ✅ 使用工厂函数和闭包
142
- - ❌ 禁止使用 class
143
- - ❌ 避免副作用和可变状态
86
+ 模板位于 `src/__templates__/`,每个模板通过 `template.config.ts` 定义参数和渲染钩子,
87
+ `.coze` 定义生成项目的运行命令。新增模板或修改模板目录后执行:
144
88
 
145
- 详见 [技术设计文档](./docs/technical-design.md#7-编码规范)
89
+ ```bash
90
+ npm run prebuild
91
+ ```
92
+
93
+ 模板开发细节见 [模板开发指南](./docs/how-to-dev.md)。仓库开发依赖由 Rush 管理,不要在本包目录单独执行 `npm install` 或 `pnpm install`。
146
94
 
147
- ## 文档
95
+ ## 本地开发与验证
148
96
 
149
- - [需求文档](./docs/requirement.md)
150
- - [技术设计](./docs/technical-design.md)
97
+ 源码入口支持直接运行,无需先构建:
151
98
 
152
- ## 许可证
99
+ ```bash
100
+ node src/cli.js --help
101
+ npm run test
102
+ npm run build
103
+ ```
153
104
 
154
- 内部项目
105
+ 端到端模板验证使用 `npm run test:e2e`,会真实安装模板依赖并启动服务。发布前需完成该验证。
package/docs/deploy.md CHANGED
@@ -93,7 +93,8 @@ coze-dev deploy code deploy --source-root . --application-type pages --yes --wai
93
93
  `--application-type pages` 自动复用,或显式传 `--app-id`。
94
94
 
95
95
  V2 接口的所有参数均通过 POST JSON body 发送,不会放入 URL。确认非 Pages 部署后,CLI
96
- 会执行 `.coze` 中可选的 `dev.pack` 命令,将
96
+ 会执行 `.coze` 中可选的 `dev.pack` 命令;原生 Windows 优先执行非空的
97
+ `dev.pack_win`,未配置时回退 `dev.pack`。该步骤将
97
98
  本地部署内容打成无根目录的 `tar.gz`,再通过 `source_package_base64` 上传。Pages 部署
98
99
  统一上传源码根目录:已有 `index.html` 时服务端跳过构建,没有时由服务端按 Snapshot 中的
99
100
  `.coze deploy.build` 构建。打包由 CLI 自带的 Node.js 脚本执行,并排除 `.coze`、`.env`、
@@ -6,7 +6,9 @@ project_type = "web"
6
6
  [dev]
7
7
  build = []
8
8
  run = [ "sh", "-c", "python -m http.server ${DEPLOY_RUN_PORT} --bind 0.0.0.0" ]
9
+ run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/serve.ps1"]
9
10
 
10
11
  [deploy]
11
12
  build = []
12
13
  run = [ "sh", "-c", "python -m http.server ${DEPLOY_RUN_PORT} --bind 0.0.0.0" ]
14
+ run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/serve.ps1"]
@@ -0,0 +1,7 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ $port = if ($env:DEPLOY_RUN_PORT) { $env:DEPLOY_RUN_PORT } else { "8000" }
4
+
5
+ Write-Host "Starting static server on port $port..."
6
+ & python -m http.server $port --bind 0.0.0.0
7
+ exit $LASTEXITCODE
@@ -4,11 +4,16 @@ project_type = "web"
4
4
 
5
5
  [dev]
6
6
  build = ["bash", "./scripts/prepare.sh"]
7
+ build_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/prepare.ps1"]
7
8
  run = ["bash", "./scripts/dev.sh"]
9
+ run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/dev.ps1"]
8
10
  validate = ["bash", "./scripts/validate.sh"]
11
+ validate_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/validate.ps1"]
9
12
  deps = ["git"] # -> apt install git
10
13
 
11
14
  [deploy]
12
15
  build = ["bash","./scripts/build.sh"]
16
+ build_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/build.ps1"]
13
17
  run = ["bash","./scripts/start.sh"]
18
+ run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/start.ps1"]
14
19
  deps = ["git"] # -> apt install git
@@ -0,0 +1,16 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ $workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { (Get-Location).Path }
4
+ Set-Location $workspace
5
+
6
+ Write-Host "Installing dependencies..."
7
+ & pnpm install --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
8
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
9
+
10
+ Write-Host "Building the Next.js project..."
11
+ & pnpm next build
12
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
13
+
14
+ Write-Host "Bundling server with tsup..."
15
+ & pnpm tsup src/server.ts --format cjs --platform node --target node20 --outDir dist --no-splitting --no-minify
16
+ exit $LASTEXITCODE
@@ -0,0 +1,63 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ function Get-WorkspacePath {
4
+ if ($env:COZE_WORKSPACE_PATH) {
5
+ return $env:COZE_WORKSPACE_PATH
6
+ }
7
+ return (Get-Location).Path
8
+ }
9
+
10
+ function Stop-ProcessTree([int] $processId) {
11
+ if ($processId -gt 0 -and (Get-Process -Id $processId -ErrorAction SilentlyContinue)) {
12
+ & taskkill.exe /PID $processId /T /F *> $null
13
+ }
14
+ }
15
+
16
+ function Stop-ListeningProcess([int] $port) {
17
+ $connections = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
18
+ $connections | Select-Object -ExpandProperty OwningProcess -Unique | ForEach-Object {
19
+ Stop-ProcessTree $_
20
+ }
21
+ }
22
+
23
+ $workspace = Get-WorkspacePath
24
+ $defaultPort = <%= port %>
25
+ $port = if ($env:DEPLOY_RUN_PORT) { [int] $env:DEPLOY_RUN_PORT } elseif ($env:PORT) { [int] $env:PORT } else { $defaultPort }
26
+ Set-Location $workspace
27
+ Stop-ListeningProcess $port
28
+
29
+ <% if (process.env.NODE_ENV === 'test') { %>
30
+ $env:PORT = "$port"
31
+ & pnpm tsx watch src/server.ts
32
+ exit $LASTEXITCODE
33
+ <% } else { %>
34
+ $logDirectory = if ($env:COZE_LOG_DIR) { $env:COZE_LOG_DIR } else { Join-Path $workspace "logs" }
35
+ $logFile = Join-Path $logDirectory "server.log"
36
+ $errorLogFile = Join-Path $logDirectory "server-error.log"
37
+ $pidFile = Join-Path $logDirectory "server.pid"
38
+ New-Item -ItemType Directory -Force -Path $logDirectory | Out-Null
39
+
40
+ if (Test-Path $pidFile) {
41
+ Stop-ProcessTree ([int] (Get-Content $pidFile -Raw))
42
+ Remove-Item $pidFile -Force
43
+ }
44
+
45
+ $previousPort = $env:PORT
46
+ $env:PORT = "$port"
47
+ try {
48
+ $process = Start-Process -FilePath $env:ComSpec -ArgumentList @("/d", "/s", "/c", "pnpm tsx watch src/server.ts") -WorkingDirectory $workspace -RedirectStandardOutput $logFile -RedirectStandardError $errorLogFile -PassThru
49
+ } finally {
50
+ if ($null -eq $previousPort) { Remove-Item Env:PORT -ErrorAction SilentlyContinue } else { $env:PORT = $previousPort }
51
+ }
52
+
53
+ $process.Id | Set-Content $pidFile
54
+ Start-Sleep -Seconds 1
55
+ if ($process.HasExited) {
56
+ Get-Content $errorLogFile -Tail 20 -ErrorAction SilentlyContinue
57
+ Remove-Item $pidFile -Force -ErrorAction SilentlyContinue
58
+ exit 1
59
+ }
60
+
61
+ Write-Host "Dev server started (PID: $($process.Id))."
62
+ Write-Host "Log file: $logFile"
63
+ <% } %>
@@ -14,9 +14,6 @@ DEPLOY_RUN_PORT="${DEPLOY_RUN_PORT:-${PORT}}"
14
14
 
15
15
  cd "${COZE_WORKSPACE_PATH}"
16
16
 
17
- LOG_DIR="${COZE_LOG_DIR:-${COZE_WORKSPACE_PATH}/logs}"
18
- LOG_SERVER_FILE="${LOG_DIR}/server.log"
19
-
20
17
  kill_port_if_listening() {
21
18
  local pids
22
19
  pids=$(ss -H -lntp 2>/dev/null | awk -v port="${DEPLOY_RUN_PORT}" '$4 ~ ":"port"$"' | grep -o 'pid=[0-9]*' | cut -d= -f2 | paste -sd' ' - || true)
@@ -35,14 +32,89 @@ kill_port_if_listening() {
35
32
  fi
36
33
  }
37
34
 
35
+ <% if (process.env.NODE_ENV === 'test') { %>
36
+ echo "Clearing port ${DEPLOY_RUN_PORT} before start."
37
+ kill_port_if_listening
38
+ echo "Starting HTTP service on port ${DEPLOY_RUN_PORT} for dev..."
39
+
40
+ # 测试环境保持服务为前台进程,便于 e2e 收集输出并停止进程。
41
+ exec env PORT="${DEPLOY_RUN_PORT}" pnpm tsx watch src/server.ts
42
+ <% } else { %>
43
+ LOG_DIR="${COZE_LOG_DIR:-${COZE_WORKSPACE_PATH}/logs}"
44
+ LOG_FILE="${LOG_DIR}/server.log"
45
+ PID_FILE="${LOG_DIR}/server.pid"
46
+
47
+ # coze-daemon 会在 runtime shell 退出时清理原进程组。
48
+ # 通过 Node detached spawn 创建独立 session/进程组,并将 stdio 直接写入日志。
49
+ spawn_detached() {
50
+ local cwd="$1"
51
+ local log_file="$2"
52
+ shift 2
53
+
54
+ node - "$cwd" "$log_file" "$@" <<'NODE'
55
+ const fs = require('node:fs');
56
+ const { spawn } = require('node:child_process');
57
+
58
+ const [cwd, logFile, command, ...args] = process.argv.slice(2);
59
+ if (!cwd || !logFile || !command) {
60
+ throw new Error('spawn_detached 缺少 cwd、log_file 或 command');
61
+ }
62
+
63
+ const logFd = fs.openSync(logFile, 'a');
64
+ try {
65
+ const child = spawn(command, args, {
66
+ cwd,
67
+ detached: true,
68
+ env: process.env,
69
+ stdio: ['ignore', logFd, logFd],
70
+ });
71
+ child.unref();
72
+ process.stdout.write(String(child.pid));
73
+ } finally {
74
+ fs.closeSync(logFd);
75
+ }
76
+ NODE
77
+ }
78
+
79
+ stop_detached() {
80
+ local pid="${1:-}"
81
+ if [[ -z "${pid}" ]]; then
82
+ return
83
+ fi
84
+
85
+ kill -TERM -- "-${pid}" 2>/dev/null || kill -TERM "${pid}" 2>/dev/null || true
86
+ sleep 1
87
+ kill -KILL -- "-${pid}" 2>/dev/null || true
88
+ }
89
+
90
+ # 监听端口的是孙进程(pnpm -> tsx -> node),只清端口会漏掉上层 pnpm/tsx,
91
+ # 所以先按上次记录的 PID 把整个进程组回收掉。
92
+ if [[ -f "${PID_FILE}" ]]; then
93
+ stop_detached "$(cat "${PID_FILE}" 2>/dev/null || true)"
94
+ rm -f "${PID_FILE}"
95
+ fi
96
+
38
97
  echo "Clearing port ${DEPLOY_RUN_PORT} before start."
39
98
  kill_port_if_listening
40
99
  echo "Starting HTTP service on port ${DEPLOY_RUN_PORT} for dev..."
41
100
 
42
101
  mkdir -p "${LOG_DIR}"
43
- : > "${LOG_SERVER_FILE}"
102
+ : > "${LOG_FILE}"
44
103
 
45
- # 保持服务为前台进程,让外层 coze-dev dev 的 PID 在整个服务生命周期内有效。
46
- # 同时通过 tee 将同一份输出写入 logs/server.log。
47
- exec env PORT="${DEPLOY_RUN_PORT}" pnpm tsx watch src/server.ts \
48
- > >(tee -a "${LOG_SERVER_FILE}") 2>&1
104
+ export PORT="${DEPLOY_RUN_PORT}"
105
+ server_pid="$(spawn_detached "${COZE_WORKSPACE_PATH}" "${LOG_FILE}" \
106
+ "$(command -v pnpm)" tsx watch src/server.ts)"
107
+ echo "${server_pid}" > "${PID_FILE}"
108
+
109
+ sleep 1
110
+ if [[ -z "${server_pid}" ]] || ! kill -0 "${server_pid}" 2>/dev/null; then
111
+ echo "Dev server failed to start. See ${LOG_FILE}." >&2
112
+ tail -n 20 "${LOG_FILE}" >&2 || true
113
+ rm -f "${PID_FILE}"
114
+ exit 1
115
+ fi
116
+
117
+ echo "Dev server started (PID: ${server_pid})."
118
+ echo "Log file: ${LOG_FILE}"
119
+ echo "PID file: ${PID_FILE}"
120
+ <% } %>
@@ -0,0 +1,18 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ $workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { (Get-Location).Path }
4
+ Set-Location $workspace
5
+
6
+ Write-Host "Installing dependencies..."
7
+ & pnpm install --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
8
+ if ($LASTEXITCODE -ne 0) {
9
+ exit $LASTEXITCODE
10
+ }
11
+
12
+ if (Get-Command coze-dev -ErrorAction SilentlyContinue) {
13
+ & coze-dev check-bins --help *> $null
14
+ if ($LASTEXITCODE -eq 0) {
15
+ & coze-dev check-bins --fix
16
+ exit $LASTEXITCODE
17
+ }
18
+ }
@@ -0,0 +1,11 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ $workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { (Get-Location).Path }
4
+ $defaultPort = <%= port %>
5
+ $port = if ($env:DEPLOY_RUN_PORT) { $env:DEPLOY_RUN_PORT } elseif ($env:PORT) { $env:PORT } else { $defaultPort }
6
+ Set-Location $workspace
7
+
8
+ Write-Host "Starting HTTP service on port $port for deploy..."
9
+ $env:PORT = "$port"
10
+ & node dist/server.js
11
+ exit $LASTEXITCODE
@@ -0,0 +1,8 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ $workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { (Get-Location).Path }
4
+ Set-Location $workspace
5
+
6
+ Write-Host "Running validate..."
7
+ & pnpm validate
8
+ exit $LASTEXITCODE
@@ -4,11 +4,16 @@ project_type = "web"
4
4
 
5
5
  [dev]
6
6
  build = ["bash", "./scripts/prepare.sh"]
7
+ build_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/prepare.ps1"]
7
8
  run = ["bash", "./scripts/dev.sh"]
9
+ run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/dev.ps1"]
8
10
  validate = ["bash", "./scripts/validate.sh"]
11
+ validate_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/validate.ps1"]
9
12
  deps = ["git"] # -> apt install git
10
13
 
11
14
  [deploy]
12
15
  build = ["bash","./scripts/build.sh"]
16
+ build_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/build.ps1"]
13
17
  run = ["bash","./scripts/start.sh"]
18
+ run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/start.ps1"]
14
19
  deps = ["git"] # -> apt install git
@@ -0,0 +1,12 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ $workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { (Get-Location).Path }
4
+ Set-Location $workspace
5
+
6
+ Write-Host "Installing dependencies..."
7
+ & pnpm install --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
8
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
9
+
10
+ Write-Host "Building the Nuxt.js project..."
11
+ & pnpm nuxt build
12
+ exit $LASTEXITCODE
@@ -0,0 +1,63 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ function Get-WorkspacePath {
4
+ if ($env:COZE_WORKSPACE_PATH) {
5
+ return $env:COZE_WORKSPACE_PATH
6
+ }
7
+ return (Get-Location).Path
8
+ }
9
+
10
+ function Stop-ProcessTree([int] $processId) {
11
+ if ($processId -gt 0 -and (Get-Process -Id $processId -ErrorAction SilentlyContinue)) {
12
+ & taskkill.exe /PID $processId /T /F *> $null
13
+ }
14
+ }
15
+
16
+ function Stop-ListeningProcess([int] $port) {
17
+ $connections = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
18
+ $connections | Select-Object -ExpandProperty OwningProcess -Unique | ForEach-Object {
19
+ Stop-ProcessTree $_
20
+ }
21
+ }
22
+
23
+ $workspace = Get-WorkspacePath
24
+ $defaultPort = <%= port %>
25
+ $port = if ($env:DEPLOY_RUN_PORT) { [int] $env:DEPLOY_RUN_PORT } elseif ($env:PORT) { [int] $env:PORT } else { $defaultPort }
26
+ Set-Location $workspace
27
+ Stop-ListeningProcess $port
28
+
29
+ <% if (process.env.NODE_ENV === 'test') { %>
30
+ $env:PORT = "$port"
31
+ & pnpm nuxt dev
32
+ exit $LASTEXITCODE
33
+ <% } else { %>
34
+ $logDirectory = if ($env:COZE_LOG_DIR) { $env:COZE_LOG_DIR } else { Join-Path $workspace "logs" }
35
+ $logFile = Join-Path $logDirectory "server.log"
36
+ $errorLogFile = Join-Path $logDirectory "server-error.log"
37
+ $pidFile = Join-Path $logDirectory "server.pid"
38
+ New-Item -ItemType Directory -Force -Path $logDirectory | Out-Null
39
+
40
+ if (Test-Path $pidFile) {
41
+ Stop-ProcessTree ([int] (Get-Content $pidFile -Raw))
42
+ Remove-Item $pidFile -Force
43
+ }
44
+
45
+ $previousPort = $env:PORT
46
+ $env:PORT = "$port"
47
+ try {
48
+ $process = Start-Process -FilePath $env:ComSpec -ArgumentList @("/d", "/s", "/c", "pnpm nuxt dev") -WorkingDirectory $workspace -RedirectStandardOutput $logFile -RedirectStandardError $errorLogFile -PassThru
49
+ } finally {
50
+ if ($null -eq $previousPort) { Remove-Item Env:PORT -ErrorAction SilentlyContinue } else { $env:PORT = $previousPort }
51
+ }
52
+
53
+ $process.Id | Set-Content $pidFile
54
+ Start-Sleep -Seconds 1
55
+ if ($process.HasExited) {
56
+ Get-Content $errorLogFile -Tail 20 -ErrorAction SilentlyContinue
57
+ Remove-Item $pidFile -Force -ErrorAction SilentlyContinue
58
+ exit 1
59
+ }
60
+
61
+ Write-Host "Nuxt dev server started (PID: $($process.Id))."
62
+ Write-Host "Log file: $logFile"
63
+ <% } %>