@coze-arch/cli 0.1.8-alpha.a87968 → 0.1.8

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.
@@ -98,7 +98,6 @@ printf '%s\n' "$$" > "$LOCK_DIR/pid"
98
98
  trap release_lock EXIT
99
99
 
100
100
  mkdir -p "$WORK_DIR"
101
- INSTALL_STATE_FILE="$WORK_DIR/.install-state"
102
101
 
103
102
  # Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
104
103
  # and framework dev servers two paths for the same source tree, which breaks
@@ -119,15 +118,12 @@ cleanup_legacy_workspace() {
119
118
  done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
120
119
  }
121
120
 
122
- # caches is reserved for the sibling prepare scripts of this project. They keep
123
- # regenerable framework output there (the Next helper points .next/dev into it), and that
124
- # output has to outlive an install, so this cleanup must not read it as leftover.
125
121
  cleanup_legacy_shadow_project() {
126
122
  local entry name
127
123
  while IFS= read -r -d '' entry; do
128
124
  name="$(basename "$entry")"
129
125
  case "$name" in
130
- node_modules|client|server|caches|.install-state) continue ;;
126
+ node_modules|client|server) continue ;;
131
127
  esac
132
128
  rm -rf "$entry"
133
129
  done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
@@ -145,162 +141,6 @@ link_install_entry() {
145
141
  fi
146
142
  }
147
143
 
148
- hash_install_input() {
149
- if command -v sha256sum >/dev/null 2>&1; then
150
- sha256sum | awk '{print $1}'
151
- elif command -v shasum >/dev/null 2>&1; then
152
- shasum -a 256 | awk '{print $1}'
153
- else
154
- cksum | awk '{print $1 "-" $2}'
155
- fi
156
- }
157
-
158
- append_file_to_install_fingerprint() {
159
- local relative_path="$1"
160
- local source_path="$PROJECT_ROOT/$relative_path"
161
- printf 'path=%s\n' "$relative_path"
162
- if [ -f "$source_path" ]; then
163
- cat "$source_path"
164
- else
165
- printf '<missing>\n'
166
- fi
167
- }
168
-
169
- append_directory_to_install_fingerprint() {
170
- local relative_path="$1"
171
- local source_path="$PROJECT_ROOT/$relative_path"
172
- local file
173
- printf 'path=%s\n' "$relative_path"
174
- if [ ! -d "$source_path" ]; then
175
- printf '<missing>\n'
176
- return
177
- fi
178
-
179
- while IFS= read -r file; do
180
- printf 'file=%s\n' "${file#"${PROJECT_ROOT}/"}"
181
- cat "$file"
182
- done < <(find -P "$source_path" -type f -print | LC_ALL=C sort)
183
- }
184
-
185
- # Everything that can change the resolved dependency tree. The leading version marker
186
- # invalidates every stored state when this scheme itself changes.
187
- calculate_install_fingerprint() {
188
- local filename workspace argument
189
- {
190
- printf 'prepare-node-modules-install-state=1\n'
191
- printf 'node-version=%s\n' "$(node --version)"
192
- printf 'pnpm-version=%s\n' "$(pnpm --version)"
193
- printf 'node-env=%s\n' "${NODE_ENV:-}"
194
- printf 'npm-config-production=%s\n' "${npm_config_production:-}"
195
- printf 'npm-config-production-upper=%s\n' "${NPM_CONFIG_PRODUCTION:-}"
196
- printf 'pnpm-config-production=%s\n' "${PNPM_CONFIG_PRODUCTION:-}"
197
- for argument in "$@"; do
198
- printf 'install-argument=%s\n' "$argument"
199
- done
200
- for filename in package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
201
- append_file_to_install_fingerprint "$filename"
202
- done
203
- for workspace in client server; do
204
- append_file_to_install_fingerprint "$workspace/package.json"
205
- done
206
- append_directory_to_install_fingerprint patches
207
- } | hash_install_input
208
- }
209
-
210
- install_requires_refresh() {
211
- local argument
212
- for argument in "$@"; do
213
- case "$argument" in
214
- --force|-f|--lockfile-only|--fix-lockfile) return 0 ;;
215
- esac
216
- done
217
- return 1
218
- }
219
-
220
- # Reuse skips pnpm install entirely, so it must not skip side effects that live outside
221
- # node_modules. preinstall is exempt: it only guards which package manager may run, and
222
- # there is no install to guard when the tree is already in place.
223
- package_has_install_lifecycle() {
224
- local package_path="$1"
225
- node - "$package_path" <<'NODE'
226
- const fs = require('node:fs');
227
- const [packagePath] = process.argv.slice(2);
228
-
229
- try {
230
- const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
231
- const scripts = packageJson && typeof packageJson.scripts === 'object' && packageJson.scripts
232
- ? packageJson.scripts
233
- : {};
234
- const hasLifecycle = ['install', 'postinstall', 'prepare'].some(
235
- name => typeof scripts[name] === 'string' && scripts[name].trim() !== '',
236
- );
237
-
238
- process.exit(hasLifecycle ? 0 : 1);
239
- } catch {
240
- process.exit(0);
241
- }
242
- NODE
243
- }
244
-
245
- install_reuse_is_safe() {
246
- local workspace package_path
247
- for package_path in "$PROJECT_ROOT/.pnpmfile.cjs" "$PROJECT_ROOT/.pnpmfile.js"; do
248
- if [ -e "$package_path" ]; then
249
- echo "[node_modules] Reinstalling because pnpm hooks are present: $package_path"
250
- return 1
251
- fi
252
- done
253
-
254
- if [ -f "$PROJECT_ROOT/.npmrc" ] && grep -Eq '^[[:space:]]*pnpmfile[[:space:]]*=' "$PROJECT_ROOT/.npmrc"; then
255
- echo "[node_modules] Reinstalling because .npmrc configures a pnpm hook."
256
- return 1
257
- fi
258
-
259
- for workspace in . client server; do
260
- package_path="$PROJECT_ROOT/$workspace/package.json"
261
- [ -f "$package_path" ] || continue
262
- if package_has_install_lifecycle "$package_path"; then
263
- echo "[node_modules] Reinstalling because $package_path has an install lifecycle script."
264
- return 1
265
- fi
266
- done
267
- return 0
268
- }
269
-
270
- dependencies_are_ready() {
271
- local workspace
272
- [ -f "$WORK_DIR/node_modules/.modules.yaml" ] || return 1
273
- for workspace in client server; do
274
- if [ -f "$PROJECT_ROOT/$workspace/package.json" ] &&
275
- [ ! -f "$WORK_DIR/$workspace/node_modules/.modules.yaml" ]; then
276
- return 1
277
- fi
278
- done
279
- return 0
280
- }
281
-
282
- # 判断顺序按代价从小到大排。指纹要读云盘上的 lockfile 和整个 patches/,所以放最后:只有其余
283
- # 条件都满足、确实可能复用时才付这份开销。build.sh 和 prepare.sh 不设开关,第一行就返回了。
284
- install_can_be_reused() {
285
- local installed_fingerprint=""
286
-
287
- [ "${COZE_REUSE_NODE_MODULES:-}" = "1" ] || return 1
288
- install_requires_refresh "$@" && return 1
289
- [ -f "$INSTALL_STATE_FILE" ] || return 1
290
- IFS= read -r installed_fingerprint < "$INSTALL_STATE_FILE" || return 1
291
- dependencies_are_ready || return 1
292
- install_reuse_is_safe || return 1
293
- [ "$installed_fingerprint" = "$(calculate_install_fingerprint "$@")" ] || return 1
294
- return 0
295
- }
296
-
297
- write_install_state() {
298
- local fingerprint="$1" temporary_state
299
- temporary_state="$(mktemp "$WORK_DIR/.install-state.XXXXXX")"
300
- printf '%s\n' "$fingerprint" > "$temporary_state"
301
- mv -f "$temporary_state" "$INSTALL_STATE_FILE"
302
- }
303
-
304
144
  copy_install_metadata() {
305
145
  local filename source_file target_file workspace
306
146
  for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
@@ -350,26 +190,16 @@ link_node_modules() {
350
190
  cleanup_legacy_shadow_project
351
191
  copy_install_metadata
352
192
 
353
- if install_can_be_reused "$@"; then
354
- echo "[node_modules] Reusing unchanged dependencies in $WORK_DIR"
355
- else
356
- # A stale state file must not survive a failed install.
357
- rm -f "$INSTALL_STATE_FILE"
358
- echo "[node_modules] Installing dependencies in $WORK_DIR"
359
- if ! (cd "$WORK_DIR" && pnpm install "$@"); then
360
- exit 1
361
- fi
362
-
363
- if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
364
- temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
365
- cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
366
- chmod 0644 "$temporary_lockfile"
367
- mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
368
- fi
193
+ echo "[node_modules] Installing dependencies in $WORK_DIR"
194
+ if ! (cd "$WORK_DIR" && pnpm install "$@"); then
195
+ exit 1
196
+ fi
369
197
 
370
- # pnpm may have rewritten the lockfile, so record the fingerprint of the tree as it
371
- # now stands. Otherwise the next run would see a changed input and reinstall.
372
- write_install_state "$(calculate_install_fingerprint "$@")"
198
+ if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
199
+ temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
200
+ cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
201
+ chmod 0644 "$temporary_lockfile"
202
+ mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
373
203
  fi
374
204
 
375
205
  link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
@@ -5,10 +5,10 @@ project_type = "web"
5
5
 
6
6
  [dev]
7
7
  build = []
8
- run = ["python3", "-m", "http.server", "${DEPLOY_RUN_PORT}", "--bind", "0.0.0.0"]
8
+ run = [ "sh", "-c", "exec python3 -m http.server ${DEPLOY_RUN_PORT} --bind 0.0.0.0" ]
9
9
  run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/serve.ps1"]
10
10
 
11
11
  [deploy]
12
12
  build = []
13
- run = ["python3", "-m", "http.server", "${DEPLOY_RUN_PORT}", "--bind", "0.0.0.0"]
13
+ run = [ "sh", "-c", "exec python3 -m http.server ${DEPLOY_RUN_PORT} --bind 0.0.0.0" ]
14
14
  run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/serve.ps1"]
@@ -71,14 +71,9 @@ warn_source_symlinks() {
71
71
  done <<< "${links}"
72
72
  }
73
73
 
74
- # 云盘挂载偶尔会把新建文件的 mtime 设为 Unix epoch,这种时间戳会让所有按 mtime 判断新旧的
75
- # 环节(Next 的首轮路由扫描、webpack timestamp 快照、tsbuildinfo)拿到错误的结论。
76
- # 只修 mtime 确实异常的那些条目:无条件刷新整棵 app/pages 会让 webpack 每次启动都重算全部
77
- # 路由文件的 hash,并让云盘把它们全部重传一遍。不跟随软链、不改文件内容。
78
- # 判定 mtime 是否异常的下界。真实的项目文件不可能早于这个时间,落在它之前的只有被云盘写坏的
79
- # 时间戳,所以用它把"需要修"和"本来就好"区分开。
80
- DRIVE_MTIME_FLOOR="1971-01-01"
81
-
74
+ # 云盘挂载偶尔会把新建文件的 mtime 设为 Unix epoch。Watchpack 会因此把路由文件当作
75
+ # 启动前的旧状态,导致 Next 的首轮扫描漏掉 route/page。只在云盘项目启动前刷新 app/pages
76
+ # 的目录和普通文件时间戳;不跟随软链、不改文件内容。
82
77
  refresh_drive_route_mtimes() {
83
78
  local drive_root="${COZE_DRIVE_ROOT:-/Coze/Drive}"
84
79
  local project_root route_dir
@@ -96,7 +91,7 @@ refresh_drive_route_mtimes() {
96
91
 
97
92
  for route_dir in "${COZE_WORKSPACE_PATH}/src/app" "${COZE_WORKSPACE_PATH}/src/pages"; do
98
93
  [[ -d "${route_dir}" ]] || continue
99
- if ! find -P "${route_dir}" \( -type f -o -type d \) ! -newermt "${DRIVE_MTIME_FLOOR}" -exec touch {} + 2>/dev/null; then
94
+ if ! find -P "${route_dir}" \( -type f -o -type d \) -exec touch {} + 2>/dev/null; then
100
95
  echo "Warning: failed to refresh Next route timestamps in ${route_dir}." >&2
101
96
  fi
102
97
  done
@@ -105,8 +100,7 @@ refresh_drive_route_mtimes() {
105
100
  <% if (process.env.NODE_ENV === 'test') { %>
106
101
  echo "Clearing port ${DEPLOY_RUN_PORT} before start."
107
102
  kill_port_if_listening
108
- COZE_REUSE_NODE_MODULES=1 bash "${COZE_WORKSPACE_PATH}/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
109
- bash "${COZE_WORKSPACE_PATH}/scripts/prepare-next-dev-output.sh"
103
+ bash "${COZE_WORKSPACE_PATH}/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
110
104
  warn_source_symlinks
111
105
  refresh_drive_route_mtimes
112
106
  echo "Starting HTTP service on port ${DEPLOY_RUN_PORT} for dev..."
@@ -300,8 +294,7 @@ fi
300
294
 
301
295
  echo "Clearing port ${DEPLOY_RUN_PORT} before start."
302
296
  kill_port_if_listening
303
- COZE_REUSE_NODE_MODULES=1 bash "${COZE_WORKSPACE_PATH}/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
304
- bash "${COZE_WORKSPACE_PATH}/scripts/prepare-next-dev-output.sh"
297
+ bash "${COZE_WORKSPACE_PATH}/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
305
298
  warn_source_symlinks
306
299
  refresh_drive_route_mtimes
307
300
  echo "Starting HTTP service on port ${DEPLOY_RUN_PORT} for dev..."
@@ -98,7 +98,6 @@ printf '%s\n' "$$" > "$LOCK_DIR/pid"
98
98
  trap release_lock EXIT
99
99
 
100
100
  mkdir -p "$WORK_DIR"
101
- INSTALL_STATE_FILE="$WORK_DIR/.install-state"
102
101
 
103
102
  # Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
104
103
  # and framework dev servers two paths for the same source tree, which breaks
@@ -119,15 +118,12 @@ cleanup_legacy_workspace() {
119
118
  done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
120
119
  }
121
120
 
122
- # caches is reserved for the sibling prepare scripts of this project. They keep
123
- # regenerable framework output there (the Next helper points .next/dev into it), and that
124
- # output has to outlive an install, so this cleanup must not read it as leftover.
125
121
  cleanup_legacy_shadow_project() {
126
122
  local entry name
127
123
  while IFS= read -r -d '' entry; do
128
124
  name="$(basename "$entry")"
129
125
  case "$name" in
130
- node_modules|client|server|caches|.install-state) continue ;;
126
+ node_modules|client|server) continue ;;
131
127
  esac
132
128
  rm -rf "$entry"
133
129
  done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
@@ -145,162 +141,6 @@ link_install_entry() {
145
141
  fi
146
142
  }
147
143
 
148
- hash_install_input() {
149
- if command -v sha256sum >/dev/null 2>&1; then
150
- sha256sum | awk '{print $1}'
151
- elif command -v shasum >/dev/null 2>&1; then
152
- shasum -a 256 | awk '{print $1}'
153
- else
154
- cksum | awk '{print $1 "-" $2}'
155
- fi
156
- }
157
-
158
- append_file_to_install_fingerprint() {
159
- local relative_path="$1"
160
- local source_path="$PROJECT_ROOT/$relative_path"
161
- printf 'path=%s\n' "$relative_path"
162
- if [ -f "$source_path" ]; then
163
- cat "$source_path"
164
- else
165
- printf '<missing>\n'
166
- fi
167
- }
168
-
169
- append_directory_to_install_fingerprint() {
170
- local relative_path="$1"
171
- local source_path="$PROJECT_ROOT/$relative_path"
172
- local file
173
- printf 'path=%s\n' "$relative_path"
174
- if [ ! -d "$source_path" ]; then
175
- printf '<missing>\n'
176
- return
177
- fi
178
-
179
- while IFS= read -r file; do
180
- printf 'file=%s\n' "${file#"${PROJECT_ROOT}/"}"
181
- cat "$file"
182
- done < <(find -P "$source_path" -type f -print | LC_ALL=C sort)
183
- }
184
-
185
- # Everything that can change the resolved dependency tree. The leading version marker
186
- # invalidates every stored state when this scheme itself changes.
187
- calculate_install_fingerprint() {
188
- local filename workspace argument
189
- {
190
- printf 'prepare-node-modules-install-state=1\n'
191
- printf 'node-version=%s\n' "$(node --version)"
192
- printf 'pnpm-version=%s\n' "$(pnpm --version)"
193
- printf 'node-env=%s\n' "${NODE_ENV:-}"
194
- printf 'npm-config-production=%s\n' "${npm_config_production:-}"
195
- printf 'npm-config-production-upper=%s\n' "${NPM_CONFIG_PRODUCTION:-}"
196
- printf 'pnpm-config-production=%s\n' "${PNPM_CONFIG_PRODUCTION:-}"
197
- for argument in "$@"; do
198
- printf 'install-argument=%s\n' "$argument"
199
- done
200
- for filename in package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
201
- append_file_to_install_fingerprint "$filename"
202
- done
203
- for workspace in client server; do
204
- append_file_to_install_fingerprint "$workspace/package.json"
205
- done
206
- append_directory_to_install_fingerprint patches
207
- } | hash_install_input
208
- }
209
-
210
- install_requires_refresh() {
211
- local argument
212
- for argument in "$@"; do
213
- case "$argument" in
214
- --force|-f|--lockfile-only|--fix-lockfile) return 0 ;;
215
- esac
216
- done
217
- return 1
218
- }
219
-
220
- # Reuse skips pnpm install entirely, so it must not skip side effects that live outside
221
- # node_modules. preinstall is exempt: it only guards which package manager may run, and
222
- # there is no install to guard when the tree is already in place.
223
- package_has_install_lifecycle() {
224
- local package_path="$1"
225
- node - "$package_path" <<'NODE'
226
- const fs = require('node:fs');
227
- const [packagePath] = process.argv.slice(2);
228
-
229
- try {
230
- const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
231
- const scripts = packageJson && typeof packageJson.scripts === 'object' && packageJson.scripts
232
- ? packageJson.scripts
233
- : {};
234
- const hasLifecycle = ['install', 'postinstall', 'prepare'].some(
235
- name => typeof scripts[name] === 'string' && scripts[name].trim() !== '',
236
- );
237
-
238
- process.exit(hasLifecycle ? 0 : 1);
239
- } catch {
240
- process.exit(0);
241
- }
242
- NODE
243
- }
244
-
245
- install_reuse_is_safe() {
246
- local workspace package_path
247
- for package_path in "$PROJECT_ROOT/.pnpmfile.cjs" "$PROJECT_ROOT/.pnpmfile.js"; do
248
- if [ -e "$package_path" ]; then
249
- echo "[node_modules] Reinstalling because pnpm hooks are present: $package_path"
250
- return 1
251
- fi
252
- done
253
-
254
- if [ -f "$PROJECT_ROOT/.npmrc" ] && grep -Eq '^[[:space:]]*pnpmfile[[:space:]]*=' "$PROJECT_ROOT/.npmrc"; then
255
- echo "[node_modules] Reinstalling because .npmrc configures a pnpm hook."
256
- return 1
257
- fi
258
-
259
- for workspace in . client server; do
260
- package_path="$PROJECT_ROOT/$workspace/package.json"
261
- [ -f "$package_path" ] || continue
262
- if package_has_install_lifecycle "$package_path"; then
263
- echo "[node_modules] Reinstalling because $package_path has an install lifecycle script."
264
- return 1
265
- fi
266
- done
267
- return 0
268
- }
269
-
270
- dependencies_are_ready() {
271
- local workspace
272
- [ -f "$WORK_DIR/node_modules/.modules.yaml" ] || return 1
273
- for workspace in client server; do
274
- if [ -f "$PROJECT_ROOT/$workspace/package.json" ] &&
275
- [ ! -f "$WORK_DIR/$workspace/node_modules/.modules.yaml" ]; then
276
- return 1
277
- fi
278
- done
279
- return 0
280
- }
281
-
282
- # 判断顺序按代价从小到大排。指纹要读云盘上的 lockfile 和整个 patches/,所以放最后:只有其余
283
- # 条件都满足、确实可能复用时才付这份开销。build.sh 和 prepare.sh 不设开关,第一行就返回了。
284
- install_can_be_reused() {
285
- local installed_fingerprint=""
286
-
287
- [ "${COZE_REUSE_NODE_MODULES:-}" = "1" ] || return 1
288
- install_requires_refresh "$@" && return 1
289
- [ -f "$INSTALL_STATE_FILE" ] || return 1
290
- IFS= read -r installed_fingerprint < "$INSTALL_STATE_FILE" || return 1
291
- dependencies_are_ready || return 1
292
- install_reuse_is_safe || return 1
293
- [ "$installed_fingerprint" = "$(calculate_install_fingerprint "$@")" ] || return 1
294
- return 0
295
- }
296
-
297
- write_install_state() {
298
- local fingerprint="$1" temporary_state
299
- temporary_state="$(mktemp "$WORK_DIR/.install-state.XXXXXX")"
300
- printf '%s\n' "$fingerprint" > "$temporary_state"
301
- mv -f "$temporary_state" "$INSTALL_STATE_FILE"
302
- }
303
-
304
144
  copy_install_metadata() {
305
145
  local filename source_file target_file workspace
306
146
  for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
@@ -350,26 +190,16 @@ link_node_modules() {
350
190
  cleanup_legacy_shadow_project
351
191
  copy_install_metadata
352
192
 
353
- if install_can_be_reused "$@"; then
354
- echo "[node_modules] Reusing unchanged dependencies in $WORK_DIR"
355
- else
356
- # A stale state file must not survive a failed install.
357
- rm -f "$INSTALL_STATE_FILE"
358
- echo "[node_modules] Installing dependencies in $WORK_DIR"
359
- if ! (cd "$WORK_DIR" && pnpm install "$@"); then
360
- exit 1
361
- fi
362
-
363
- if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
364
- temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
365
- cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
366
- chmod 0644 "$temporary_lockfile"
367
- mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
368
- fi
193
+ echo "[node_modules] Installing dependencies in $WORK_DIR"
194
+ if ! (cd "$WORK_DIR" && pnpm install "$@"); then
195
+ exit 1
196
+ fi
369
197
 
370
- # pnpm may have rewritten the lockfile, so record the fingerprint of the tree as it
371
- # now stands. Otherwise the next run would see a changed input and reinstall.
372
- write_install_state "$(calculate_install_fingerprint "$@")"
198
+ if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
199
+ temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
200
+ cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
201
+ chmod 0644 "$temporary_lockfile"
202
+ mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
373
203
  fi
374
204
 
375
205
  link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
@@ -27,6 +27,7 @@
27
27
  "**/*.ts",
28
28
  "**/*.tsx",
29
29
  ".next/types/**/*.ts",
30
+ ".next/dev/types/**/*.ts",
30
31
  "**/*.mts"
31
32
  ],
32
33
  "exclude": ["node_modules", "dist"]
@@ -98,7 +98,6 @@ printf '%s\n' "$$" > "$LOCK_DIR/pid"
98
98
  trap release_lock EXIT
99
99
 
100
100
  mkdir -p "$WORK_DIR"
101
- INSTALL_STATE_FILE="$WORK_DIR/.install-state"
102
101
 
103
102
  # Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
104
103
  # and framework dev servers two paths for the same source tree, which breaks
@@ -119,15 +118,12 @@ cleanup_legacy_workspace() {
119
118
  done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
120
119
  }
121
120
 
122
- # caches is reserved for the sibling prepare scripts of this project. They keep
123
- # regenerable framework output there (the Next helper points .next/dev into it), and that
124
- # output has to outlive an install, so this cleanup must not read it as leftover.
125
121
  cleanup_legacy_shadow_project() {
126
122
  local entry name
127
123
  while IFS= read -r -d '' entry; do
128
124
  name="$(basename "$entry")"
129
125
  case "$name" in
130
- node_modules|client|server|caches|.install-state) continue ;;
126
+ node_modules|client|server) continue ;;
131
127
  esac
132
128
  rm -rf "$entry"
133
129
  done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
@@ -145,162 +141,6 @@ link_install_entry() {
145
141
  fi
146
142
  }
147
143
 
148
- hash_install_input() {
149
- if command -v sha256sum >/dev/null 2>&1; then
150
- sha256sum | awk '{print $1}'
151
- elif command -v shasum >/dev/null 2>&1; then
152
- shasum -a 256 | awk '{print $1}'
153
- else
154
- cksum | awk '{print $1 "-" $2}'
155
- fi
156
- }
157
-
158
- append_file_to_install_fingerprint() {
159
- local relative_path="$1"
160
- local source_path="$PROJECT_ROOT/$relative_path"
161
- printf 'path=%s\n' "$relative_path"
162
- if [ -f "$source_path" ]; then
163
- cat "$source_path"
164
- else
165
- printf '<missing>\n'
166
- fi
167
- }
168
-
169
- append_directory_to_install_fingerprint() {
170
- local relative_path="$1"
171
- local source_path="$PROJECT_ROOT/$relative_path"
172
- local file
173
- printf 'path=%s\n' "$relative_path"
174
- if [ ! -d "$source_path" ]; then
175
- printf '<missing>\n'
176
- return
177
- fi
178
-
179
- while IFS= read -r file; do
180
- printf 'file=%s\n' "${file#"${PROJECT_ROOT}/"}"
181
- cat "$file"
182
- done < <(find -P "$source_path" -type f -print | LC_ALL=C sort)
183
- }
184
-
185
- # Everything that can change the resolved dependency tree. The leading version marker
186
- # invalidates every stored state when this scheme itself changes.
187
- calculate_install_fingerprint() {
188
- local filename workspace argument
189
- {
190
- printf 'prepare-node-modules-install-state=1\n'
191
- printf 'node-version=%s\n' "$(node --version)"
192
- printf 'pnpm-version=%s\n' "$(pnpm --version)"
193
- printf 'node-env=%s\n' "${NODE_ENV:-}"
194
- printf 'npm-config-production=%s\n' "${npm_config_production:-}"
195
- printf 'npm-config-production-upper=%s\n' "${NPM_CONFIG_PRODUCTION:-}"
196
- printf 'pnpm-config-production=%s\n' "${PNPM_CONFIG_PRODUCTION:-}"
197
- for argument in "$@"; do
198
- printf 'install-argument=%s\n' "$argument"
199
- done
200
- for filename in package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
201
- append_file_to_install_fingerprint "$filename"
202
- done
203
- for workspace in client server; do
204
- append_file_to_install_fingerprint "$workspace/package.json"
205
- done
206
- append_directory_to_install_fingerprint patches
207
- } | hash_install_input
208
- }
209
-
210
- install_requires_refresh() {
211
- local argument
212
- for argument in "$@"; do
213
- case "$argument" in
214
- --force|-f|--lockfile-only|--fix-lockfile) return 0 ;;
215
- esac
216
- done
217
- return 1
218
- }
219
-
220
- # Reuse skips pnpm install entirely, so it must not skip side effects that live outside
221
- # node_modules. preinstall is exempt: it only guards which package manager may run, and
222
- # there is no install to guard when the tree is already in place.
223
- package_has_install_lifecycle() {
224
- local package_path="$1"
225
- node - "$package_path" <<'NODE'
226
- const fs = require('node:fs');
227
- const [packagePath] = process.argv.slice(2);
228
-
229
- try {
230
- const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
231
- const scripts = packageJson && typeof packageJson.scripts === 'object' && packageJson.scripts
232
- ? packageJson.scripts
233
- : {};
234
- const hasLifecycle = ['install', 'postinstall', 'prepare'].some(
235
- name => typeof scripts[name] === 'string' && scripts[name].trim() !== '',
236
- );
237
-
238
- process.exit(hasLifecycle ? 0 : 1);
239
- } catch {
240
- process.exit(0);
241
- }
242
- NODE
243
- }
244
-
245
- install_reuse_is_safe() {
246
- local workspace package_path
247
- for package_path in "$PROJECT_ROOT/.pnpmfile.cjs" "$PROJECT_ROOT/.pnpmfile.js"; do
248
- if [ -e "$package_path" ]; then
249
- echo "[node_modules] Reinstalling because pnpm hooks are present: $package_path"
250
- return 1
251
- fi
252
- done
253
-
254
- if [ -f "$PROJECT_ROOT/.npmrc" ] && grep -Eq '^[[:space:]]*pnpmfile[[:space:]]*=' "$PROJECT_ROOT/.npmrc"; then
255
- echo "[node_modules] Reinstalling because .npmrc configures a pnpm hook."
256
- return 1
257
- fi
258
-
259
- for workspace in . client server; do
260
- package_path="$PROJECT_ROOT/$workspace/package.json"
261
- [ -f "$package_path" ] || continue
262
- if package_has_install_lifecycle "$package_path"; then
263
- echo "[node_modules] Reinstalling because $package_path has an install lifecycle script."
264
- return 1
265
- fi
266
- done
267
- return 0
268
- }
269
-
270
- dependencies_are_ready() {
271
- local workspace
272
- [ -f "$WORK_DIR/node_modules/.modules.yaml" ] || return 1
273
- for workspace in client server; do
274
- if [ -f "$PROJECT_ROOT/$workspace/package.json" ] &&
275
- [ ! -f "$WORK_DIR/$workspace/node_modules/.modules.yaml" ]; then
276
- return 1
277
- fi
278
- done
279
- return 0
280
- }
281
-
282
- # 判断顺序按代价从小到大排。指纹要读云盘上的 lockfile 和整个 patches/,所以放最后:只有其余
283
- # 条件都满足、确实可能复用时才付这份开销。build.sh 和 prepare.sh 不设开关,第一行就返回了。
284
- install_can_be_reused() {
285
- local installed_fingerprint=""
286
-
287
- [ "${COZE_REUSE_NODE_MODULES:-}" = "1" ] || return 1
288
- install_requires_refresh "$@" && return 1
289
- [ -f "$INSTALL_STATE_FILE" ] || return 1
290
- IFS= read -r installed_fingerprint < "$INSTALL_STATE_FILE" || return 1
291
- dependencies_are_ready || return 1
292
- install_reuse_is_safe || return 1
293
- [ "$installed_fingerprint" = "$(calculate_install_fingerprint "$@")" ] || return 1
294
- return 0
295
- }
296
-
297
- write_install_state() {
298
- local fingerprint="$1" temporary_state
299
- temporary_state="$(mktemp "$WORK_DIR/.install-state.XXXXXX")"
300
- printf '%s\n' "$fingerprint" > "$temporary_state"
301
- mv -f "$temporary_state" "$INSTALL_STATE_FILE"
302
- }
303
-
304
144
  copy_install_metadata() {
305
145
  local filename source_file target_file workspace
306
146
  for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
@@ -350,26 +190,16 @@ link_node_modules() {
350
190
  cleanup_legacy_shadow_project
351
191
  copy_install_metadata
352
192
 
353
- if install_can_be_reused "$@"; then
354
- echo "[node_modules] Reusing unchanged dependencies in $WORK_DIR"
355
- else
356
- # A stale state file must not survive a failed install.
357
- rm -f "$INSTALL_STATE_FILE"
358
- echo "[node_modules] Installing dependencies in $WORK_DIR"
359
- if ! (cd "$WORK_DIR" && pnpm install "$@"); then
360
- exit 1
361
- fi
362
-
363
- if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
364
- temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
365
- cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
366
- chmod 0644 "$temporary_lockfile"
367
- mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
368
- fi
193
+ echo "[node_modules] Installing dependencies in $WORK_DIR"
194
+ if ! (cd "$WORK_DIR" && pnpm install "$@"); then
195
+ exit 1
196
+ fi
369
197
 
370
- # pnpm may have rewritten the lockfile, so record the fingerprint of the tree as it
371
- # now stands. Otherwise the next run would see a changed input and reinstall.
372
- write_install_state "$(calculate_install_fingerprint "$@")"
198
+ if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
199
+ temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
200
+ cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
201
+ chmod 0644 "$temporary_lockfile"
202
+ mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
373
203
  fi
374
204
 
375
205
  link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
@@ -98,7 +98,6 @@ printf '%s\n' "$$" > "$LOCK_DIR/pid"
98
98
  trap release_lock EXIT
99
99
 
100
100
  mkdir -p "$WORK_DIR"
101
- INSTALL_STATE_FILE="$WORK_DIR/.install-state"
102
101
 
103
102
  # Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
104
103
  # and framework dev servers two paths for the same source tree, which breaks
@@ -119,15 +118,12 @@ cleanup_legacy_workspace() {
119
118
  done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
120
119
  }
121
120
 
122
- # caches is reserved for the sibling prepare scripts of this project. They keep
123
- # regenerable framework output there (the Next helper points .next/dev into it), and that
124
- # output has to outlive an install, so this cleanup must not read it as leftover.
125
121
  cleanup_legacy_shadow_project() {
126
122
  local entry name
127
123
  while IFS= read -r -d '' entry; do
128
124
  name="$(basename "$entry")"
129
125
  case "$name" in
130
- node_modules|client|server|caches|.install-state) continue ;;
126
+ node_modules|client|server) continue ;;
131
127
  esac
132
128
  rm -rf "$entry"
133
129
  done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
@@ -145,162 +141,6 @@ link_install_entry() {
145
141
  fi
146
142
  }
147
143
 
148
- hash_install_input() {
149
- if command -v sha256sum >/dev/null 2>&1; then
150
- sha256sum | awk '{print $1}'
151
- elif command -v shasum >/dev/null 2>&1; then
152
- shasum -a 256 | awk '{print $1}'
153
- else
154
- cksum | awk '{print $1 "-" $2}'
155
- fi
156
- }
157
-
158
- append_file_to_install_fingerprint() {
159
- local relative_path="$1"
160
- local source_path="$PROJECT_ROOT/$relative_path"
161
- printf 'path=%s\n' "$relative_path"
162
- if [ -f "$source_path" ]; then
163
- cat "$source_path"
164
- else
165
- printf '<missing>\n'
166
- fi
167
- }
168
-
169
- append_directory_to_install_fingerprint() {
170
- local relative_path="$1"
171
- local source_path="$PROJECT_ROOT/$relative_path"
172
- local file
173
- printf 'path=%s\n' "$relative_path"
174
- if [ ! -d "$source_path" ]; then
175
- printf '<missing>\n'
176
- return
177
- fi
178
-
179
- while IFS= read -r file; do
180
- printf 'file=%s\n' "${file#"${PROJECT_ROOT}/"}"
181
- cat "$file"
182
- done < <(find -P "$source_path" -type f -print | LC_ALL=C sort)
183
- }
184
-
185
- # Everything that can change the resolved dependency tree. The leading version marker
186
- # invalidates every stored state when this scheme itself changes.
187
- calculate_install_fingerprint() {
188
- local filename workspace argument
189
- {
190
- printf 'prepare-node-modules-install-state=1\n'
191
- printf 'node-version=%s\n' "$(node --version)"
192
- printf 'pnpm-version=%s\n' "$(pnpm --version)"
193
- printf 'node-env=%s\n' "${NODE_ENV:-}"
194
- printf 'npm-config-production=%s\n' "${npm_config_production:-}"
195
- printf 'npm-config-production-upper=%s\n' "${NPM_CONFIG_PRODUCTION:-}"
196
- printf 'pnpm-config-production=%s\n' "${PNPM_CONFIG_PRODUCTION:-}"
197
- for argument in "$@"; do
198
- printf 'install-argument=%s\n' "$argument"
199
- done
200
- for filename in package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
201
- append_file_to_install_fingerprint "$filename"
202
- done
203
- for workspace in client server; do
204
- append_file_to_install_fingerprint "$workspace/package.json"
205
- done
206
- append_directory_to_install_fingerprint patches
207
- } | hash_install_input
208
- }
209
-
210
- install_requires_refresh() {
211
- local argument
212
- for argument in "$@"; do
213
- case "$argument" in
214
- --force|-f|--lockfile-only|--fix-lockfile) return 0 ;;
215
- esac
216
- done
217
- return 1
218
- }
219
-
220
- # Reuse skips pnpm install entirely, so it must not skip side effects that live outside
221
- # node_modules. preinstall is exempt: it only guards which package manager may run, and
222
- # there is no install to guard when the tree is already in place.
223
- package_has_install_lifecycle() {
224
- local package_path="$1"
225
- node - "$package_path" <<'NODE'
226
- const fs = require('node:fs');
227
- const [packagePath] = process.argv.slice(2);
228
-
229
- try {
230
- const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
231
- const scripts = packageJson && typeof packageJson.scripts === 'object' && packageJson.scripts
232
- ? packageJson.scripts
233
- : {};
234
- const hasLifecycle = ['install', 'postinstall', 'prepare'].some(
235
- name => typeof scripts[name] === 'string' && scripts[name].trim() !== '',
236
- );
237
-
238
- process.exit(hasLifecycle ? 0 : 1);
239
- } catch {
240
- process.exit(0);
241
- }
242
- NODE
243
- }
244
-
245
- install_reuse_is_safe() {
246
- local workspace package_path
247
- for package_path in "$PROJECT_ROOT/.pnpmfile.cjs" "$PROJECT_ROOT/.pnpmfile.js"; do
248
- if [ -e "$package_path" ]; then
249
- echo "[node_modules] Reinstalling because pnpm hooks are present: $package_path"
250
- return 1
251
- fi
252
- done
253
-
254
- if [ -f "$PROJECT_ROOT/.npmrc" ] && grep -Eq '^[[:space:]]*pnpmfile[[:space:]]*=' "$PROJECT_ROOT/.npmrc"; then
255
- echo "[node_modules] Reinstalling because .npmrc configures a pnpm hook."
256
- return 1
257
- fi
258
-
259
- for workspace in . client server; do
260
- package_path="$PROJECT_ROOT/$workspace/package.json"
261
- [ -f "$package_path" ] || continue
262
- if package_has_install_lifecycle "$package_path"; then
263
- echo "[node_modules] Reinstalling because $package_path has an install lifecycle script."
264
- return 1
265
- fi
266
- done
267
- return 0
268
- }
269
-
270
- dependencies_are_ready() {
271
- local workspace
272
- [ -f "$WORK_DIR/node_modules/.modules.yaml" ] || return 1
273
- for workspace in client server; do
274
- if [ -f "$PROJECT_ROOT/$workspace/package.json" ] &&
275
- [ ! -f "$WORK_DIR/$workspace/node_modules/.modules.yaml" ]; then
276
- return 1
277
- fi
278
- done
279
- return 0
280
- }
281
-
282
- # 判断顺序按代价从小到大排。指纹要读云盘上的 lockfile 和整个 patches/,所以放最后:只有其余
283
- # 条件都满足、确实可能复用时才付这份开销。build.sh 和 prepare.sh 不设开关,第一行就返回了。
284
- install_can_be_reused() {
285
- local installed_fingerprint=""
286
-
287
- [ "${COZE_REUSE_NODE_MODULES:-}" = "1" ] || return 1
288
- install_requires_refresh "$@" && return 1
289
- [ -f "$INSTALL_STATE_FILE" ] || return 1
290
- IFS= read -r installed_fingerprint < "$INSTALL_STATE_FILE" || return 1
291
- dependencies_are_ready || return 1
292
- install_reuse_is_safe || return 1
293
- [ "$installed_fingerprint" = "$(calculate_install_fingerprint "$@")" ] || return 1
294
- return 0
295
- }
296
-
297
- write_install_state() {
298
- local fingerprint="$1" temporary_state
299
- temporary_state="$(mktemp "$WORK_DIR/.install-state.XXXXXX")"
300
- printf '%s\n' "$fingerprint" > "$temporary_state"
301
- mv -f "$temporary_state" "$INSTALL_STATE_FILE"
302
- }
303
-
304
144
  copy_install_metadata() {
305
145
  local filename source_file target_file workspace
306
146
  for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
@@ -350,26 +190,16 @@ link_node_modules() {
350
190
  cleanup_legacy_shadow_project
351
191
  copy_install_metadata
352
192
 
353
- if install_can_be_reused "$@"; then
354
- echo "[node_modules] Reusing unchanged dependencies in $WORK_DIR"
355
- else
356
- # A stale state file must not survive a failed install.
357
- rm -f "$INSTALL_STATE_FILE"
358
- echo "[node_modules] Installing dependencies in $WORK_DIR"
359
- if ! (cd "$WORK_DIR" && pnpm install "$@"); then
360
- exit 1
361
- fi
362
-
363
- if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
364
- temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
365
- cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
366
- chmod 0644 "$temporary_lockfile"
367
- mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
368
- fi
193
+ echo "[node_modules] Installing dependencies in $WORK_DIR"
194
+ if ! (cd "$WORK_DIR" && pnpm install "$@"); then
195
+ exit 1
196
+ fi
369
197
 
370
- # pnpm may have rewritten the lockfile, so record the fingerprint of the tree as it
371
- # now stands. Otherwise the next run would see a changed input and reinstall.
372
- write_install_state "$(calculate_install_fingerprint "$@")"
198
+ if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
199
+ temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
200
+ cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
201
+ chmod 0644 "$temporary_lockfile"
202
+ mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
373
203
  fi
374
204
 
375
205
  link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
@@ -98,7 +98,6 @@ printf '%s\n' "$$" > "$LOCK_DIR/pid"
98
98
  trap release_lock EXIT
99
99
 
100
100
  mkdir -p "$WORK_DIR"
101
- INSTALL_STATE_FILE="$WORK_DIR/.install-state"
102
101
 
103
102
  # Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
104
103
  # and framework dev servers two paths for the same source tree, which breaks
@@ -119,15 +118,12 @@ cleanup_legacy_workspace() {
119
118
  done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
120
119
  }
121
120
 
122
- # caches is reserved for the sibling prepare scripts of this project. They keep
123
- # regenerable framework output there (the Next helper points .next/dev into it), and that
124
- # output has to outlive an install, so this cleanup must not read it as leftover.
125
121
  cleanup_legacy_shadow_project() {
126
122
  local entry name
127
123
  while IFS= read -r -d '' entry; do
128
124
  name="$(basename "$entry")"
129
125
  case "$name" in
130
- node_modules|client|server|caches|.install-state) continue ;;
126
+ node_modules|client|server) continue ;;
131
127
  esac
132
128
  rm -rf "$entry"
133
129
  done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
@@ -145,162 +141,6 @@ link_install_entry() {
145
141
  fi
146
142
  }
147
143
 
148
- hash_install_input() {
149
- if command -v sha256sum >/dev/null 2>&1; then
150
- sha256sum | awk '{print $1}'
151
- elif command -v shasum >/dev/null 2>&1; then
152
- shasum -a 256 | awk '{print $1}'
153
- else
154
- cksum | awk '{print $1 "-" $2}'
155
- fi
156
- }
157
-
158
- append_file_to_install_fingerprint() {
159
- local relative_path="$1"
160
- local source_path="$PROJECT_ROOT/$relative_path"
161
- printf 'path=%s\n' "$relative_path"
162
- if [ -f "$source_path" ]; then
163
- cat "$source_path"
164
- else
165
- printf '<missing>\n'
166
- fi
167
- }
168
-
169
- append_directory_to_install_fingerprint() {
170
- local relative_path="$1"
171
- local source_path="$PROJECT_ROOT/$relative_path"
172
- local file
173
- printf 'path=%s\n' "$relative_path"
174
- if [ ! -d "$source_path" ]; then
175
- printf '<missing>\n'
176
- return
177
- fi
178
-
179
- while IFS= read -r file; do
180
- printf 'file=%s\n' "${file#"${PROJECT_ROOT}/"}"
181
- cat "$file"
182
- done < <(find -P "$source_path" -type f -print | LC_ALL=C sort)
183
- }
184
-
185
- # Everything that can change the resolved dependency tree. The leading version marker
186
- # invalidates every stored state when this scheme itself changes.
187
- calculate_install_fingerprint() {
188
- local filename workspace argument
189
- {
190
- printf 'prepare-node-modules-install-state=1\n'
191
- printf 'node-version=%s\n' "$(node --version)"
192
- printf 'pnpm-version=%s\n' "$(pnpm --version)"
193
- printf 'node-env=%s\n' "${NODE_ENV:-}"
194
- printf 'npm-config-production=%s\n' "${npm_config_production:-}"
195
- printf 'npm-config-production-upper=%s\n' "${NPM_CONFIG_PRODUCTION:-}"
196
- printf 'pnpm-config-production=%s\n' "${PNPM_CONFIG_PRODUCTION:-}"
197
- for argument in "$@"; do
198
- printf 'install-argument=%s\n' "$argument"
199
- done
200
- for filename in package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
201
- append_file_to_install_fingerprint "$filename"
202
- done
203
- for workspace in client server; do
204
- append_file_to_install_fingerprint "$workspace/package.json"
205
- done
206
- append_directory_to_install_fingerprint patches
207
- } | hash_install_input
208
- }
209
-
210
- install_requires_refresh() {
211
- local argument
212
- for argument in "$@"; do
213
- case "$argument" in
214
- --force|-f|--lockfile-only|--fix-lockfile) return 0 ;;
215
- esac
216
- done
217
- return 1
218
- }
219
-
220
- # Reuse skips pnpm install entirely, so it must not skip side effects that live outside
221
- # node_modules. preinstall is exempt: it only guards which package manager may run, and
222
- # there is no install to guard when the tree is already in place.
223
- package_has_install_lifecycle() {
224
- local package_path="$1"
225
- node - "$package_path" <<'NODE'
226
- const fs = require('node:fs');
227
- const [packagePath] = process.argv.slice(2);
228
-
229
- try {
230
- const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
231
- const scripts = packageJson && typeof packageJson.scripts === 'object' && packageJson.scripts
232
- ? packageJson.scripts
233
- : {};
234
- const hasLifecycle = ['install', 'postinstall', 'prepare'].some(
235
- name => typeof scripts[name] === 'string' && scripts[name].trim() !== '',
236
- );
237
-
238
- process.exit(hasLifecycle ? 0 : 1);
239
- } catch {
240
- process.exit(0);
241
- }
242
- NODE
243
- }
244
-
245
- install_reuse_is_safe() {
246
- local workspace package_path
247
- for package_path in "$PROJECT_ROOT/.pnpmfile.cjs" "$PROJECT_ROOT/.pnpmfile.js"; do
248
- if [ -e "$package_path" ]; then
249
- echo "[node_modules] Reinstalling because pnpm hooks are present: $package_path"
250
- return 1
251
- fi
252
- done
253
-
254
- if [ -f "$PROJECT_ROOT/.npmrc" ] && grep -Eq '^[[:space:]]*pnpmfile[[:space:]]*=' "$PROJECT_ROOT/.npmrc"; then
255
- echo "[node_modules] Reinstalling because .npmrc configures a pnpm hook."
256
- return 1
257
- fi
258
-
259
- for workspace in . client server; do
260
- package_path="$PROJECT_ROOT/$workspace/package.json"
261
- [ -f "$package_path" ] || continue
262
- if package_has_install_lifecycle "$package_path"; then
263
- echo "[node_modules] Reinstalling because $package_path has an install lifecycle script."
264
- return 1
265
- fi
266
- done
267
- return 0
268
- }
269
-
270
- dependencies_are_ready() {
271
- local workspace
272
- [ -f "$WORK_DIR/node_modules/.modules.yaml" ] || return 1
273
- for workspace in client server; do
274
- if [ -f "$PROJECT_ROOT/$workspace/package.json" ] &&
275
- [ ! -f "$WORK_DIR/$workspace/node_modules/.modules.yaml" ]; then
276
- return 1
277
- fi
278
- done
279
- return 0
280
- }
281
-
282
- # 判断顺序按代价从小到大排。指纹要读云盘上的 lockfile 和整个 patches/,所以放最后:只有其余
283
- # 条件都满足、确实可能复用时才付这份开销。build.sh 和 prepare.sh 不设开关,第一行就返回了。
284
- install_can_be_reused() {
285
- local installed_fingerprint=""
286
-
287
- [ "${COZE_REUSE_NODE_MODULES:-}" = "1" ] || return 1
288
- install_requires_refresh "$@" && return 1
289
- [ -f "$INSTALL_STATE_FILE" ] || return 1
290
- IFS= read -r installed_fingerprint < "$INSTALL_STATE_FILE" || return 1
291
- dependencies_are_ready || return 1
292
- install_reuse_is_safe || return 1
293
- [ "$installed_fingerprint" = "$(calculate_install_fingerprint "$@")" ] || return 1
294
- return 0
295
- }
296
-
297
- write_install_state() {
298
- local fingerprint="$1" temporary_state
299
- temporary_state="$(mktemp "$WORK_DIR/.install-state.XXXXXX")"
300
- printf '%s\n' "$fingerprint" > "$temporary_state"
301
- mv -f "$temporary_state" "$INSTALL_STATE_FILE"
302
- }
303
-
304
144
  copy_install_metadata() {
305
145
  local filename source_file target_file workspace
306
146
  for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
@@ -350,26 +190,16 @@ link_node_modules() {
350
190
  cleanup_legacy_shadow_project
351
191
  copy_install_metadata
352
192
 
353
- if install_can_be_reused "$@"; then
354
- echo "[node_modules] Reusing unchanged dependencies in $WORK_DIR"
355
- else
356
- # A stale state file must not survive a failed install.
357
- rm -f "$INSTALL_STATE_FILE"
358
- echo "[node_modules] Installing dependencies in $WORK_DIR"
359
- if ! (cd "$WORK_DIR" && pnpm install "$@"); then
360
- exit 1
361
- fi
362
-
363
- if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
364
- temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
365
- cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
366
- chmod 0644 "$temporary_lockfile"
367
- mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
368
- fi
193
+ echo "[node_modules] Installing dependencies in $WORK_DIR"
194
+ if ! (cd "$WORK_DIR" && pnpm install "$@"); then
195
+ exit 1
196
+ fi
369
197
 
370
- # pnpm may have rewritten the lockfile, so record the fingerprint of the tree as it
371
- # now stands. Otherwise the next run would see a changed input and reinstall.
372
- write_install_state "$(calculate_install_fingerprint "$@")"
198
+ if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
199
+ temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
200
+ cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
201
+ chmod 0644 "$temporary_lockfile"
202
+ mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
373
203
  fi
374
204
 
375
205
  link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
package/lib/cli.js CHANGED
@@ -2114,7 +2114,7 @@ const EventBuilder = {
2114
2114
  };
2115
2115
 
2116
2116
  var name = "@coze-arch/cli";
2117
- var version = "0.1.8-alpha.a87968";
2117
+ var version = "0.1.8";
2118
2118
  var description = "coze coding devtools cli";
2119
2119
  var license = "MIT";
2120
2120
  var author = "fanwenjie.fe@bytedance.com";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coze-arch/cli",
3
- "version": "0.1.8-alpha.a87968",
3
+ "version": "0.1.8",
4
4
  "private": false,
5
5
  "description": "coze coding devtools cli",
6
6
  "license": "MIT",
@@ -1,122 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Keep Next development output off Coze Drive without moving production output.
3
- set -euo pipefail
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
6
- if [ "$(basename "$(dirname "$SCRIPT_DIR")")" = ".cozeproj" ]; then
7
- PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
8
- else
9
- PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
10
- fi
11
-
12
- DRIVE_ROOT="${COZE_DRIVE_ROOT:-/Coze/Drive}"
13
- if [ -d "$DRIVE_ROOT" ]; then
14
- DRIVE_ROOT="$(cd "$DRIVE_ROOT" && pwd -P)"
15
- else
16
- DRIVE_ROOT="${DRIVE_ROOT%/}"
17
- fi
18
- case "$PROJECT_ROOT" in
19
- "$DRIVE_ROOT"|"$DRIVE_ROOT"/*) ;;
20
- *) exit 0 ;;
21
- esac
22
-
23
- # Next writes every on-demand development build into .next/dev. On Coze Drive each of
24
- # those writes goes through the sync daemon, so only that subtree moves to local disk.
25
- # Production output stays in the project directory, which keeps `next build` and
26
- # deployment unchanged.
27
- #
28
- # One local directory per project, shared with prepare-node-modules.sh: node_modules and
29
- # this cache sit side by side under WORK_DIR. The caches subdirectory is the slot that
30
- # script reserves for output that must survive an install, so nothing here is cleaned up
31
- # behind our back. PROJECT_ID has to stay identical to the one it computes.
32
- NM_ROOT="/tmp/nm"
33
- PROJECT_ID="$(basename "$PROJECT_ROOT")-$(printf '%s' "$PROJECT_ROOT" | cksum | awk '{print $1}')"
34
- WORK_DIR="$NM_ROOT/$PROJECT_ID"
35
- NEXT_ROOT="$PROJECT_ROOT/.next"
36
- NEXT_DEV="$NEXT_ROOT/dev"
37
- LEGACY_NEXT_ROOT="$WORK_DIR/.next"
38
- LOCAL_NEXT_DEV="$WORK_DIR/caches/next-dev"
39
-
40
- case "$LOCAL_NEXT_DEV" in
41
- "$DRIVE_ROOT"|"$DRIVE_ROOT"/*)
42
- echo "[next] local development output must not be on Coze Drive: $LOCAL_NEXT_DEV" >&2
43
- exit 1
44
- ;;
45
- esac
46
-
47
- # Coze Drive sync does not preserve symlinks: a synced copy of a link arrives as a
48
- # regular file holding the link target. Recognize the links this scheme creates so a
49
- # project that came back from sync repairs itself instead of failing to start.
50
- is_flattened_managed_link() {
51
- local candidate="$1" content=""
52
- [ -f "$candidate" ] || return 1
53
- [ ! -L "$candidate" ] || return 1
54
- [ "$(wc -c < "$candidate")" -le 4096 ] || return 1
55
- content="$(tr -d '\r\n' < "$candidate")"
56
- case "$content" in
57
- "$NM_ROOT"/*) return 0 ;;
58
- esac
59
- return 1
60
- }
61
-
62
- # Earlier versions linked the whole .next directory. Bring any production build made
63
- # back then into the project; from here on only .next/dev is redirected.
64
- restore_legacy_next_root() {
65
- local entry
66
- rm -f "$NEXT_ROOT"
67
- mkdir -p "$NEXT_ROOT"
68
- if [ -d "$LEGACY_NEXT_ROOT" ]; then
69
- while IFS= read -r -d '' entry; do
70
- mv "$entry" "$NEXT_ROOT/"
71
- done < <(find -P "$LEGACY_NEXT_ROOT" -mindepth 1 -maxdepth 1 -print0)
72
- rmdir "$LEGACY_NEXT_ROOT" 2>/dev/null || true
73
- fi
74
- }
75
-
76
- if [ -L "$NEXT_ROOT" ]; then
77
- case "$(readlink "$NEXT_ROOT")" in
78
- "$NM_ROOT"/*)
79
- echo "[next] Restoring legacy .next output to the project directory."
80
- restore_legacy_next_root
81
- ;;
82
- *)
83
- echo "[next] refusing to replace unmanaged .next symlink: $NEXT_ROOT" >&2
84
- exit 1
85
- ;;
86
- esac
87
- elif is_flattened_managed_link "$NEXT_ROOT"; then
88
- echo "[next] Recovering .next flattened by Coze Drive sync."
89
- restore_legacy_next_root
90
- elif [ -e "$NEXT_ROOT" ] && [ ! -d "$NEXT_ROOT" ]; then
91
- echo "[next] refusing to replace non-directory Next output: $NEXT_ROOT" >&2
92
- exit 1
93
- fi
94
-
95
- mkdir -p "$NEXT_ROOT" "$LOCAL_NEXT_DEV"
96
-
97
- if [ -L "$NEXT_DEV" ]; then
98
- if [ "$(readlink "$NEXT_DEV")" = "$LOCAL_NEXT_DEV" ]; then
99
- exit 0
100
- fi
101
- case "$(readlink "$NEXT_DEV")" in
102
- "$NM_ROOT"/*) rm "$NEXT_DEV" ;;
103
- *)
104
- echo "[next] refusing to replace unmanaged .next/dev symlink: $NEXT_DEV" >&2
105
- exit 1
106
- ;;
107
- esac
108
- elif [ -e "$NEXT_DEV" ]; then
109
- # .next/dev holds development output only, and Next regenerates it on demand.
110
- rm -rf "$NEXT_DEV"
111
- fi
112
-
113
- # A second dev start can win the race between the removal above and this link.
114
- if ! ln -s "$LOCAL_NEXT_DEV" "$NEXT_DEV" 2>/dev/null; then
115
- if [ -L "$NEXT_DEV" ] && [ "$(readlink "$NEXT_DEV")" = "$LOCAL_NEXT_DEV" ]; then
116
- exit 0
117
- fi
118
- echo "[next] failed to link development output: $NEXT_DEV" >&2
119
- exit 1
120
- fi
121
-
122
- echo "[next] Development output linked to $LOCAL_NEXT_DEV"