5 Commits
Author SHA1 Message Date
8679b7ca75 fix(managed-agent): e2e runner and image robustness from the #12692 R2 review (#13344)
* fix(managed-agent): e2e runner and image robustness from the #12692 R2 review

Six R2 review follow-ups on #12692, re-verified against the current
script (post-#13263) before patching:

- waitUntil surfaces the last predicate error in its timeout message
  instead of swallowing it, and every iteration is bounded by the
  remaining deadline so a hung predicate can no longer outlive
  timeoutMs several-fold.
- crashProcess refuses to kill a child that already died BY SIGNAL
  (exitCode stays null but signalCode is set), instead of throwing a
  raw ESRCH that hides the descriptive pre-exit diagnostic.
- An interrupt delivered after the last poll now exits 1 with the
  signal name; success JSON can no longer appear for a run the
  operator explicitly stopped.
- runMysql and mysqladmin pass --no-defaults first, so a developer's
  ~/.my.cnf client options cannot auth-fail the scratch
  empty-password server (both mysqld invocations already did).
- The packaged-jar lookup is version-independent in both the runner
  (exactly-one jar assertion over target/) and the Dockerfile COPY
  (wildcard that fails on zero or multiple matches).
- The image listens beyond loopback by default: ENV
  QWEN_MANAGED_AGENT_SERVER_ADDRESS=0.0.0.0 and
  QWEN_MANAGED_AGENT_RUNTIME_BROKER_HOST=0.0.0.0, so a documented
  docker run -p no longer refuses traffic; the application.yml
  loopback defaults stay host-appropriate.

* fix(scripts-e2e): unbreak the failover lanes from the #13344 review

Three findings on the R2-fix follow-up, each verified against the CI
failure on hosted-harness-mysql before patching:

- The packaged-jar lookup assumed exactly one qwen-managed-agent-server-*.jar
  in target/, but the pom attaches classified repackage executions
  (workspace-bundle, operator-recovery) besides the unclassified server
  jar, so the cardinality check could never pass and every
  test:e2e:managed-*-failover step died at module scope. The script now
  reads the project <version> from the pom — the one following the
  module's own <artifactId>, not the <parent> block's — and names the
  unclassified jar exactly.
- The Dockerfile wildcard COPY fails the image build outright: multiple
  matches into a file destination is a Docker error, and no workflow
  builds this image to catch it. The build stage now selects the
  unclassified jar by classifier exclusion, fails unless exactly one
  exists, and publishes it under the fixed name the runtime stage COPYs.
- waitUntil's stall timer was discarded live: a fast success idled the
  runner up to the largest timeout after the success JSON. The timer is
  unref'd.

Script suite 14/14 green (new pins: pom-version jar naming, the
classifier-exclusion line, stallTimer.unref()); Prettier and ESLint
clean. The pom regex and the Dockerfile selection loop were both
executed locally against a three-jar target before committing.

* fix(scripts-e2e): harden the image guard and record the publish consequence

Second review round on the jar/bind changes, each verified before patching:

- The glob default arm trusted sh's non-nullglob behaviour: with zero
  matches the pattern itself reaches the counting arm, passes count=1
  vacuously, and the build dies at cp with a glob in the message. The
  loop now breaks on a non-existent match candidate and the guard names
  the expectation loudly (expected exactly one unclassified server jar,
  found N).
- The wide bind was necessary (-p DNATs to the bridge IP) but its
  consequence went unrecorded while README.md:6-8 still says the server
  listens on the loopback deliberately: the API has no HTTP
  authentication, so tenancy is whatever X-Qwen-Tenant-Id says, and
  docker run -p on a shared host publishes that surface. README's
  container section and the Dockerfile ENV comment now say this
  plainly; the pre-existing private-ingress gate stays with the wider
  lifecycle deferrals (#13302 family).
- Two pins watch the bind and the guard in the script suite (both go
  red against the old text); the Reviewer Test Plan again matches the
  current head.

Suite 15/15 green; Prettier applied.

* fix(scripts-e2e): close the second round's eight follow-ups

Each verified against the runner before patching:

- The synthetic 'predicate stalled' rejection no longer overwrites the
  real predicate error; undici's cause (ECONNREFUSED) is what the
  diagnostic surfaces instead of 'fetch failed'.
- MySQL client invocations run with an isolated empty HOME: MySQL
  honours ~/.mylogin.cnf even with option files disabled, so a
  developer's mysql_config_editor credential would otherwise defeat
  the flag at exactly the scratch empty-password servers it was for.
  spawnSync probes carry their own timeouts, as a sync-blocking call
  prevents the race deadline from firing inside an iteration.
- One 'has this child exited' spelling (childExited) replaces the four
  drifted phrasings that produced the original ESRCH.
- The AST extractor exists once; the port-allocation test calls the
  shared helper instead of maintaining a second copy.
- The two surfaces that pick the server jar are named similarly in
  test intent (pom-derived exact name in the runner, classifier
  exclusion with a loud guard in the Dockerfile) so their ownership
  no longer contradicts, and the pom regex is executed against the
  real pom in the suite rather than only grepped.

Suite 16/16 green; ESLint clean; Prettier applied.

* fix(scripts-e2e): opt the image bind in explicitly and close round three

Third review round, each finding reproduced or probed before patching:

- The published image no longer binds the managed-agent control plane
  on 0.0.0.0 by default (R1-1): the module has no HTTP authentication,
  so the ENV pair published a tenant API anyone routing to the port
  could drive. The image keeps the application.yml loopback defaults;
  publishing is now an explicit docker run -e opt-in documented in the
  README's container section, including the no--p bridge exposure.
- .dockerignore excludes target/ in both spellings, so a developer's
  stale Maven output cannot ride the build context into the stage the
  jar cardinality guard inspects.
- waitUntil: the stall sentinel is compared by identity and recorded
  only when no real predicate error was seen; an answered (falsy)
  iteration clears a stale earlier error; and the remaining budget is
  handed to the predicate so synchronous probes bound themselves.
- runMysql surfaces the spawn-level reason (a timeout no longer reads
  as a bare 'MySQL command failed: '), and the isolated MySQL client
  HOME hangs off the runner's one reclaimed scratch root.
- Tests: extraction lists name childExited so the early-exit branch is
  reachable; the pom regex is lifted from the runner source and pinned
  by shape instead of a copied literal (a version bump stays green);
  the double transpile is gone.

Suite 23/23 green (7 existing + 16 added by this PR); build, typecheck
and lint clean; 14 mutation probes each red their witness.

* fix(scripts-e2e): floor the last MySQL probe and close round four (#13344)

Fourth review round, each finding reproduced or probed before patching:

- R4-1 (Critical): the container-publishing recipe named
  QWEN_MANAGED_AGENT_RUNTIME_BROKER_HOST=0.0.0.0 without
  ALLOW_NON_LOOPBACK=true, which the broker requires for a non-loopback
  bind — an operator enabling the broker got a crash-looping container.
  Dropped the broker line from the README recipe and the Dockerfile
  comment and pointed at the Broker deployment section; publishing the
  API never needs the broker's HTTP face. A pairing pin now reds any
  documented broker wildcard bind that omits the flag.
- R1-8: waitUntil handed the final poll a remaining budget below one
  client round trip, so the deadline message named a wedged client when
  the lease was simply unexpired. runMysql floors its spawn timeout at
  2s, re-admitting up to ~2s of overshoot per wedged poll against the
  ~10s the budget threading removed.
- R3-1: the mysqlClientHome pin was a whole-file count, blind to a
  fifth client invocation without the override. Assert per call site
  over the runner's AST: every spawnSync mysql/mysqladmin/mysqld passes
  --no-defaults, and the mysql/mysqladmin clients use the isolated HOME
  (the mysqld launches deliberately keep the real one).
- R3-3: the loopback-by-default guarantee is pinned at application.yml's
  own default, not only at the Dockerfile declining to override it.
- R3-2: the waitUntil extraction list lived in six pasted copies; one
  WAIT_UNTIL_DEPS inventory with one loadWaitUntil() loader now serves
  all six, and the loader takes injected new Function parameters (used
  by the crashProcess and runMysql cases).
- R4-2: the jar-selection guard is exercised as shell — the RUN stage's
  own text run under a probed sh against zero/one/three/two-unclassified
  fixture target/ populations, asserting exit status and message.

Suite 24/24 green (7 existing + 17 added by this PR); build, typecheck
and lint clean; seven mutation probes red their witnesses, with the
guarded broker variant staying green as the control.

* fix(scripts-e2e): gate the jar guard on cp and sharpen the MySQL scan (#13344)

Fifth review round, each finding probed before patching:

- R4-2 (Critical): the executable jar-guard case gated on sh alone,
  but its fixture ends in cp — on a Git-Bash-only Windows PATH sh.exe
  resolves without the coreutils, so the merge-queue lane would red at
  the status assertions, and where sh is absent the in-body return
  recorded a pass for a guard that never ran. The case now probes the
  capability it consumes (sh -c 'command -v cp') and skipIf-gates, so
  a host without either reports skipped, matching the repo's own
  hasSha256sum probe spelling.
- R3-1: the per-call-site scan matched spawnSync only, missing the
  long-lived start(mysqld, ...) launch (3 of the 4 MySQL process
  launches); a collapsed population still passed the lower-bound
  assertion; and toContain pinned the flag's presence, not its
  position, though MySQL honors --no-defaults only as the first
  option. The scan now covers spawnSync/spawn/start, asserts the
  exact four-call inventory, and pins --no-defaults as each call's
  first array element.
- R1-8: the floor pin exercised only the clamp side; the
  default-timeout call now also pins that a budget above the floor
  reaches spawnSync unchanged (10_000), so a Math.max-to-constant
  regression reds the case.

R1-12 stays open for a maintainer: it asks for a PR-body edit (the
stale Image bind bullet and the 15/15 count), which this loop cannot
perform — it has no GitHub write credentials.

Suite 24/24 green; build, typecheck, lint and prettier clean; six
mutation probes red their witnesses (clamp-to-constant; --no-defaults
dropped from the start() launch; a fifth unprotected client; the flag
moved off index 0; cardinality guard disabled; a cp-less PATH reports
the jar-guard case skipped).

* fix(scripts-e2e): document a bootable publish recipe and close round five (#13344)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(scripts-e2e): boot the documented container recipe and pin the round-two gaps (#13344)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(scripts-e2e): pass the recipe's datasource password valueless and pin the round-three gaps (#13344)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(scripts-e2e): name both valueless credentials in the publish recipe's export instruction (#13344)

* fix(scripts-e2e): cover the third pom classifier in the image jar guard (#13344)

The base-conflict merge from main added the workspace-migration repackage
classifier to the pom while the Dockerfile's exclusion list stayed at two
arms, so the cardinality guard counted two unclassified jars and the
documented image build failed at the RUN stage with no CI lane building
the image to catch it. Add the missing arm, extend the shell-fixture
populations with the real four-artifact target/ layout, and derive the
exclusion set from the pom's own <classifier> elements in the suite so a
fourth classifier reddens the Test job instead of an operator's docker
build. The three prose copies of the artifact count are retired rather
than renumbered, so they cannot drift again.

Also close the two remaining round-one test-hardening findings: the
port-allocation test now loads through the shared load() helper instead
of a second local new Function spelling, and the image ENV pins anchor on
the instruction (case-insensitive, leading whitespace tolerated) rather
than the conventional uppercase column-0 spelling.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(scripts-e2e): defer the freeze wake record and name stalls in waitUntil errors (#13344)

- The --freeze arm printed its fencedFormerWriter record eagerly inside
  the try, so a SIGINT right after it left a success-shaped document on
  stdout for a run that exits 1, and the record flipped to before the
  main result. Defer it like resultJson and print it after the main
  result, restoring the base ordering.
- When the deadline's own poll stalled, waitUntil reported a stale
  earlier error as the final state. The message now reads "predicate
  stalled; last error before the stall: ..." so both facts survive.
- README: the documented MySQL grants did not boot the server (Flyway's
  performance_schema.user_variables_by_thread probe is denied with
  ERROR 1142 and startup stops at 'foreign_key_checks'); add the SELECT
  grant to the one-liner and mention it in the container section.

* fix(scripts-e2e): floor the waitUntil stall bound so a final in-flight poll can answer (#13344)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(scripts-e2e): print the README's full grant set in the dev recipe hint (#13344)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(scripts-e2e): re-key the in-flight poll test off the clock for re-entry margin (#13344)

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: qwen-code[bot] <qwen-code[bot]@users.noreply.github.com>
2026-10-11 16:42:22 +00:00
078b924989 refactor(ci): extract release workflow scripts (#11165)
* refactor(ci): extract release workflow scripts

* test(ci): pin release-note labeling to the extracted step script

release.yml no longer inlines the auto-labeling body: the publish step now
just dispatches to run-release-step.sh, which owns the git rev-list
enumeration and the classify-release-notes.mjs call. The wiring test kept
asserting those four strings against the step's run block, so it failed on
the dispatcher path alone.

Read run-release-step.sh and assert the labeling body there, and assert the
dispatch from release.yml. The workflow-level guarantees (step name,
continue-on-error, GITHUB_TOKEN env, issues/pull-requests write permissions)
and the .github/release.yml exclusion entry are unchanged.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmtpmlcodmz

* test(ci): repoint release assertions at the extracted step script

The extraction moved the GitHub Release notes, standalone archive, and
release-failure autofix label logic out of .github/workflows/release.yml
into .github/scripts/run-release-step.sh, but three consumer suites still
pinned the moved text in the YAML, so `npm run test:scripts` was red and
the release workflow's fail-closed `quality` gate would refuse to publish.

Read the step script in ai-release-notes-workflow, install-script, and
qwen-autofix-workflow and assert the moved strings there with their new
lowercase locals (`notes_args`/`notes_file`), keeping on release.yml only
what genuinely stayed in it. Restore the "Safe to auto-apply approval"
rationale above both AUTOFIX_APPROVED_LABEL writes in the notify-failure
arm; it is the only recorded justification for the workflow granting
itself autonomous autofix approval.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtq0vr74ni

* test(ci): derive the ECS wipe subject set from the runner, not the wipe

Selecting checkout jobs by "has a Restore workspace ownership step" made
the subject set a function of the property under test, so a job added to
the ecs-qwen-hk4-host pool with an actions/checkout and no wipe was
filtered out before any assertion ran. Key the exemption on the pool
marker instead, and pin that every pool-routed job carries the wipe
whether or not it checks out.

The hosted label cannot be the discriminator: every pool-routed runs-on
expression names 'ubuntu-latest' as its fallback branch.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtq0vr74ni

* test(ci): re-scope the release-creation token pin to its own step

The workflow-wide toContain was satisfied by the second CI_BOT_PAT
occurrence on "Trigger ECS runner qwen update", so flipping the token on
"Create GitHub Release and Tag" to github.token left every lane green.
Restore the step scoping and the deleted rationale comment in release.yml.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtqfvvmto2

* fix(ci): fail the release step runner closed on pipeline legs

run-release-step.sh set bare `set -e` while both sibling runners set
pipefail. In notify-failure a connection-level gh failure inside
`gh issue list ... | jq -c ...` leaves jq exiting 0 on empty input, so the
empty result took the create-a-new-issue branch and filed a duplicate
autofix/approved release-failure issue past all three reuse guards.

Pin the flag next to the existing sibling pin and add a behavioural row for
the notify-failure arm with an unreachable gh.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtqfvvmto2

* test(ci): relocate the release timeout value-format contract

This PR condensed the nine-line release.yml lane comment to "values are
minutes" and relocated the format half nowhere: grep for the deleted claims
(expression error / free text / truthy / leading number) found no copy under
.github/, scripts/tests/ or docs/. Put the contract beside the assertions that
already pin the three tunable lanes' expressions, since release.yml is
size-pinned twice.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtqfvvmto2

* docs(ci): scope the workflow_sha claim to the extracted helpers

The design doc said the trusted checkout "prevents an operator-selected
release ref from supplying code that receives release credentials". Two paths
at HEAD contradict that reading: resolve-version executes the ref's
scripts/get-release-version.js under the job token (run-release-step.sh:84),
and push-release-branch commits with core.hooksPath .husky (:116, :130) in a
step whose env carries CI_BOT_PAT (release.yml:659). Both predate this
extraction, so name them as residual paths rather than as prevented ones.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtqfvvmto2

* fix(ci): format the release-version test to satisfy the Prettier gate

`Lint & Static` failed at the `Run Prettier` step: a continuation line in
the `assert-unreleased` format-gate test carried four extra spaces.

* fix(ci): exclude nested .git dirs from the docker build context

The extraction added a `.release-workflow/` checkout to `integration_docker`
between the sandbox build's reset and the build itself, and `.dockerignore`
listed `.git` only in its root-anchored form. A depth-1 sparse checkout of
the pinned ref is 1.1M of worktree over 51M of git objects, so every
uncached sandbox image baked those objects into a discarded builder layer on
the shared pool this job is routed to. `node_modules` and `dist` already
carry both forms.

* fix(release): close four gaps the script extraction left behind

R1-15: the push-time guard returned exit 2 for a malformed version, and
run-release-step.sh treats 2 as a transient probe failure — so a refusal
that can never succeed was retried three times over ~45s and logged as a
connectivity problem. Malformed versions now exit 4, which the retry loop
breaks on immediately and which is not the exit-3 version_refusal marker.

R1-16: the extraction moved the guard behind `.release-workflow/` so the
decision comes from the workflow-pinned SHA, but left a working
`--assert-unreleased` dispatch in the ref-supplied get-release-version.js.
A future step using that spelling would load the selected ref's guard.
Removed it, and re-pointed its five tests at the real entry point.

R1-13: assert-release-version.mjs ships alone under release.yml's
sparse-checkout set, so it cannot import release-helpers.js — the
duplicated isExpectedMissingGitHubRelease is deliberate. Documented that
at both sites and pinned the two bodies identical, so widening only the
guard's copy (which would read a rate-limited probe as "release absent")
goes red.

R1-6: restored the notify-failure arm's rationale, matching the condensed
comments the other five arms kept: the exact-title re-check that stops a
v0.18.1 failure reusing a v0.18.10 issue, the bot-author preference, and
the re-query that keeps autofix off a maintainer-owned issue.

* fix(release): re-pin the trusted runner before every step that runs it

R1-10: `Build Bundle and Prepare Package`, `Build Standalone Archives` and
`Verify Standalone Archives` executed `.release-workflow/.../run-release-step.sh`
from a tree that selected-ref npm code had already run over — `build-package`
for the first two, and `npm publish`'s own lifecycle scripts for the third,
inside the job that holds `id-token: 'write'`. At the merge base these were
inline `run:` bodies sourced from the workflow file, immune to anything
written into the working tree, so the exposure is introduced by the
extraction. Each now gets the reset-and-re-checkout pair the design doc
already promises for credential-bearing steps.

The guarding test hand-listed six protected step names, so it could not fail
for the three it omitted. It now sweeps every step whose `run` mentions
`.release-workflow/` (17 today) and separately pins that each re-checkout is
preceded by a reset.

R1-5: nothing tied the publish allowlist to the guard's `PUBLISHED_PACKAGES`
except a comment pointing at publish steps this PR deleted from release.yml.
A channel added to the loop but not the array would ship unprobed, so a retry
of a partial release reports "unreleased" and force-pushes over the tip the
shipped package anchors to. The two sets are now pinned against each other,
and the stale comment points at the real pin.

The three inserted step pairs grow release.yml by 294 bytes (well inside the
4096-byte allowance); the ratchet baseline and the PR description move with
it rather than letting the recorded number drift.

* test(release): pin the flags, arms and modes the extraction left unwitnessed

R1-19: the arm's only executable probe stubbed npm with a script that
recorded `$PWD` and discarded every argument, so deleting `--tag=${NPM_TAG}`
stayed green while `npm publish` would fall back to the `latest` dist-tag —
handing the 21:00 UTC nightly to every end-user install and to the ECS fleet
updater. The stub now records the argv and each publish is checked for
`--access public` and the dist-tag.

R2-3: `resolve-version` was the one arm no test executed or text-pinned;
flipping `--type=preview` to `--type=stable` survived all 2233 scripts tests,
while at runtime the stable path ignores `preview_version_override` and would
publish an operator's manual preview as a stable release. Two execution tests
now cover the preview mapping and the malformed-version rejection; both were
run against the real arm before pushing.

R1-18: release.yml invokes these scripts by bare path, so the executable bit
is load-bearing, but every test invokes them as `bash <script>`, which
ignores the mode — and the workflow triggers only on schedule/dispatch, so no
pull-request lane runs the bare-path form. A mode-normalizing commit would
keep the suite green and kill `set-flags` with exit 126. The recorded git
mode is now pinned for all three scripts.

* test(release): pin the trusted prefix, the fork gate and the real version formats

R1-3: every arm pin elsewhere is a substring match that a bare
`.github/scripts/run-release-step.sh` invocation would satisfy just as well,
so nothing pinned the mechanism the extraction exists for. All 17 step
invocations are now required to carry the `.release-workflow/` prefix and
forbidden from naming the release-ref copy.

R1-9: `PUBLISH_AUDIO_CAPTURE` replaced a deleted step-level
`if: github.repository == 'QwenLM/qwen-code'` gate and, unlike its
`PUBLISH_EXTERNAL_CONTEXT_MEM0` sibling, had neither its gate expression nor
its true branch pinned — a fork running this workflow would publish
@qwen-code/audio-capture. Both halves are pinned now.

R1-14: the format gate is the guard's only genuinely new behaviour and its
accept path was exercised solely with '1.2.3', while the two scheduled
releases produce nightly and preview strings. Both formats are now asserted,
so tightening the pattern fails here rather than at 21:00 UTC.

* fix(release): keep failure reporting alive when the trusted runner is not

R1-11: `notify_failure` gained a checkout of `github.workflow_sha` as its
first step, and its whole body now lives in a shell script. At base the job
had no checkout at all and the notifier was inline, so the job whose only
purpose is reporting every other job's failure acquired two ways to fail
silently: a degraded git backend takes out the checkout, and a syntax error
or dropped exec bit in run-release-step.sh takes out every arm including
notify-failure. In both cases `Create Issue on Failure` carried the default
`if: success()` and was skipped — no issue, no autofix label, no dispatch,
and the scheduled autofix fallback scans issues, so nothing recovered it.

Rather than moving 105 lines of notifier back into the workflow — which the
orchestration guard in this PR forbids at 12 run-lines per step — the primary
path keeps the extraction and stops being load-bearing: it runs under
`always()`, absorbs its own failure, and a five-line inline fallback files a
plain issue whenever it did not succeed. The fallback depends on neither the
checkout nor the script, does no reuse, labelling or dispatch, and accepts a
duplicate issue as the cost of never going silent.

The workflow's line ceiling moves 800 -> 830 for those lines, with the reason
recorded at the assertion; the per-step run cap that keeps logic out of the
YAML is unchanged and still holds. Ratchet baseline and the PR description
move with the file.

* style(release): apply the pinned Prettier to the fallback notifier

Prettier 3.6.1 normalizes the quoting on the new `if:` and drops a
trailing space; the ratchet baseline follows the two-byte change.

* docs(release): name exit 4 where the guard's exit codes are described

Adding the malformed-version code left two descriptions behind: the retry
loop had no statement of the contract at all, and the test comment beside it
still said exit 0 and exit 3 were the only decisive outcomes.

* fix(release): avoid duplicate fallback failure issues

---------

Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: yiliang114 <[email protected]>
2026-09-08 07:09:09 +00:00
tanzhenxinandQwen-Coder cceac6093e fix(cli): skip stdin read for ACP mode
- Extend stdin handling to skip reading for ACP mode, similar to stream-json
- Remove duplicate line in .dockerignore

ACP mode passes protocol data via stdin that should be forwarded to the
sandbox intact, not consumed as a user prompt.

Co-authored-by: Qwen-Coder <[email protected]>
2026-03-27 11:47:01 +00:00
tanzhenxinandQwen-Coder a806c8aaf6 chore(docker): ignore tsconfig.tsbuildinfo files
Add tsconfig.tsbuildinfo pattern to .dockerignore to exclude
TypeScript incremental compilation cache files from Docker builds.

Co-authored-by: Qwen-Coder <[email protected]>
2026-03-27 10:11:17 +00:00
tanzhenxinandQwen-Coder 9e4c5ee891 refactor: Extract web-templates package and unify build/pack workflow
Moves export-html and insight templates from cli/assets to a new
dedicated web-templates package. Updates Dockerfile and build scripts
to use consolidated bundle/prepare:package/pack workflow.

Co-authored-by: Qwen-Coder <[email protected]>
2026-02-26 21:02:46 +08:00