yazses 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. yazses-0.4.1/.github/workflows/apt-repo.yml +91 -0
  2. yazses-0.4.1/.github/workflows/build-macos.yml +160 -0
  3. yazses-0.4.1/.github/workflows/build-windows.yml +141 -0
  4. yazses-0.4.1/.github/workflows/ppa.yml +51 -0
  5. yazses-0.4.1/.github/workflows/release.yml +148 -0
  6. yazses-0.4.1/.github/workflows/snap.yml +74 -0
  7. yazses-0.4.1/.github/workflows/test.yml +38 -0
  8. yazses-0.4.1/.gitignore +254 -0
  9. yazses-0.4.1/CHANGELOG.md +241 -0
  10. yazses-0.4.1/LICENSE +201 -0
  11. yazses-0.4.1/PKG-INFO +428 -0
  12. yazses-0.4.1/README.md +374 -0
  13. yazses-0.4.1/ROADMAP.md +89 -0
  14. yazses-0.4.1/contrib/yazses.service +16 -0
  15. yazses-0.4.1/debian/changelog +5 -0
  16. yazses-0.4.1/debian/control +21 -0
  17. yazses-0.4.1/debian/copyright +19 -0
  18. yazses-0.4.1/debian/postinst +17 -0
  19. yazses-0.4.1/debian/prerm +12 -0
  20. yazses-0.4.1/debian/rules +13 -0
  21. yazses-0.4.1/debian/source/format +1 -0
  22. yazses-0.4.1/docs/adr/adr-v04-001-slm-inference.md +89 -0
  23. yazses-0.4.1/docs/adr/adr-v04-002-lsp-context.md +102 -0
  24. yazses-0.4.1/docs/adr/adr-v04-003-emg-serial.md +106 -0
  25. yazses-0.4.1/docs/apt-repo-setup.md +52 -0
  26. yazses-0.4.1/docs/architecture.md +270 -0
  27. yazses-0.4.1/docs/distribution-status.md +127 -0
  28. yazses-0.4.1/docs/emg-protocol.md +174 -0
  29. yazses-0.4.1/docs/macos-install.md +128 -0
  30. yazses-0.4.1/docs/macos-signing.md +104 -0
  31. yazses-0.4.1/docs/ppa-setup.md +41 -0
  32. yazses-0.4.1/docs/snap-setup.md +49 -0
  33. yazses-0.4.1/docs/superpowers/plans/2026-05-08-distribution-channels.md +869 -0
  34. yazses-0.4.1/docs/windows-install.md +115 -0
  35. yazses-0.4.1/docs/windows-signing.md +80 -0
  36. yazses-0.4.1/examples/config.example.toml +23 -0
  37. yazses-0.4.1/install-apt.sh +85 -0
  38. yazses-0.4.1/install.sh +94 -0
  39. yazses-0.4.1/packaging/README.md +76 -0
  40. yazses-0.4.1/packaging/arch/PKGBUILD +114 -0
  41. yazses-0.4.1/packaging/arch/README.md +75 -0
  42. yazses-0.4.1/packaging/homebrew/yazses.rb +44 -0
  43. yazses-0.4.1/packaging/macos/entitlements.plist +21 -0
  44. yazses-0.4.1/packaging/macos/yazses.spec +123 -0
  45. yazses-0.4.1/packaging/windows/installer.iss +79 -0
  46. yazses-0.4.1/packaging/windows/yazses.spec +88 -0
  47. yazses-0.4.1/packaging/winget/manifests/n/NovaFabric/YazSes/0.2.0/NovaFabric.YazSes.installer.yaml +31 -0
  48. yazses-0.4.1/packaging/winget/manifests/n/NovaFabric/YazSes/0.2.0/NovaFabric.YazSes.locale.en-US.yaml +46 -0
  49. yazses-0.4.1/packaging/winget/manifests/n/NovaFabric/YazSes/0.2.0/NovaFabric.YazSes.yaml +16 -0
  50. yazses-0.4.1/pyproject.toml +93 -0
  51. yazses-0.4.1/scripts/build-deb.sh +95 -0
  52. yazses-0.4.1/scripts/build-macos.sh +67 -0
  53. yazses-0.4.1/scripts/build-windows.ps1 +84 -0
  54. yazses-0.4.1/scripts/update-apt-repo.sh +94 -0
  55. yazses-0.4.1/scripts/upload-ppa.sh +38 -0
  56. yazses-0.4.1/snap/local/yazses-daemon-wrapper +5 -0
  57. yazses-0.4.1/snap/local/yazses-tray-wrapper +5 -0
  58. yazses-0.4.1/snap/local/yazses-wrapper +5 -0
  59. yazses-0.4.1/snap/snapcraft.yaml +59 -0
  60. yazses-0.4.1/src/yazses/__init__.py +1 -0
  61. yazses-0.4.1/src/yazses/__main__.py +46 -0
  62. yazses-0.4.1/src/yazses/accessibility/__init__.py +0 -0
  63. yazses-0.4.1/src/yazses/accessibility/enroll.py +181 -0
  64. yazses-0.4.1/src/yazses/audio/__init__.py +0 -0
  65. yazses-0.4.1/src/yazses/audio/padding.py +75 -0
  66. yazses-0.4.1/src/yazses/audio/recorder.py +60 -0
  67. yazses-0.4.1/src/yazses/audio/vad.py +9 -0
  68. yazses-0.4.1/src/yazses/audio/vad_calibrated.py +16 -0
  69. yazses-0.4.1/src/yazses/cli.py +225 -0
  70. yazses-0.4.1/src/yazses/commands/__init__.py +0 -0
  71. yazses-0.4.1/src/yazses/commands/dispatch.py +146 -0
  72. yazses-0.4.1/src/yazses/commands/grammar.py +115 -0
  73. yazses-0.4.1/src/yazses/commands/lsp_context.py +370 -0
  74. yazses-0.4.1/src/yazses/commands/model_manager.py +118 -0
  75. yazses-0.4.1/src/yazses/commands/profiles.py +21 -0
  76. yazses-0.4.1/src/yazses/commands/slm_router.py +231 -0
  77. yazses-0.4.1/src/yazses/config.py +162 -0
  78. yazses-0.4.1/src/yazses/core/__init__.py +0 -0
  79. yazses-0.4.1/src/yazses/core/daemon.py +508 -0
  80. yazses-0.4.1/src/yazses/hotkeys/__init__.py +0 -0
  81. yazses-0.4.1/src/yazses/hotkeys/evdev_hold.py +79 -0
  82. yazses-0.4.1/src/yazses/hotkeys/hold_detector.py +32 -0
  83. yazses-0.4.1/src/yazses/inject/__init__.py +0 -0
  84. yazses-0.4.1/src/yazses/inject/auto.py +21 -0
  85. yazses-0.4.1/src/yazses/inject/base.py +8 -0
  86. yazses-0.4.1/src/yazses/inject/clipboard.py +80 -0
  87. yazses-0.4.1/src/yazses/inject/streaming.py +59 -0
  88. yazses-0.4.1/src/yazses/inject/wtype.py +31 -0
  89. yazses-0.4.1/src/yazses/inject/xdotool.py +28 -0
  90. yazses-0.4.1/src/yazses/inject/ydotool.py +20 -0
  91. yazses-0.4.1/src/yazses/ipc/__init__.py +0 -0
  92. yazses-0.4.1/src/yazses/ipc/client.py +85 -0
  93. yazses-0.4.1/src/yazses/ipc/protocol.py +100 -0
  94. yazses-0.4.1/src/yazses/ipc/server.py +169 -0
  95. yazses-0.4.1/src/yazses/main.py +7 -0
  96. yazses-0.4.1/src/yazses/platform/__init__.py +33 -0
  97. yazses-0.4.1/src/yazses/platform/base.py +205 -0
  98. yazses-0.4.1/src/yazses/platform/emg/__init__.py +0 -0
  99. yazses-0.4.1/src/yazses/platform/emg/backend.py +150 -0
  100. yazses-0.4.1/src/yazses/platform/emg/ble_backend.py +144 -0
  101. yazses-0.4.1/src/yazses/platform/factory.py +30 -0
  102. yazses-0.4.1/src/yazses/platform/linux/__init__.py +39 -0
  103. yazses-0.4.1/src/yazses/platform/linux/hotkey.py +79 -0
  104. yazses-0.4.1/src/yazses/platform/linux/injector.py +119 -0
  105. yazses-0.4.1/src/yazses/platform/linux/ipc.py +20 -0
  106. yazses-0.4.1/src/yazses/platform/linux/lifecycle.py +85 -0
  107. yazses-0.4.1/src/yazses/platform/linux/paths.py +22 -0
  108. yazses-0.4.1/src/yazses/platform/linux/permissions.py +43 -0
  109. yazses-0.4.1/src/yazses/platform/macos/__init__.py +44 -0
  110. yazses-0.4.1/src/yazses/platform/macos/hotkey.py +259 -0
  111. yazses-0.4.1/src/yazses/platform/macos/injector.py +137 -0
  112. yazses-0.4.1/src/yazses/platform/macos/ipc.py +18 -0
  113. yazses-0.4.1/src/yazses/platform/macos/lifecycle.py +140 -0
  114. yazses-0.4.1/src/yazses/platform/macos/paths.py +24 -0
  115. yazses-0.4.1/src/yazses/platform/macos/permissions.py +80 -0
  116. yazses-0.4.1/src/yazses/platform/macos/tray.py +92 -0
  117. yazses-0.4.1/src/yazses/platform/windows/__init__.py +46 -0
  118. yazses-0.4.1/src/yazses/platform/windows/hotkey.py +211 -0
  119. yazses-0.4.1/src/yazses/platform/windows/injector.py +168 -0
  120. yazses-0.4.1/src/yazses/platform/windows/ipc.py +238 -0
  121. yazses-0.4.1/src/yazses/platform/windows/lifecycle.py +152 -0
  122. yazses-0.4.1/src/yazses/platform/windows/paths.py +24 -0
  123. yazses-0.4.1/src/yazses/platform/windows/permissions.py +47 -0
  124. yazses-0.4.1/src/yazses/platform/windows/tray.py +100 -0
  125. yazses-0.4.1/src/yazses/postprocess/__init__.py +0 -0
  126. yazses-0.4.1/src/yazses/postprocess/cleaner.py +11 -0
  127. yazses-0.4.1/src/yazses/remote/__init__.py +0 -0
  128. yazses-0.4.1/src/yazses/remote/agent.py +95 -0
  129. yazses-0.4.1/src/yazses/remote/forwarder.py +100 -0
  130. yazses-0.4.1/src/yazses/remote/inject.py +65 -0
  131. yazses-0.4.1/src/yazses/remote/local_proxy.py +72 -0
  132. yazses-0.4.1/src/yazses/stt/__init__.py +0 -0
  133. yazses-0.4.1/src/yazses/stt/faster_whisper.py +32 -0
  134. yazses-0.4.1/src/yazses/stt/filters/__init__.py +0 -0
  135. yazses-0.4.1/src/yazses/stt/filters/disfluency.py +132 -0
  136. yazses-0.4.1/src/yazses/stt/streaming.py +150 -0
  137. yazses-0.4.1/src/yazses/system/__init__.py +0 -0
  138. yazses-0.4.1/src/yazses/system/doctor.py +102 -0
  139. yazses-0.4.1/src/yazses/system/pid.py +41 -0
  140. yazses-0.4.1/src/yazses/tray/__init__.py +0 -0
  141. yazses-0.4.1/src/yazses/tray/app.py +105 -0
  142. yazses-0.4.1/tests/__init__.py +0 -0
  143. yazses-0.4.1/tests/conftest.py +15 -0
  144. yazses-0.4.1/tests/fixtures/accessibility/rms_stats.json +5 -0
  145. yazses-0.4.1/tests/fixtures/commands/command_phrases.json +70 -0
  146. yazses-0.4.1/tests/fixtures/commands/dictation_corpus.txt +87 -0
  147. yazses-0.4.1/tests/fixtures/disfluency/corpus.json +162 -0
  148. yazses-0.4.1/tests/test_accessibility_enroll.py +81 -0
  149. yazses-0.4.1/tests/test_audio_padding.py +82 -0
  150. yazses-0.4.1/tests/test_auto_inject.py +44 -0
  151. yazses-0.4.1/tests/test_cleaner.py +51 -0
  152. yazses-0.4.1/tests/test_config.py +43 -0
  153. yazses-0.4.1/tests/test_disfluency_filter.py +81 -0
  154. yazses-0.4.1/tests/test_emg_backend.py +309 -0
  155. yazses-0.4.1/tests/test_grammar_classifier.py +103 -0
  156. yazses-0.4.1/tests/test_hold_detector.py +62 -0
  157. yazses-0.4.1/tests/test_ipc_protocol.py +141 -0
  158. yazses-0.4.1/tests/test_lsp_context.py +225 -0
  159. yazses-0.4.1/tests/test_platform_factory.py +69 -0
  160. yazses-0.4.1/tests/test_platform_macos.py +96 -0
  161. yazses-0.4.1/tests/test_platform_windows.py +106 -0
  162. yazses-0.4.1/tests/test_remote_agent.py +158 -0
  163. yazses-0.4.1/tests/test_slm_router.py +177 -0
  164. yazses-0.4.1/tests/test_streaming_engine.py +91 -0
  165. yazses-0.4.1/tests/test_streaming_injector.py +90 -0
  166. yazses-0.4.1/tests/test_vad_calibrated.py +43 -0
  167. yazses-0.4.1/uv.lock +1813 -0
@@ -0,0 +1,91 @@
1
+ name: APT Repository
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["Release"]
6
+ types: [completed]
7
+ workflow_dispatch:
8
+ inputs:
9
+ run_id:
10
+ description: "Release workflow run ID (optional, uses latest if empty)"
11
+ required: false
12
+ default: ""
13
+
14
+ env:
15
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
16
+
17
+ concurrency:
18
+ group: apt-repo-${{ github.repository }}
19
+ cancel-in-progress: false
20
+
21
+ jobs:
22
+ update-apt-repo:
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 15
25
+ if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
26
+ permissions:
27
+ contents: write
28
+ actions: read
29
+
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ with:
33
+ fetch-depth: 0
34
+
35
+ - name: Determine release run ID
36
+ id: run_id
37
+ env:
38
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39
+ run: |
40
+ MANUAL_ID="${{ github.event.inputs.run_id }}"
41
+ EVENT_ID="${{ github.event.workflow_run.id }}"
42
+ if [ -n "$MANUAL_ID" ]; then
43
+ echo "id=$MANUAL_ID" >> $GITHUB_OUTPUT
44
+ elif [ -n "$EVENT_ID" ]; then
45
+ echo "id=$EVENT_ID" >> $GITHUB_OUTPUT
46
+ else
47
+ # Auto-detect: find latest successful Release run that has a deb-package artifact
48
+ RUN_ID=$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/release.yml/runs?status=completed&per_page=10" \
49
+ --jq '.workflow_runs[] | select(.conclusion=="success" or .conclusion=="failure") | .id' \
50
+ | head -1)
51
+ echo "id=$RUN_ID" >> $GITHUB_OUTPUT
52
+ echo "Auto-detected release run ID: $RUN_ID"
53
+ fi
54
+
55
+ - name: Download .deb artifact from release workflow
56
+ uses: actions/download-artifact@v4
57
+ with:
58
+ name: deb-package
59
+ path: /tmp/debs
60
+ run-id: ${{ steps.run_id.outputs.id }}
61
+ github-token: ${{ secrets.GITHUB_TOKEN }}
62
+
63
+ - name: Import GPG signing key
64
+ env:
65
+ GPG_PRIVATE_KEY: ${{ secrets.APT_REPO_GPG_PRIVATE_KEY }}
66
+ GPG_KEY_ID: ${{ secrets.APT_REPO_GPG_KEY_ID }}
67
+ run: |
68
+ set -euo pipefail
69
+
70
+ test -n "$GPG_PRIVATE_KEY" || { echo "::error::APT_REPO_GPG_PRIVATE_KEY secret is empty or missing"; exit 1; }
71
+ test -n "$GPG_KEY_ID" || { echo "::error::APT_REPO_GPG_KEY_ID secret is empty or missing"; exit 1; }
72
+
73
+ mkdir -p ~/.gnupg
74
+ chmod 700 ~/.gnupg
75
+ printf 'allow-loopback-pinentry\n' > ~/.gnupg/gpg-agent.conf
76
+ gpgconf --kill gpg-agent || true
77
+
78
+ printf '%s\n' "$GPG_PRIVATE_KEY" | gpg --batch --yes --import
79
+ echo "$GPG_KEY_ID:6:" | gpg --import-ownertrust
80
+ gpg --list-secret-keys "$GPG_KEY_ID"
81
+
82
+ echo "" > /tmp/gpg-passphrase
83
+
84
+ - name: Update apt repository
85
+ env:
86
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
87
+ GPG_KEY_ID: ${{ secrets.APT_REPO_GPG_KEY_ID }}
88
+ run: |
89
+ debs=(/tmp/debs/*.deb)
90
+ [[ ${#debs[@]} -eq 1 ]] || { echo "Expected 1 .deb, got ${#debs[@]}"; exit 1; }
91
+ bash scripts/update-apt-repo.sh "${debs[0]}" "$GPG_KEY_ID"
@@ -0,0 +1,160 @@
1
+ name: build-macos
2
+
3
+ on:
4
+ push:
5
+ tags: ['v*']
6
+ pull_request:
7
+ paths:
8
+ - 'src/yazses/platform/macos/**'
9
+ - 'src/yazses/__main__.py'
10
+ - 'src/yazses/tray/**'
11
+ - 'packaging/macos/**'
12
+ - 'scripts/build-macos.sh'
13
+ - '.github/workflows/build-macos.yml'
14
+ - 'pyproject.toml'
15
+ workflow_dispatch:
16
+
17
+ permissions:
18
+ contents: write # needed by softprops/action-gh-release to attach .dmg to the Release
19
+
20
+ jobs:
21
+ build:
22
+ runs-on: macos-latest
23
+ timeout-minutes: 30
24
+
25
+ env:
26
+ # Apple Developer Program signing + notarisation. All four secrets must
27
+ # be set for the signing path to fire. Otherwise the workflow ships an
28
+ # unsigned dev preview (the v0 default).
29
+ # MACOS_CERTIFICATE — base64 of the Developer ID .p12 keystore
30
+ # MACOS_CERTIFICATE_PWD — password for the .p12
31
+ # MACOS_NOTARY_APPLE_ID — Apple ID email used for notarytool
32
+ # MACOS_NOTARY_PASSWORD — app-specific password from appleid.apple.com
33
+ # MACOS_NOTARY_TEAM_ID — 10-char team ID (Apple Developer Membership)
34
+ # MACOS_SIGNING_IDENTITY — exact "Developer ID Application: <Name> (<Team>)" string
35
+ MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
36
+ MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
37
+ MACOS_NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARY_APPLE_ID }}
38
+ MACOS_NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARY_PASSWORD }}
39
+ MACOS_NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARY_TEAM_ID }}
40
+ MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
41
+
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+
45
+ - name: Set up uv
46
+ uses: astral-sh/setup-uv@v5
47
+ with:
48
+ version: '0.5.x'
49
+
50
+ - name: Set up Python
51
+ run: uv python install 3.12
52
+
53
+ - name: Install create-dmg
54
+ run: brew install create-dmg
55
+
56
+ - name: Detect signing presence
57
+ id: sign
58
+ shell: bash
59
+ run: |
60
+ if [[ -n "${MACOS_CERTIFICATE}" \
61
+ && -n "${MACOS_CERTIFICATE_PWD}" \
62
+ && -n "${MACOS_NOTARY_APPLE_ID}" \
63
+ && -n "${MACOS_NOTARY_PASSWORD}" \
64
+ && -n "${MACOS_NOTARY_TEAM_ID}" \
65
+ && -n "${MACOS_SIGNING_IDENTITY}" ]]; then
66
+ echo "sign=true" >> "${GITHUB_OUTPUT}"
67
+ echo "Signing + notarisation will run."
68
+ else
69
+ echo "sign=false" >> "${GITHUB_OUTPUT}"
70
+ echo "Signing secrets not all present; producing unsigned dev preview."
71
+ fi
72
+
73
+ - name: Import signing certificate into keychain
74
+ if: steps.sign.outputs.sign == 'true'
75
+ shell: bash
76
+ run: |
77
+ KEYCHAIN=yazses-build.keychain-db
78
+ KEYCHAIN_PWD=$(uuidgen)
79
+ security create-keychain -p "${KEYCHAIN_PWD}" "${KEYCHAIN}"
80
+ security set-keychain-settings -lut 21600 "${KEYCHAIN}"
81
+ security unlock-keychain -p "${KEYCHAIN_PWD}" "${KEYCHAIN}"
82
+
83
+ # Decode the base64-encoded .p12 and import it.
84
+ echo -n "${MACOS_CERTIFICATE}" | base64 --decode > /tmp/cert.p12
85
+ security import /tmp/cert.p12 \
86
+ -k "${KEYCHAIN}" \
87
+ -P "${MACOS_CERTIFICATE_PWD}" \
88
+ -T /usr/bin/codesign \
89
+ -T /usr/bin/security
90
+ rm -f /tmp/cert.p12
91
+
92
+ # Allow codesign to use the imported key without a UI prompt.
93
+ security set-key-partition-list \
94
+ -S apple-tool:,apple:,codesign: \
95
+ -s -k "${KEYCHAIN_PWD}" "${KEYCHAIN}" >/dev/null
96
+ security list-keychains -d user -s "${KEYCHAIN}" $(security list-keychains -d user | tr -d '"' | xargs)
97
+ echo "KEYCHAIN_PWD=${KEYCHAIN_PWD}" >> "${GITHUB_ENV}"
98
+ echo "Keychain ready."
99
+
100
+ - name: Build .dmg
101
+ run: ./scripts/build-macos.sh
102
+
103
+ - name: Codesign .app and .dmg (Developer ID)
104
+ if: steps.sign.outputs.sign == 'true'
105
+ shell: bash
106
+ run: |
107
+ set -euo pipefail
108
+ APP="dist/YazSes.app"
109
+ DMG=$(ls dist/YazSes-*.dmg | head -1)
110
+ ENTITLEMENTS=packaging/macos/entitlements.plist
111
+
112
+ echo "==> codesign --deep on the app bundle"
113
+ /usr/bin/codesign \
114
+ --force --deep --options runtime \
115
+ --timestamp \
116
+ --entitlements "${ENTITLEMENTS}" \
117
+ --sign "${MACOS_SIGNING_IDENTITY}" \
118
+ "${APP}"
119
+
120
+ echo "==> codesign on the .dmg itself"
121
+ /usr/bin/codesign \
122
+ --force --timestamp \
123
+ --sign "${MACOS_SIGNING_IDENTITY}" \
124
+ "${DMG}"
125
+
126
+ echo "==> verify"
127
+ /usr/bin/codesign --verify --deep --strict --verbose=2 "${APP}"
128
+ /usr/bin/codesign --verify --verbose=2 "${DMG}"
129
+
130
+ - name: Notarise .dmg with Apple
131
+ if: steps.sign.outputs.sign == 'true'
132
+ shell: bash
133
+ run: |
134
+ set -euo pipefail
135
+ DMG=$(ls dist/YazSes-*.dmg | head -1)
136
+ xcrun notarytool submit "${DMG}" \
137
+ --apple-id "${MACOS_NOTARY_APPLE_ID}" \
138
+ --password "${MACOS_NOTARY_PASSWORD}" \
139
+ --team-id "${MACOS_NOTARY_TEAM_ID}" \
140
+ --wait
141
+ xcrun stapler staple "${DMG}"
142
+ xcrun stapler validate "${DMG}"
143
+
144
+ - name: Show build artefacts
145
+ run: ls -lh dist/
146
+
147
+ - name: Upload .dmg (signed if applicable)
148
+ uses: actions/upload-artifact@v4
149
+ with:
150
+ name: yazses-macos-${{ steps.sign.outputs.sign == 'true' && 'signed' || 'unsigned' }}-${{ github.sha }}
151
+ path: dist/YazSes-*.dmg
152
+ if-no-files-found: error
153
+ retention-days: 14
154
+
155
+ - name: Attach to release (tag builds only)
156
+ if: startsWith(github.ref, 'refs/tags/v')
157
+ uses: softprops/action-gh-release@v2
158
+ with:
159
+ files: dist/YazSes-*.dmg
160
+ fail_on_unmatched_files: true
@@ -0,0 +1,141 @@
1
+ name: build-windows
2
+
3
+ on:
4
+ push:
5
+ tags: ['v*']
6
+ pull_request:
7
+ paths:
8
+ - 'src/yazses/platform/windows/**'
9
+ - 'src/yazses/__main__.py'
10
+ - 'src/yazses/tray/**'
11
+ - 'packaging/windows/**'
12
+ - 'scripts/build-windows.ps1'
13
+ - '.github/workflows/build-windows.yml'
14
+ - 'pyproject.toml'
15
+ workflow_dispatch:
16
+
17
+ permissions:
18
+ id-token: write # needed by SignPath's OIDC-based authentication
19
+ contents: write # needed by softprops/action-gh-release to attach .exe
20
+
21
+ jobs:
22
+ build:
23
+ runs-on: windows-latest
24
+ timeout-minutes: 30
25
+
26
+ env:
27
+ # SignPath.io signing (free for OSS). All four must be set for the
28
+ # sign step to fire; otherwise the workflow ships an unsigned dev
29
+ # preview (the v0 default).
30
+ # SIGNPATH_API_TOKEN — API token from SignPath account
31
+ # SIGNPATH_ORGANIZATION_ID — UUID of your SignPath organisation
32
+ # SIGNPATH_PROJECT_SLUG — e.g. "yazses"
33
+ # SIGNPATH_SIGNING_POLICY — e.g. "release-signing"
34
+ SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
35
+ SIGNPATH_ORGANIZATION_ID: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
36
+ SIGNPATH_PROJECT_SLUG: ${{ secrets.SIGNPATH_PROJECT_SLUG }}
37
+ SIGNPATH_SIGNING_POLICY: ${{ secrets.SIGNPATH_SIGNING_POLICY }}
38
+
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+
42
+ - name: Set up uv
43
+ uses: astral-sh/setup-uv@v5
44
+ with:
45
+ version: '0.5.x'
46
+
47
+ - name: Set up Python
48
+ run: uv python install 3.12
49
+
50
+ - name: Confirm Inno Setup is available
51
+ shell: pwsh
52
+ run: |
53
+ $cands = @(
54
+ "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe",
55
+ "${env:ProgramFiles}\Inno Setup 6\ISCC.exe"
56
+ )
57
+ $iscc = $cands | Where-Object { Test-Path $_ } | Select-Object -First 1
58
+ if (-not $iscc) {
59
+ Write-Host "Inno Setup not preinstalled; installing via choco"
60
+ choco install innosetup -y --no-progress
61
+ } else {
62
+ Write-Host "Found Inno Setup at $iscc"
63
+ }
64
+
65
+ - name: Detect signing presence
66
+ id: sign
67
+ shell: pwsh
68
+ run: |
69
+ if ($env:SIGNPATH_API_TOKEN -and $env:SIGNPATH_ORGANIZATION_ID -and $env:SIGNPATH_PROJECT_SLUG -and $env:SIGNPATH_SIGNING_POLICY) {
70
+ Write-Host "Signing via SignPath will run."
71
+ echo "sign=true" >> $env:GITHUB_OUTPUT
72
+ } else {
73
+ Write-Host "SignPath secrets not all present; producing unsigned dev preview."
74
+ echo "sign=false" >> $env:GITHUB_OUTPUT
75
+ }
76
+
77
+ - name: Build installer
78
+ shell: pwsh
79
+ run: ./scripts/build-windows.ps1
80
+
81
+ - name: Locate .exe
82
+ id: locate
83
+ shell: pwsh
84
+ run: |
85
+ $exe = Get-ChildItem "dist\YazSes-*-windows-x64.exe" | Select-Object -First 1
86
+ if (-not $exe) { throw "No installer produced" }
87
+ echo "path=$($exe.FullName)" >> $env:GITHUB_OUTPUT
88
+ echo "name=$($exe.Name)" >> $env:GITHUB_OUTPUT
89
+
90
+ # Upload the unsigned .exe to SignPath, which signs it and returns the
91
+ # signed binary as a separate artefact named "$name.signed".
92
+ - name: Upload to SignPath for signing
93
+ if: steps.sign.outputs.sign == 'true'
94
+ uses: actions/upload-artifact@v4
95
+ with:
96
+ name: unsigned-installer
97
+ path: ${{ steps.locate.outputs.path }}
98
+ if-no-files-found: error
99
+
100
+ - name: Sign with SignPath
101
+ if: steps.sign.outputs.sign == 'true'
102
+ uses: signpath/github-action-submit-signing-request@v1
103
+ with:
104
+ api-token: ${{ env.SIGNPATH_API_TOKEN }}
105
+ organization-id: ${{ env.SIGNPATH_ORGANIZATION_ID }}
106
+ project-slug: ${{ env.SIGNPATH_PROJECT_SLUG }}
107
+ signing-policy-slug: ${{ env.SIGNPATH_SIGNING_POLICY }}
108
+ github-artifact-id: ${{ github.run_id }}
109
+ wait-for-completion: true
110
+ output-artifact-directory: dist-signed
111
+ parameters: |
112
+ Version: ${{ github.ref_name }}
113
+
114
+ - name: Replace unsigned installer with signed one
115
+ if: steps.sign.outputs.sign == 'true'
116
+ shell: pwsh
117
+ run: |
118
+ $signed = Get-ChildItem "dist-signed\*.exe" | Select-Object -First 1
119
+ if (-not $signed) { throw "SignPath returned no signed exe" }
120
+ Copy-Item -Force $signed.FullName "${{ steps.locate.outputs.path }}"
121
+ Write-Host "Replaced unsigned installer with signed version: $($signed.Name)"
122
+ Get-AuthenticodeSignature "${{ steps.locate.outputs.path }}" | Format-List
123
+
124
+ - name: Show build artefacts
125
+ shell: pwsh
126
+ run: Get-ChildItem dist | Format-Table Name, Length, LastWriteTime
127
+
128
+ - name: Upload installer (signed if applicable)
129
+ uses: actions/upload-artifact@v4
130
+ with:
131
+ name: yazses-windows-${{ steps.sign.outputs.sign == 'true' && 'signed' || 'unsigned' }}-${{ github.sha }}
132
+ path: dist/YazSes-*-windows-x64.exe
133
+ if-no-files-found: error
134
+ retention-days: 14
135
+
136
+ - name: Attach to release (tag builds only)
137
+ if: startsWith(github.ref, 'refs/tags/v')
138
+ uses: softprops/action-gh-release@v2
139
+ with:
140
+ files: dist/YazSes-*-windows-x64.exe
141
+ fail_on_unmatched_files: true
@@ -0,0 +1,51 @@
1
+ name: Launchpad PPA
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ env:
9
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
10
+
11
+ jobs:
12
+ upload-ppa:
13
+ runs-on: ubuntu-latest
14
+ timeout-minutes: 15
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Install Debian packaging tools
19
+ run: |
20
+ sudo apt-get install -y -q devscripts debhelper dput gpg
21
+
22
+ - name: Import GPG key
23
+ uses: crazy-max/ghaction-import-gpg@v6
24
+ with:
25
+ gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
26
+ passphrase: ""
27
+ git_user_signingkey: true
28
+
29
+ - name: Configure dput
30
+ run: |
31
+ cat > ~/.dput.cf <<'EOF'
32
+ [yazses-ppa]
33
+ fqdn = ppa.launchpad.net
34
+ method = ftp
35
+ incoming = ~novafabric/ubuntu/yazses/
36
+ login = anonymous
37
+ allow_unsigned_uploads = 0
38
+ EOF
39
+
40
+ - name: Upload to PPA
41
+ env:
42
+ DEBEMAIL: mohsen.seyedkazemi@gmail.com
43
+ DEBFULLNAME: Mohsen Seyedkazemi Moghadam
44
+ GPG_KEY_ID: ${{ secrets.PPA_GPG_KEY_ID }}
45
+ run: |
46
+ VERSION=${GITHUB_REF_NAME#v}
47
+ chmod +x scripts/upload-ppa.sh
48
+ bash scripts/upload-ppa.sh \
49
+ "$VERSION" \
50
+ "$GPG_KEY_ID" \
51
+ "yazses-ppa"
@@ -0,0 +1,148 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ env:
12
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
13
+
14
+ jobs:
15
+ test:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Install system dependencies
21
+ run: sudo apt-get install -y libportaudio2
22
+
23
+ - uses: astral-sh/setup-uv@v5
24
+ with:
25
+ version: "latest"
26
+ python-version: "3.12"
27
+
28
+ - run: uv sync
29
+
30
+ - run: uv run pytest tests/ -v
31
+
32
+ publish-pypi:
33
+ runs-on: ubuntu-latest
34
+ needs: test
35
+ environment: pypi
36
+ permissions:
37
+ id-token: write # required for OIDC trusted publishing
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+
41
+ - uses: astral-sh/setup-uv@v5
42
+ with:
43
+ version: "latest"
44
+ python-version: "3.12"
45
+
46
+ - name: Build wheel and sdist
47
+ run: uv build
48
+
49
+ - name: Publish to PyPI
50
+ uses: pypa/gh-action-pypi-publish@release/v1
51
+
52
+ release-linux:
53
+ # Builds the .deb, creates the GitHub Release, and uploads the artifact
54
+ # so the APT repo workflow can download it.
55
+ runs-on: ubuntu-latest
56
+ needs: test
57
+ steps:
58
+ - uses: actions/checkout@v4
59
+
60
+ - name: Install build tools
61
+ run: sudo apt-get install -y dpkg-dev
62
+
63
+ - name: Build .deb
64
+ run: |
65
+ chmod +x scripts/build-deb.sh
66
+ bash scripts/build-deb.sh
67
+
68
+ - name: Upload .deb artifact
69
+ uses: actions/upload-artifact@v4
70
+ with:
71
+ name: deb-package
72
+ path: "*.deb"
73
+ if-no-files-found: error
74
+
75
+ - name: Create GitHub Release
76
+ uses: softprops/action-gh-release@v2
77
+ with:
78
+ files: "*.deb"
79
+ generate_release_notes: true
80
+ body: |
81
+ Local, offline voice dictation. **Hold a key, speak, release.** No cloud, no GPU.
82
+
83
+ ## macOS
84
+
85
+ Download `YazSes-<version>.dmg` from the Assets below, open it, drag
86
+ **YazSes.app** into **Applications**.
87
+
88
+ On first launch, **right-click → Open** (the v0 build is unsigned;
89
+ signing is coming). Grant **Accessibility** + **Microphone** when prompted.
90
+ Default hotkey: **Right Option**. Full guide: `docs/macos-install.md`.
91
+
92
+ ## Windows
93
+
94
+ Download `YazSes-<version>-windows-x64.exe` from the Assets and run it.
95
+
96
+ SmartScreen may say *"unrecognized app"* — click **More info → Run anyway**
97
+ (the v0 build is unsigned; signing is coming). Default hotkey: **Right
98
+ Ctrl**. Full guide: `docs/windows-install.md`.
99
+
100
+ ## Linux
101
+
102
+ **One-line installer (Debian/Ubuntu):**
103
+ ```bash
104
+ bash <(curl -fsSL https://raw.githubusercontent.com/novafabric/yazses/main/install.sh)
105
+ ```
106
+
107
+ **APT repository:**
108
+ ```bash
109
+ curl -fsSL https://novafabric.github.io/yazses/apt/KEY.gpg \
110
+ | sudo gpg --dearmor --yes -o /usr/share/keyrings/yazses.gpg
111
+ echo "deb [signed-by=/usr/share/keyrings/yazses.gpg] https://novafabric.github.io/yazses/apt ./" \
112
+ | sudo tee /etc/apt/sources.list.d/yazses.list
113
+ sudo apt update && sudo apt install yazses
114
+ sudo usermod -aG input $USER && logout
115
+ systemctl --user enable --now yazses.service
116
+ ```
117
+
118
+ **Launchpad PPA (Ubuntu):**
119
+ ```bash
120
+ sudo add-apt-repository ppa:novafabric/yazses
121
+ sudo apt update && sudo apt install yazses
122
+ sudo usermod -aG input $USER && logout
123
+ systemctl --user enable --now yazses.service
124
+ ```
125
+
126
+ **Snap:**
127
+ ```bash
128
+ sudo snap install yazses --classic
129
+ sudo usermod -aG input $USER && logout
130
+ ```
131
+
132
+ **pipx (any Linux):**
133
+ ```bash
134
+ sudo apt install pipx libportaudio2 xdotool xclip
135
+ sudo usermod -aG input $USER && logout
136
+ pipx install yazses && pipx ensurepath
137
+ systemctl --user enable --now yazses.service
138
+ ```
139
+
140
+ **.deb package:**
141
+ ```bash
142
+ sudo apt install ./yazses_*.deb
143
+ sudo usermod -aG input $USER && logout
144
+ systemctl --user enable --now yazses.service
145
+ ```
146
+
147
+ Default hotkey on Linux: **Space** (configurable). Default config lives
148
+ at `~/.config/yazses/config.toml`.
@@ -0,0 +1,74 @@
1
+ name: Snap
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+ inputs:
9
+ version:
10
+ description: "Version to build (without 'v' prefix). Defaults to current snapcraft.yaml value."
11
+ required: false
12
+ type: string
13
+
14
+ env:
15
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
16
+
17
+ jobs:
18
+ snap:
19
+ runs-on: ubuntu-latest
20
+ timeout-minutes: 30
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+
24
+ - name: Set version
25
+ run: |
26
+ if [[ "${{ github.event_name }}" == "workflow_dispatch" && -n "${{ inputs.version }}" ]]; then
27
+ VERSION="${{ inputs.version }}"
28
+ elif [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
29
+ VERSION=${GITHUB_REF_NAME#v}
30
+ else
31
+ VERSION=$(grep -E '^version:' snap/snapcraft.yaml | head -1 | sed -E 's/version:\s*"([^"]+)".*/\1/')
32
+ fi
33
+ sed -i "s/^version:.*/version: \"$VERSION\"/" snap/snapcraft.yaml
34
+ echo "Building snap version $VERSION"
35
+
36
+ - name: Build snap
37
+ uses: snapcore/action-build@v1
38
+ id: snap-build
39
+ # Default mode uses an LXD container, which has root and can refresh
40
+ # the apt cache. --destructive-mode runs as the runner user without
41
+ # root, which leaves the package list stale and triggers 404s when
42
+ # Ubuntu rolls forward.
43
+
44
+ - name: Show built artefact
45
+ run: ls -lh "${{ steps.snap-build.outputs.snap }}"
46
+
47
+ - name: Upload .snap as workflow artefact
48
+ uses: actions/upload-artifact@v4
49
+ with:
50
+ name: yazses-snap-${{ github.sha }}
51
+ path: ${{ steps.snap-build.outputs.snap }}
52
+ if-no-files-found: error
53
+ retention-days: 14
54
+
55
+ - name: Check Snap Store credentials presence
56
+ id: check-snap-creds
57
+ env:
58
+ CREDS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
59
+ run: |
60
+ if [[ -z "${CREDS}" ]]; then
61
+ echo "Snap Store credentials not configured; skipping publish."
62
+ echo "have_creds=false" >> "${GITHUB_OUTPUT}"
63
+ else
64
+ echo "have_creds=true" >> "${GITHUB_OUTPUT}"
65
+ fi
66
+
67
+ - name: Publish to Snap Store
68
+ if: steps.check-snap-creds.outputs.have_creds == 'true'
69
+ uses: snapcore/action-publish@v1
70
+ env:
71
+ SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
72
+ with:
73
+ snap: ${{ steps.snap-build.outputs.snap }}
74
+ release: stable