Skip to content

Commit 1284383

Browse files
committed
feat(daemon): rebase daemon_mode_b_main onto main
Squashed feature work from daemon_mode_b_main branch, rebased onto latest main to establish proper merge-base and clean PR diff. Original commits: - perf(core): F2 cleanup PR A — R9/W11/W12/R10 (post-merge follow-ups) (#4411) - refactor(acp-bridge): F1 test split — lift bridge.test.ts (6861 LOC) to acp-bridge (#4445) - fix(core): F2 cleanup PR B — self-heal observability (W133-a + W134) (#4460) - feat(sdk/daemon-ui): unified completeness follow-up to #4328 (#4353) - docs(serve): v0.16-alpha known limits + SDK QWEN_SERVER_TOKEN env fallback (PR 27) (#4473) - docs(deploy): local launch templates for v0.16-alpha (PR 30a) (#4483) - feat(daemon+sdk): cross-client real-time sync completeness (#4484) - feat(serve): add POST /session/:id/recap (#4504) - feat(daemon): add voterClientId to permission_resolved (A4) (#4539) - feat(serve): --allow-origin <pattern> CORS allowlist (T2.4 #4514) (#4527) - feat(daemon): in-session model switch reaches the bus (A1) (#4546) - feat(serve): prompt absolute deadline + SSE writer idle timeout (#4514 T2.9) (#4530) - Feat/daemon react cli (#4380)
1 parent 5aca042 commit 1284383

391 files changed

Lines changed: 70290 additions & 21419 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/pull_request_template.md

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,73 @@
11
<!--
2-
Maintainers prioritize PRs with a clear reviewer test plan — without it, review may be delayed.
2+
Help reviewers verify this PR quickly.
33
4-
Don't hard-wrap paragraphs: GitHub renders single newlines as <br>, so wrapped text shows as a narrow column. Write each paragraph or list item as one long line.
4+
Maintainers prioritize PRs that include clear proof of work.
5+
If a PR does not include enough validation detail to reproduce and verify the change efficiently, review may be delayed.
56
-->
67

7-
## What this PR does
8+
## Summary
89

9-
<!-- What this PR does. Describe the change in prose, not by file or function names. -->
10+
- What changed:
11+
- Why it changed:
12+
- Reviewer focus:
1013

11-
## Why it's needed
12-
13-
<!-- Why it's needed: the motivation, the problem being solved, or the user-facing benefit. -->
14-
15-
## Reviewer Test Plan
14+
## Validation
1615

1716
<!--
18-
How a reviewer can confirm this PR: reproduction steps, expected vs observed behavior, and evidence. CI runs on macOS, Windows, and Linux — Tested on is what you verified locally.
19-
20-
User-visible / TUI: Before/After with tmux-real-user-testing skill, screenshots, or a short recording.
21-
Non–user-visible (refactor, types, docs): commands and output below; write N/A under Before/After.
22-
-->
23-
24-
### How to verify
25-
26-
<!-- How you reproduced it and what a reviewer should confirm — steps if needed, expected vs observed behavior. Focus on outcomes; paste logs or test output when helpful. -->
27-
28-
### Evidence (Before & After)
29-
30-
<!-- User-visible / TUI changes: paste before-and-after screenshots, tmux logs, or video side by side. Non-UI changes (docs, refactor, types): N/A -->
17+
Be concrete. Do not write only "tested locally".
18+
Include the exact commands, prompts, outputs, logs, screenshots, or videos that prove the change was actually run and observed.
3119
32-
### Tested on
20+
For user-visible changes, bug fixes, CLI / TUI behavior changes, or interaction changes, include key screenshots or a short video.
21+
When possible, show before/after behavior.
3322
34-
| OS | Status |
35-
| :--------: | :----: |
36-
| 🍏 macOS | |
37-
| 🪟 Windows | |
38-
| 🐧 Linux | |
39-
40-
<!-- ✅ tested · ⚠️ not tested · N/A -->
41-
42-
### Environment (optional)
23+
If helpful, use the `e2e-testing` skill to gather stronger end-to-end validation evidence.
24+
-->
4325

44-
<!-- Local runtime: e.g. npm run dev, Docker/Podman sandbox, seatbelt. N/A if only unit tests. -->
26+
- Commands run:
27+
```bash
28+
# paste commands here
29+
```
30+
- Prompts / inputs used:
31+
- Expected result:
32+
- Observed result:
33+
- Quickest reviewer verification path:
34+
- Evidence (output, logs, screenshots, video, JSON, before/after, etc.):
4535

46-
## Risk & Scope
36+
## Scope / Risk
4737

4838
- Main risk or tradeoff:
49-
- Not validated / out of scope:
39+
- Not covered / not validated:
5040
- Breaking changes / migration notes:
5141

52-
## Linked Issues
42+
## Testing Matrix
5343

5444
<!--
55-
Closes #N / Fixes #N / Resolves #N to auto-close.
56-
Otherwise reference without a closing keyword.
45+
Use:
46+
- ✅ tested
47+
- ⚠️ not tested
48+
- N/A
49+
If anything is ⚠️, explain why briefly below.
5750
-->
5851

59-
<details>
60-
<summary>中文说明</summary>
52+
| | 🍏 | 🪟 | 🐧 |
53+
| -------- | --- | --- | --- |
54+
| npm run | ⚠️ | ⚠️ | ⚠️ |
55+
| npx | ⚠️ | ⚠️ | ⚠️ |
56+
| Docker | ⚠️ | ⚠️ | ⚠️ |
57+
| Podman | ⚠️ | N/A | N/A |
58+
| Seatbelt | ⚠️ | N/A | N/A |
59+
60+
Testing matrix notes:
61+
62+
-
63+
64+
## Linked Issues / Bugs
6165

6266
<!--
63-
完整翻译上面的英文正文,逐段对应,不要省略或缩写。PR 标题保持英文。
64-
-->
67+
If this PR fully resolves an issue, use one of:
68+
- Closes #<issue_number>
69+
- Fixes #<issue_number>
70+
- Resolves #<issue_number>
6571
66-
</details>
72+
Otherwise reference related issues without a closing keyword.
73+
-->

.github/workflows/release.yml

Lines changed: 178 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,63 @@ jobs:
386386
RELEASE_VERSION: '${{ needs.prepare.outputs.release_version }}'
387387
run: 'npm run package:standalone:release -- --version "${RELEASE_VERSION}" --out-dir dist/standalone'
388388

389+
- name: 'Verify Installation Release Assets'
390+
run: 'npm run verify:installation-release -- --dir dist/standalone'
391+
392+
- name: 'Package Hosted Installation Assets'
393+
env:
394+
RELEASE_VERSION: '${{ needs.prepare.outputs.release_version }}'
395+
run: 'npm run package:hosted-installation -- --out-dir dist/installation --version "${RELEASE_VERSION}"'
396+
397+
- name: 'Install ossutil'
398+
if: |-
399+
${{ needs.prepare.outputs.is_dry_run == 'false' }}
400+
env:
401+
OSSUTIL_URL: "${{ vars.OSSUTIL_URL || 'https://gosspublic.alicdn.com/ossutil/1.7.19/ossutil-v1.7.19-linux-amd64.zip' }}"
402+
OSSUTIL_SHA256: "${{ vars.OSSUTIL_SHA256 || 'dcc512e4a893e16bbee63bc769339d8e56b21744fd83c8212a9d8baf28767343' }}"
403+
run: |-
404+
set -euo pipefail
405+
406+
tmp_dir="$(mktemp -d)"
407+
curl -fsSL --connect-timeout 15 --max-time 300 "${OSSUTIL_URL}" -o "${tmp_dir}/ossutil.zip"
408+
echo "${OSSUTIL_SHA256} ${tmp_dir}/ossutil.zip" | sha256sum -c -
409+
unzip -q "${tmp_dir}/ossutil.zip" -d "${tmp_dir}"
410+
411+
ossutil_path="$(find "${tmp_dir}" -type f \( -name 'ossutil' -o -name 'ossutil64' \) -print -quit)"
412+
if [[ -z "${ossutil_path}" ]]; then
413+
echo "::error::ossutil binary not found in downloaded archive"
414+
exit 1
415+
fi
416+
417+
chmod +x "${ossutil_path}"
418+
mkdir -p "${HOME}/.local/bin"
419+
install -m 0755 "${ossutil_path}" "${HOME}/.local/bin/ossutil"
420+
echo "${HOME}/.local/bin" >> "${GITHUB_PATH}"
421+
rm -rf "${tmp_dir}"
422+
"${HOME}/.local/bin/ossutil" >/dev/null
423+
424+
- name: 'Configure Aliyun OSS Credentials'
425+
if: |-
426+
${{ needs.prepare.outputs.is_dry_run == 'false' }}
427+
env:
428+
ALIYUN_OSS_ACCESS_KEY_ID: '${{ secrets.ALIYUN_OSS_ACCESS_KEY_ID }}'
429+
ALIYUN_OSS_ACCESS_KEY_SECRET: '${{ secrets.ALIYUN_OSS_ACCESS_KEY_SECRET }}'
430+
ALIYUN_OSS_ENDPOINT: "${{ vars.ALIYUN_OSS_ENDPOINT || 'https://oss-cn-hangzhou.aliyuncs.com' }}"
431+
run: |-
432+
set -euo pipefail
433+
434+
if [[ -z "${ALIYUN_OSS_ACCESS_KEY_ID}" || -z "${ALIYUN_OSS_ACCESS_KEY_SECRET}" ]]; then
435+
echo "::error::Missing Aliyun OSS credentials. Set ALIYUN_OSS_ACCESS_KEY_ID and ALIYUN_OSS_ACCESS_KEY_SECRET in the production-release environment secrets."
436+
exit 1
437+
fi
438+
439+
ossutil config \
440+
-e "${ALIYUN_OSS_ENDPOINT}" \
441+
-i "${ALIYUN_OSS_ACCESS_KEY_ID}" \
442+
-k "${ALIYUN_OSS_ACCESS_KEY_SECRET}" \
443+
-L EN \
444+
-c "${RUNNER_TEMP}/.ossutilconfig"
445+
389446
- name: 'Publish @qwen-code/qwen-code'
390447
working-directory: 'dist'
391448
run: |-
@@ -400,37 +457,150 @@ jobs:
400457
env:
401458
NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
402459

403-
- name: 'Verify Standalone Archives'
404-
run: |-
405-
npm run verify:installation-release -- --dir dist/standalone
406-
407460
- name: 'Create GitHub Release and Tag'
408461
if: |-
409462
${{ needs.prepare.outputs.is_dry_run == 'false' }}
410463
env:
411-
# CI_BOT_PAT required: GITHUB_TOKEN events cannot trigger downstream workflows (sync-release-to-oss.yml).
412-
GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}'
464+
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
413465
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
414466
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
415467
PREVIOUS_RELEASE_TAG: '${{ needs.prepare.outputs.previous_release_tag }}'
416468
IS_NIGHTLY: '${{ needs.prepare.outputs.is_nightly }}'
417469
IS_PREVIEW: '${{ needs.prepare.outputs.is_preview }}'
418470
run: |-
471+
set -euo pipefail
472+
419473
PRERELEASE_FLAG=""
420474
if [[ "${IS_NIGHTLY}" == "true" || "${IS_PREVIEW}" == "true" ]]; then
421475
PRERELEASE_FLAG="--prerelease"
422476
fi
423477
478+
mapfile -t release_assets < <(node scripts/verify-installation-release.js --dir dist/standalone --list-release-asset-paths)
479+
424480
gh release create "${RELEASE_TAG}" \
425481
dist/cli.js \
426-
dist/standalone/qwen-code-* \
427-
dist/standalone/SHA256SUMS \
482+
"${release_assets[@]}" \
428483
--target "${RELEASE_BRANCH}" \
429484
--title "Release ${RELEASE_TAG}" \
430485
--notes-start-tag "${PREVIOUS_RELEASE_TAG}" \
431486
--generate-notes \
432487
${PRERELEASE_FLAG}
433488
489+
- name: 'Sync Release Assets to Aliyun OSS'
490+
if: |-
491+
${{ needs.prepare.outputs.is_dry_run == 'false' }}
492+
env:
493+
ALIYUN_OSS_BUCKET: "${{ vars.ALIYUN_OSS_BUCKET || 'qwen-code-assets' }}"
494+
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
495+
run: |-
496+
set -euo pipefail
497+
498+
mapfile -t release_assets < <(node scripts/verify-installation-release.js --dir dist/standalone --list-release-asset-paths)
499+
node scripts/upload-aliyun-oss-assets.js \
500+
--bucket "${ALIYUN_OSS_BUCKET}" \
501+
--config "${RUNNER_TEMP}/.ossutilconfig" \
502+
--prefix "releases/qwen-code/${RELEASE_TAG}" \
503+
"${release_assets[@]}"
504+
505+
- name: 'Verify Aliyun OSS Release Assets'
506+
if: |-
507+
${{ needs.prepare.outputs.is_dry_run == 'false' }}
508+
env:
509+
ALIYUN_OSS_PUBLIC_BASE_URL: "${{ vars.ALIYUN_OSS_PUBLIC_BASE_URL || 'https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com' }}"
510+
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
511+
run: |-
512+
set -euo pipefail
513+
514+
npm run verify:installation-release -- --base-url "${ALIYUN_OSS_PUBLIC_BASE_URL}/releases/qwen-code/${RELEASE_TAG}"
515+
516+
- name: 'Sync Hosted Installation Assets to Aliyun OSS'
517+
if: |-
518+
${{ needs.prepare.outputs.is_dry_run == 'false' && needs.prepare.outputs.is_nightly == 'false' && needs.prepare.outputs.is_preview == 'false' }}
519+
env:
520+
ALIYUN_OSS_BUCKET: "${{ vars.ALIYUN_OSS_BUCKET || 'qwen-code-assets' }}"
521+
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
522+
run: |-
523+
set -euo pipefail
524+
525+
hosted_assets=(
526+
dist/installation/install-qwen-standalone.sh
527+
dist/installation/install-qwen-standalone.ps1
528+
dist/installation/install-qwen-standalone.bat
529+
dist/installation/uninstall-qwen-standalone.sh
530+
dist/installation/uninstall-qwen-standalone.ps1
531+
dist/installation/SHA256SUMS
532+
)
533+
node scripts/upload-aliyun-oss-assets.js \
534+
--bucket "${ALIYUN_OSS_BUCKET}" \
535+
--config "${RUNNER_TEMP}/.ossutilconfig" \
536+
--prefix "installation/${RELEASE_TAG}" \
537+
"${hosted_assets[@]}"
538+
node scripts/upload-aliyun-oss-assets.js \
539+
--bucket "${ALIYUN_OSS_BUCKET}" \
540+
--config "${RUNNER_TEMP}/.ossutilconfig" \
541+
--prefix "installation" \
542+
"${hosted_assets[@]}"
543+
544+
- name: 'Verify Aliyun OSS Hosted Installation Assets'
545+
if: |-
546+
${{ needs.prepare.outputs.is_dry_run == 'false' && needs.prepare.outputs.is_nightly == 'false' && needs.prepare.outputs.is_preview == 'false' }}
547+
env:
548+
ALIYUN_OSS_PUBLIC_BASE_URL: "${{ vars.ALIYUN_OSS_PUBLIC_BASE_URL || 'https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com' }}"
549+
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
550+
run: |-
551+
set -euo pipefail
552+
553+
hosted_tmp_dir="$(mktemp -d)"
554+
trap 'rm -rf "${hosted_tmp_dir}"' EXIT
555+
mkdir -p "${hosted_tmp_dir}/versioned" "${hosted_tmp_dir}/global"
556+
for asset in install-qwen-standalone.sh install-qwen-standalone.ps1 install-qwen-standalone.bat uninstall-qwen-standalone.sh uninstall-qwen-standalone.ps1 SHA256SUMS; do
557+
url="${ALIYUN_OSS_PUBLIC_BASE_URL}/installation/${RELEASE_TAG}/${asset}"
558+
global_url="${ALIYUN_OSS_PUBLIC_BASE_URL}/installation/${asset}"
559+
curl -fsSL --connect-timeout 15 --max-time 300 "${url}" -o "${hosted_tmp_dir}/versioned/${asset}"
560+
curl -fsSL --connect-timeout 15 --max-time 300 "${global_url}" -o "${hosted_tmp_dir}/global/${asset}"
561+
done
562+
cmp -s "dist/installation/SHA256SUMS" "${hosted_tmp_dir}/versioned/SHA256SUMS" || {
563+
echo "::error::Hosted installation SHA256SUMS does not match local dist/installation/SHA256SUMS"
564+
diff -u "dist/installation/SHA256SUMS" "${hosted_tmp_dir}/versioned/SHA256SUMS" || true
565+
exit 1
566+
}
567+
cmp -s "dist/installation/SHA256SUMS" "${hosted_tmp_dir}/global/SHA256SUMS" || {
568+
echo "::error::Global hosted installation SHA256SUMS does not match local dist/installation/SHA256SUMS"
569+
diff -u "dist/installation/SHA256SUMS" "${hosted_tmp_dir}/global/SHA256SUMS" || true
570+
exit 1
571+
}
572+
(cd "${hosted_tmp_dir}/versioned" && sha256sum -c SHA256SUMS)
573+
(cd "${hosted_tmp_dir}/global" && sha256sum -c SHA256SUMS)
574+
575+
- name: 'Publish Aliyun OSS Latest VERSION'
576+
# Run last so the `latest/VERSION` pointer only flips after every
577+
# release asset and hosted installer object has been uploaded and
578+
# verified. If any earlier step fails, the pointer keeps referring
579+
# to the previously-good release.
580+
if: |-
581+
${{ needs.prepare.outputs.is_dry_run == 'false' && needs.prepare.outputs.is_nightly == 'false' && needs.prepare.outputs.is_preview == 'false' }}
582+
env:
583+
ALIYUN_OSS_BUCKET: "${{ vars.ALIYUN_OSS_BUCKET || 'qwen-code-assets' }}"
584+
ALIYUN_OSS_PUBLIC_BASE_URL: "${{ vars.ALIYUN_OSS_PUBLIC_BASE_URL || 'https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com' }}"
585+
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
586+
run: |-
587+
set -euo pipefail
588+
589+
printf '%s\n' "${RELEASE_TAG}" > "${RUNNER_TEMP}/qwen-code-latest-version"
590+
ossutil cp "${RUNNER_TEMP}/qwen-code-latest-version" "oss://${ALIYUN_OSS_BUCKET}/releases/qwen-code/latest/VERSION" -c "${RUNNER_TEMP}/.ossutilconfig" -f --acl public-read
591+
592+
latest_version="$(curl -fsSL --connect-timeout 15 --max-time 300 "${ALIYUN_OSS_PUBLIC_BASE_URL}/releases/qwen-code/latest/VERSION" | tr -d '[:space:]')"
593+
if [[ "${latest_version}" != "${RELEASE_TAG}" ]]; then
594+
echo "::error::Aliyun latest VERSION points to ${latest_version}, expected ${RELEASE_TAG}"
595+
exit 1
596+
fi
597+
598+
- name: 'Cleanup Aliyun OSS Credentials'
599+
if: |-
600+
${{ always() && needs.prepare.outputs.is_dry_run == 'false' }}
601+
run: |-
602+
rm -f "${RUNNER_TEMP}/.ossutilconfig"
603+
434604
- name: 'Create PR to merge release branch into main'
435605
if: |-
436606
${{ needs.prepare.outputs.is_dry_run == 'false' && needs.prepare.outputs.is_nightly == 'false' && needs.prepare.outputs.is_preview == 'false' }}

0 commit comments

Comments
 (0)