qwen-code/.gitignore
顾盼 829385a14e
feat(qwen-live): protocol v7 playback receipts and interactive init — M5 (#10769)
* feat(qwen-live): interactive init command with agent detection

`qwen-live init` walks a new user through setup:

- Scans PATH for supported coding agents (qodercli, qwen, gemini,
  claude, codex) via `--version` probe, resolving each to an absolute
  path. Native agents (qodercli/qwen/gemini) use their resolved binary
  directly; adapter-based agents (claude/codex) configure npx to run
  the ACP adapter, passing the detected binary via env (CODEX_PATH).
- Interactive prompts (prompts library) let the user pick a default
  backend, add additional backends, enter their DashScope API key
  (with env-var reuse option), and set a working directory.
- macOS: checks the Live Host app via LiveHostInstaller.refresh();
  offers to install if missing. Non-macOS: informs the user the Host
  is required.
- Writes ~/.qwen-live/config.json atomically (tmp + rename) with
  user-only permissions (0o600).

Subcommand dispatch in index.ts: `qwen-live init` runs the wizard,
everything else starts the daemon.

* feat(qwen-live,live-host): protocol v7 playback receipts

The injector's playback window no longer guesses from byte counts;
the Host app sends real playback_started/completed events.

Protocol:
- LIVE_HOST_PROTOCOL_VERSION 6 → 7 (types.ts both copies +
  live-host shared/protocol.ts)
- New Host → Daemon messages: host.playback_started, host.playback_completed
  (both carry epoch for call-scoping)
- CLI coordinator copy accepts the new messages (v7 Host connecting to
  built-in serve is not rejected as invalid)
- FakeHost in e2e harness bumped to v7

Daemon side (packages/qwen-live):
- Injector: replaces playbackDeadline (byte ÷ 48kB/s estimation) with
  playbackInProgress boolean + playbackCompletedAt timestamp.
  notePlaybackStarted/notePlaybackCompleted replace noteOutputAudio.
  windowClosedForMs returns -1 while playback is active, waits
  completedAt + quietGapMs after completion, returns 0 if no playback
  or cleared.
- v6 forward compatibility: onOutputAudioDelta calls notePlaybackStarted
  as a fallback (v6 Host never sends receipts; v7 Host does). The
  fallback preserves the existing "speech after audio triggers
  clearOutput" behavior without the byte-estimation wait.
- live-session: onPlaybackStarted/onPlaybackCompleted wired from
  coordinator handlers to injector signals
- Coordinator: parseHostMessage + handleTextFrame dispatch the new
  messages; LiveCallHandlers gains optional onPlaybackStarted/
  onPlaybackCompleted; epoch-scoped (stale receipts ignored)

Host app (packages/live-host):
- audio-engine.ts: constructor gains onPlaybackStarted/
  onPlaybackCompleted callbacks; play() fires started on 0→1 source
  transition; source.onended fires completed on 1→0 transition
  (generation-guarded so clearOutput's stop() doesn't false-fire)
- preload/index.ts: callbacks → IPC live:audio:playback-started/
  playback-completed with the current epoch; play IPC payload changes
  from bare Uint8Array to { audio, epoch }
- main/index.ts: IPC handlers validate epoch === daemon.getEpoch(),
  call daemon.sendPlaybackStarted/sendPlaybackCompleted
- daemon-connection.ts: sendPlaybackStarted/sendPlaybackCompleted
  via sendControl

Tests: injector tests rewritten for receipt-based timing (started
holds, completed + gap delivers); e2e discovery assertion bumped to v7.

* test(qwen-live): v7 parity test and playback receipt coverage

- live-host parity test: version assertion bumped 6→7, 13/13 pass
- injector tests rewritten: playback_started holds window,
  playback_completed + quiet gap delivers, clearOutput reopens
  immediately, speech-started returns playbackInProgress
- e2e: FakeHost protocol version v7, discovery record assertion v7

* docs(qwen-live): rewrite README with quick start, init wizard, v7, and backend table

* fix(live-host): prefix unused IPC event parameters

* fix(live-host): gate playback IPC handlers with isTrustedSender

The playback-started/completed IPC channels skipped the sender
validation every sibling channel applies — an untrusted renderer
could forge playback receipts. Added isTrustedSender(event) as the
first guard, matching the pattern in live:set-output-muted etc.

* fix(qwen-live): wire playback receipts to injector + publish qwen-live with Host release

Two fixes in one commit:

1. Playback receipt wiring (qqqys's blocking review):
   - daemon.ts setHandlers now passes onPlaybackStarted/
     onPlaybackCompleted to the session
   - LiveSession gains notePlaybackStarted/notePlaybackCompleted
     methods that resolve the active context by epoch and forward
     to the injector
   - The per-delta notePlaybackStarted fallback in onOutputAudioDelta
     is removed — it latched playbackInProgress closed with no matching
     completion, blocking all queued injections until the user spoke
   - v7 Host receipts now flow end-to-end: Host audio engine →
     IPC → daemon-connection → WS frame → coordinator → session →
     injector
   - Test updated to call session.notePlaybackStarted directly
     (matching the production coordinator→session path)

2. qwen-live npm publish in live-host-release.yml:
   - Added checkout + install + build + npm publish steps to the
     Host release publish job
   - Version aligned with the Host release version via npm version
   - Published as @qwen-code/qwen-live with --provenance
   - Tag: "preview" for prereleases, "latest" for stable
   - Skips if the version is already published

* fix(live-host-release): omit --provenance on first npm publish

@qwen-code/qwen-live does not exist on npm yet; provenance requires
an existing package. The workflow now checks if the package exists
and omits --provenance on the first publish, adds it on subsequent
ones.

* fix(live-host-release): use single quotes in NPM_TAG expression

GitHub Actions expressions require single quotes for string literals;
double quotes cause actionlint to fail.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-02 11:13:04 +00:00

148 lines
3.6 KiB
Text

# API keys and secrets
.env
.env~
# gemini-cli settings
# We want to keep the .gemini in the root of the repo and ignore any .gemini
# in subdirectories. In our root .gemini we want to allow for version control
# for subcommands.
**/.gemini/
!/.gemini/
.gemini/*
!.gemini/config.yaml
!.gemini/commands/
# Note: .qwen-clipboard/ is NOT in gitignore so Gemini can access pasted images
# Dependency directory
node_modules
bower_components
# Editors
.idea
*.iml
.cursor
.qoder
.claude
.codex
.worktrees/
.atlarix/
# Qwen Code Configs
.qwen/*
!.qwen/commands/
!.qwen/commands/**
!.qwen/skills/
!.qwen/skills/**
# Re-ignore auto-generated skills (created by the managed-skill-extractor
# agent with the mandatory `auto-skill-` directory prefix). Git's last-rule-
# wins semantics keep hand-authored project skills tracked while excluding
# these transient, session-specific directories. The `auto-skill-` prefix is
# reserved for auto-generated skills — do not hand-author a project skill with
# this prefix, or its directory will be ignored here.
.qwen/skills/auto-skill-*/
# Re-ignore learned skills (created by the /learn command with the mandatory
# `learned-skill-` directory prefix). Same rationale as auto-skill-*/.
.qwen/skills/learned-skill-*/
# Staging area for auto-skills awaiting confirmation (memory.autoSkillConfirm).
# Already covered by `.qwen/*` above since it's never re-included, but listed
# explicitly alongside auto-skill-*/ so the intent is obvious.
.qwen/pending-skills/
!.qwen/agents/
!.qwen/agents/**
# Team memory (memory.enableTeamMemory) is shared with collaborators THROUGH
# git, so it must be tracked despite the `.qwen/*` ignore above.
!.qwen/team-memory/
!.qwen/team-memory/**
# Repository review context is shared review metadata for `qwen review
# repo-context`, not session output, so it must be tracked too.
!.qwen/review-context.json
# Developer-local session identifier (auto-generated by qwen serve).
.qwen-session
# OS metadata
.DS_Store
Thumbs.db
# TypeScript build info files
*.tsbuildinfo
# Ignore built ts files
dist
packages/audio-capture/build/
packages/audio-capture/prebuilds/
# Docker folder to help skip auth refreshes
.docker
bundle
# Test report files
junit.xml
packages/*/coverage/
packages/web-shell/client/e2e/playwright-report/
packages/web-shell/client/e2e/test-results/
packages/web-shell/client/e2e/visuals/output/
packages/web-shell/client/e2e/visuals/.playwright/
# PR body draft
pr_body.md
# Generated files
packages/cli/src/generated/
packages/core/src/generated/
packages/web-templates/src/generated/
.integration-tests/
packages/vscode-ide-companion/*.vsix
logs/
.repro-runs/
# GHA credentials
gha-creds-*.json
# Log files
patch_output.log
# docs build
docs-site/.next
# content is a symlink to ../docs
docs-site/content
# python cache
__pycache__/
integration-tests/concurrent-runner/output/
integration-tests/concurrent-runner/task-*
integration-tests/terminal-capture/scenarios/screenshots/
# Test-built worker artifact (fzfWorkerHandle.test.ts builds this on-the-fly)
packages/core/src/utils/filesearch/fzfWorker.js
# storybook
*storybook.log
storybook-static
# test
coverage/
# Dev symlink: qc-helper bundled skill docs (created by scripts/dev.js)
packages/core/src/skills/bundled/qc-helper/docs
tmp/
.worktrees/
# code graph skills
.venv
.codegraph
.qwen/computer-use/installed.json
# Auto-generated computer-use marker can also appear under nested packages.
**/.qwen/computer-use/
.playwright-mcp/
# Tool state written to the repo root when $HOME is unset (e.g. gh CLI)
.local/
# Brand build workspaces (created by the desktop-brand-builder skill)
brand-builds/
workspace/