ai-browser-toolkit 0.1.2__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 (133) hide show
  1. ai_browser_toolkit-0.1.2/.claude/settings.local.json +8 -0
  2. ai_browser_toolkit-0.1.2/.gitattributes +9 -0
  3. ai_browser_toolkit-0.1.2/.github/workflows/check-secrets.yml +51 -0
  4. ai_browser_toolkit-0.1.2/.github/workflows/ci.yml +54 -0
  5. ai_browser_toolkit-0.1.2/.github/workflows/release.yml +448 -0
  6. ai_browser_toolkit-0.1.2/.gitignore +31 -0
  7. ai_browser_toolkit-0.1.2/.sheet-url +1 -0
  8. ai_browser_toolkit-0.1.2/AGENTS.md +179 -0
  9. ai_browser_toolkit-0.1.2/CLAUDE.md +14 -0
  10. ai_browser_toolkit-0.1.2/GEMINI.md +14 -0
  11. ai_browser_toolkit-0.1.2/LICENSE +201 -0
  12. ai_browser_toolkit-0.1.2/PKG-INFO +940 -0
  13. ai_browser_toolkit-0.1.2/README.md +921 -0
  14. ai_browser_toolkit-0.1.2/docs/TODO.md +18 -0
  15. ai_browser_toolkit-0.1.2/docs/engine-choice-for-profile-sessions.md +168 -0
  16. ai_browser_toolkit-0.1.2/docs/known-issues.md +395 -0
  17. ai_browser_toolkit-0.1.2/docs/packaging.md +104 -0
  18. ai_browser_toolkit-0.1.2/docs/playwright-spike-2026-08-19.md +205 -0
  19. ai_browser_toolkit-0.1.2/docs/session-review-20260806-linkedin.md +389 -0
  20. ai_browser_toolkit-0.1.2/docs/superpowers/plans/2026-08-19-browser-server-decoupling.md +2089 -0
  21. ai_browser_toolkit-0.1.2/docs/superpowers/plans/2026-08-20-packaging-and-distribution.md +2538 -0
  22. ai_browser_toolkit-0.1.2/docs/superpowers/specs/2026-08-03-selenium-browser-api-design.md +302 -0
  23. ai_browser_toolkit-0.1.2/docs/superpowers/specs/2026-08-19-browser-server-decoupling-design.md +459 -0
  24. ai_browser_toolkit-0.1.2/docs/superpowers/specs/2026-08-19-profile-sessions-design.md +350 -0
  25. ai_browser_toolkit-0.1.2/docs/superpowers/specs/2026-08-20-packaging-and-distribution-design.md +489 -0
  26. ai_browser_toolkit-0.1.2/guidelines/README.md +55 -0
  27. ai_browser_toolkit-0.1.2/guidelines/fojik-mlwbd.md +147 -0
  28. ai_browser_toolkit-0.1.2/guidelines/google-docs.md +101 -0
  29. ai_browser_toolkit-0.1.2/guidelines/google-forms-apps-script.md +210 -0
  30. ai_browser_toolkit-0.1.2/guidelines/google-sheets.md +163 -0
  31. ai_browser_toolkit-0.1.2/guidelines/kayoanime.md +138 -0
  32. ai_browser_toolkit-0.1.2/guidelines/messenger.md +222 -0
  33. ai_browser_toolkit-0.1.2/guidelines/toolkit-workflow.md +433 -0
  34. ai_browser_toolkit-0.1.2/packaging/aur/PKGBUILD.template +32 -0
  35. ai_browser_toolkit-0.1.2/packaging/aur/aibrowsertoolkit-bin.install +22 -0
  36. ai_browser_toolkit-0.1.2/packaging/bundle.py +228 -0
  37. ai_browser_toolkit-0.1.2/packaging/homebrew/formula.rb.template +43 -0
  38. ai_browser_toolkit-0.1.2/packaging/nfpm.yaml +28 -0
  39. ai_browser_toolkit-0.1.2/packaging/postinstall.sh +14 -0
  40. ai_browser_toolkit-0.1.2/packaging/preremove.sh +4 -0
  41. ai_browser_toolkit-0.1.2/packaging/scoop/manifest.json.template +31 -0
  42. ai_browser_toolkit-0.1.2/packaging/smoke.sh +54 -0
  43. ai_browser_toolkit-0.1.2/packaging/windows/abt.iss +124 -0
  44. ai_browser_toolkit-0.1.2/pyproject.toml +39 -0
  45. ai_browser_toolkit-0.1.2/src/abt/__init__.py +3 -0
  46. ai_browser_toolkit-0.1.2/src/abt/__main__.py +10 -0
  47. ai_browser_toolkit-0.1.2/src/abt/autostart.py +405 -0
  48. ai_browser_toolkit-0.1.2/src/abt/browser.py +851 -0
  49. ai_browser_toolkit-0.1.2/src/abt/cli.py +828 -0
  50. ai_browser_toolkit-0.1.2/src/abt/diff.py +652 -0
  51. ai_browser_toolkit-0.1.2/src/abt/doctor.py +220 -0
  52. ai_browser_toolkit-0.1.2/src/abt/engine.py +241 -0
  53. ai_browser_toolkit-0.1.2/src/abt/errors.py +34 -0
  54. ai_browser_toolkit-0.1.2/src/abt/frames.py +134 -0
  55. ai_browser_toolkit-0.1.2/src/abt/launch.py +64 -0
  56. ai_browser_toolkit-0.1.2/src/abt/mcp.py +438 -0
  57. ai_browser_toolkit-0.1.2/src/abt/messenger.py +782 -0
  58. ai_browser_toolkit-0.1.2/src/abt/ops/__init__.py +306 -0
  59. ai_browser_toolkit-0.1.2/src/abt/ops/control.py +165 -0
  60. ai_browser_toolkit-0.1.2/src/abt/ops/inspect.py +228 -0
  61. ai_browser_toolkit-0.1.2/src/abt/ops/interact.py +665 -0
  62. ai_browser_toolkit-0.1.2/src/abt/ops/navigate.py +41 -0
  63. ai_browser_toolkit-0.1.2/src/abt/ops/read.py +124 -0
  64. ai_browser_toolkit-0.1.2/src/abt/ops/tabs.py +24 -0
  65. ai_browser_toolkit-0.1.2/src/abt/paths.py +131 -0
  66. ai_browser_toolkit-0.1.2/src/abt/proc.py +208 -0
  67. ai_browser_toolkit-0.1.2/src/abt/pwdriver.py +1277 -0
  68. ai_browser_toolkit-0.1.2/src/abt/recorder.py +235 -0
  69. ai_browser_toolkit-0.1.2/src/abt/refs.py +91 -0
  70. ai_browser_toolkit-0.1.2/src/abt/schema.py +450 -0
  71. ai_browser_toolkit-0.1.2/src/abt/server.py +457 -0
  72. ai_browser_toolkit-0.1.2/src/abt/shadow.py +127 -0
  73. ai_browser_toolkit-0.1.2/src/abt/shots.py +148 -0
  74. ai_browser_toolkit-0.1.2/src/abt/targeting.py +359 -0
  75. ai_browser_toolkit-0.1.2/src/abt/viewer.py +453 -0
  76. ai_browser_toolkit-0.1.2/start-server.bat +155 -0
  77. ai_browser_toolkit-0.1.2/start-server.sh +178 -0
  78. ai_browser_toolkit-0.1.2/tests/conftest.py +202 -0
  79. ai_browser_toolkit-0.1.2/tests/fixtures/actionable.html +72 -0
  80. ai_browser_toolkit-0.1.2/tests/fixtures/canvas.html +42 -0
  81. ai_browser_toolkit-0.1.2/tests/fixtures/cards.html +17 -0
  82. ai_browser_toolkit-0.1.2/tests/fixtures/chainfetch.html +33 -0
  83. ai_browser_toolkit-0.1.2/tests/fixtures/controlled.html +41 -0
  84. ai_browser_toolkit-0.1.2/tests/fixtures/controlled_trusted.html +35 -0
  85. ai_browser_toolkit-0.1.2/tests/fixtures/delayed.html +13 -0
  86. ai_browser_toolkit-0.1.2/tests/fixtures/form.html +24 -0
  87. ai_browser_toolkit-0.1.2/tests/fixtures/framed_button.html +13 -0
  88. ai_browser_toolkit-0.1.2/tests/fixtures/framed_preload.html +11 -0
  89. ai_browser_toolkit-0.1.2/tests/fixtures/frames.html +19 -0
  90. ai_browser_toolkit-0.1.2/tests/fixtures/frames_cross.html +25 -0
  91. ai_browser_toolkit-0.1.2/tests/fixtures/frames_order.html +50 -0
  92. ai_browser_toolkit-0.1.2/tests/fixtures/frames_swap.html +29 -0
  93. ai_browser_toolkit-0.1.2/tests/fixtures/inspect.html +48 -0
  94. ai_browser_toolkit-0.1.2/tests/fixtures/late.html +25 -0
  95. ai_browser_toolkit-0.1.2/tests/fixtures/links.html +6 -0
  96. ai_browser_toolkit-0.1.2/tests/fixtures/messenger.html +130 -0
  97. ai_browser_toolkit-0.1.2/tests/fixtures/nav.html +24 -0
  98. ai_browser_toolkit-0.1.2/tests/fixtures/overlay.html +40 -0
  99. ai_browser_toolkit-0.1.2/tests/fixtures/repeated.html +41 -0
  100. ai_browser_toolkit-0.1.2/tests/fixtures/shadow.html +45 -0
  101. ai_browser_toolkit-0.1.2/tests/fixtures/slowfetch.html +28 -0
  102. ai_browser_toolkit-0.1.2/tests/test_actionable.py +225 -0
  103. ai_browser_toolkit-0.1.2/tests/test_autostart.py +166 -0
  104. ai_browser_toolkit-0.1.2/tests/test_bundle.py +110 -0
  105. ai_browser_toolkit-0.1.2/tests/test_click_at.py +109 -0
  106. ai_browser_toolkit-0.1.2/tests/test_diff.py +448 -0
  107. ai_browser_toolkit-0.1.2/tests/test_doctor.py +147 -0
  108. ai_browser_toolkit-0.1.2/tests/test_engine.py +147 -0
  109. ai_browser_toolkit-0.1.2/tests/test_frames.py +316 -0
  110. ai_browser_toolkit-0.1.2/tests/test_hit_test.py +112 -0
  111. ai_browser_toolkit-0.1.2/tests/test_inspect.py +389 -0
  112. ai_browser_toolkit-0.1.2/tests/test_launch.py +120 -0
  113. ai_browser_toolkit-0.1.2/tests/test_lifecycle.py +414 -0
  114. ai_browser_toolkit-0.1.2/tests/test_lifecycle_live.py +58 -0
  115. ai_browser_toolkit-0.1.2/tests/test_mcp.py +271 -0
  116. ai_browser_toolkit-0.1.2/tests/test_messenger.py +298 -0
  117. ai_browser_toolkit-0.1.2/tests/test_ops_click_options.py +128 -0
  118. ai_browser_toolkit-0.1.2/tests/test_ops_interact.py +212 -0
  119. ai_browser_toolkit-0.1.2/tests/test_ops_navigate.py +118 -0
  120. ai_browser_toolkit-0.1.2/tests/test_ops_read.py +108 -0
  121. ai_browser_toolkit-0.1.2/tests/test_ops_tabs.py +75 -0
  122. ai_browser_toolkit-0.1.2/tests/test_paths.py +132 -0
  123. ai_browser_toolkit-0.1.2/tests/test_proc.py +77 -0
  124. ai_browser_toolkit-0.1.2/tests/test_recorder.py +133 -0
  125. ai_browser_toolkit-0.1.2/tests/test_schema.py +62 -0
  126. ai_browser_toolkit-0.1.2/tests/test_server.py +131 -0
  127. ai_browser_toolkit-0.1.2/tests/test_server_logs.py +132 -0
  128. ai_browser_toolkit-0.1.2/tests/test_settle.py +143 -0
  129. ai_browser_toolkit-0.1.2/tests/test_shadow.py +211 -0
  130. ai_browser_toolkit-0.1.2/tests/test_shots.py +148 -0
  131. ai_browser_toolkit-0.1.2/tests/test_shots_live.py +113 -0
  132. ai_browser_toolkit-0.1.2/tests/test_targeting.py +101 -0
  133. ai_browser_toolkit-0.1.2/tests/test_workflows.py +143 -0
@@ -0,0 +1,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(git -C \"C:/Users/Dipto.SHAHARIARDIPTO/Documents/GitHub/aibrowsertoolkit\" log --oneline -10)",
5
+ "Bash(curl *)"
6
+ ]
7
+ }
8
+ }
@@ -0,0 +1,9 @@
1
+ # Line endings are load-bearing for the start scripts.
2
+ #
3
+ # This repo is developed on Windows with core.autocrlf=true, which would hand a
4
+ # CRLF start-server.sh to anyone cloning on Linux or macOS -- where bash reads
5
+ # the trailing \r as part of the command and every line fails. Pin each script
6
+ # to the endings its interpreter needs, whatever the cloning machine prefers.
7
+ * text=auto
8
+ *.sh text eol=lf
9
+ *.bat text eol=crlf
@@ -0,0 +1,51 @@
1
+ name: Check secrets
2
+
3
+ # Answers one question definitively: which secrets can a workflow in this
4
+ # repository actually see? An empty secret is not an error in Actions -- it
5
+ # interpolates as nothing, and the tool downstream reports it as bad
6
+ # credentials, which sends you looking in the wrong place. Every publisher
7
+ # failure on v0.1.1 read that way.
8
+ #
9
+ # Prints only set/empty and a length. Never a value, so the log is safe to
10
+ # paste anywhere.
11
+ #
12
+ # Worth knowing: Settings -> Secrets and variables has three separate stores
13
+ # -- Actions, Dependabot and Codespaces. They look identical in the UI and
14
+ # only the Actions one is visible here. Secrets attached to an *environment*
15
+ # are also invisible to any job that does not declare that environment.
16
+
17
+ on:
18
+ workflow_dispatch:
19
+
20
+ jobs:
21
+ check:
22
+ runs-on: ubuntu-24.04
23
+ steps:
24
+ - name: Report which secrets are visible
25
+ env:
26
+ TAP_TOKEN: ${{ secrets.TAP_TOKEN }}
27
+ WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }}
28
+ AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }}
29
+ FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
30
+ run: |
31
+ for name in TAP_TOKEN WINGET_TOKEN AUR_SSH_KEY FURY_TOKEN; do
32
+ value="${!name}"
33
+ if [ -z "$value" ]; then
34
+ echo "$name: EMPTY -- not visible to Actions in this repository"
35
+ else
36
+ echo "$name: set, ${#value} characters"
37
+ fi
38
+ done
39
+
40
+ - name: Sanity-check the shapes
41
+ env:
42
+ AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }}
43
+ run: |
44
+ # The most common AUR mistake is pasting the .pub half, which fails
45
+ # later with an unhelpful permission error rather than here.
46
+ case "$AUR_SSH_KEY" in
47
+ "") echo "AUR_SSH_KEY: empty, nothing to check" ;;
48
+ ssh-*) echo "AUR_SSH_KEY: looks like a PUBLIC key -- the private half is wanted" ; exit 1 ;;
49
+ *"BEGIN OPENSSH PRIVATE KEY"*) echo "AUR_SSH_KEY: looks like a private key" ;;
50
+ *) echo "AUR_SSH_KEY: set, but not in a shape I recognise" ;;
51
+ esac
@@ -0,0 +1,54 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ os: [ubuntu-24.04, windows-latest, macos-14]
14
+ python: ["3.11", "3.13"]
15
+ runs-on: ${{ matrix.os }}
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ # Deliberately not astral-sh/setup-uv here. uv is what the *bundle*
20
+ # build needs -- it knows how to fetch python-build-standalone -- but in
21
+ # a test job it introduces a second interpreter: setup-uv with a
22
+ # python-version creates and activates .venv, while `uv pip install
23
+ # --system` installs somewhere else, and the test step then reports
24
+ # "No module named pytest". One interpreter, named explicitly, end to
25
+ # end.
26
+ - uses: actions/setup-python@v5
27
+ with:
28
+ python-version: ${{ matrix.python }}
29
+
30
+ - name: Install
31
+ run: python -m pip install -e ".[dev]"
32
+
33
+ - name: Show what this machine has
34
+ # Dogfooding the dependency check, and the fastest way to read a
35
+ # browser-related failure below. Never fatal: doctor exits non-zero
36
+ # when no browser is present, which is information, not a broken job.
37
+ run: python -m abt doctor || true
38
+
39
+ - name: Test
40
+ # The live tests need a real browser on a real profile; everything
41
+ # else runs anywhere.
42
+ env:
43
+ # This runner is several times slower than a developer machine, and
44
+ # it blew a different budget each run: first a click timeout
45
+ # ("ElementHandle.click: Timeout 3000ms exceeded", already at
46
+ # "performing click action" -- slow, not stuck), then a settle grace
47
+ # that called a page idle mid-fetch and snapshotted the spinner.
48
+ # One multiplier scales them together rather than chasing each.
49
+ # Not a retry: a retry would hide a genuine hang just as well.
50
+ ABT_TEST_SLOWDOWN: "5"
51
+ run: >
52
+ python -m pytest -q
53
+ --deselect tests/test_lifecycle_live.py
54
+ --deselect tests/test_shots_live.py
@@ -0,0 +1,448 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+ workflow_dispatch:
7
+ inputs:
8
+ dry_run:
9
+ description: "Build and smoke-test everything, publish nothing"
10
+ type: boolean
11
+ default: true
12
+
13
+ permissions:
14
+ contents: write
15
+ id-token: write
16
+ attestations: write
17
+
18
+ jobs:
19
+ test:
20
+ runs-on: ubuntu-24.04
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: actions/setup-python@v5
24
+ with:
25
+ python-version: "3.13"
26
+ - run: python -m pip install -e ".[dev]"
27
+ - env:
28
+ # Same multiplier as ci.yml. A release must not fail on a budget
29
+ # that only a loaded runner blows.
30
+ ABT_TEST_SLOWDOWN: "5"
31
+ run: >
32
+ python -m pytest -q
33
+ --deselect tests/test_lifecycle_live.py
34
+ --deselect tests/test_shots_live.py
35
+
36
+ wheel:
37
+ needs: test
38
+ runs-on: ubuntu-24.04
39
+ outputs:
40
+ version: ${{ steps.v.outputs.version }}
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ - uses: actions/setup-python@v5
44
+ with:
45
+ python-version: "3.13"
46
+
47
+ - name: Read the version from pyproject
48
+ id: v
49
+ run: |
50
+ version=$(python -c "import tomllib,pathlib;print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
51
+ echo "version=$version" >> "$GITHUB_OUTPUT"
52
+ echo "pyproject says $version"
53
+
54
+ - name: Guard the tag against the version
55
+ if: startsWith(github.ref, 'refs/tags/')
56
+ run: |
57
+ tag="${GITHUB_REF_NAME#v}"
58
+ if [ "$tag" != "${{ steps.v.outputs.version }}" ]; then
59
+ echo "tag $GITHUB_REF_NAME does not match pyproject version ${{ steps.v.outputs.version }}"
60
+ echo "bump pyproject.toml first, commit, then tag -- never the reverse."
61
+ exit 1
62
+ fi
63
+
64
+ - name: Build wheel and sdist
65
+ run: |
66
+ python -m pip install build
67
+ python -m build
68
+
69
+ - uses: actions/upload-artifact@v4
70
+ with:
71
+ name: dist
72
+ path: dist/
73
+
74
+ bundle:
75
+ needs: wheel
76
+ strategy:
77
+ fail-fast: false
78
+ matrix:
79
+ include:
80
+ - target: linux-x86_64
81
+ os: ubuntu-24.04
82
+ - target: linux-aarch64
83
+ os: ubuntu-24.04-arm
84
+ - target: macos-arm64
85
+ os: macos-14
86
+ # macos-x86_64 is deliberately absent. macos-13 is GitHub's last
87
+ # Intel image and is being retired, so it queues badly and held up
88
+ # every release behind it. bundle.py still knows the target, so an
89
+ # Intel Mac can build one locally; nothing ships it automatically.
90
+ # The Homebrew formula is arm64-only as a result -- see
91
+ # docs/packaging.md.
92
+ - target: windows-x86_64
93
+ os: windows-latest
94
+ runs-on: ${{ matrix.os }}
95
+ steps:
96
+ - uses: actions/checkout@v4
97
+
98
+ # setup-python runs bundle.py; setup-uv supplies the `uv` binary the
99
+ # builder shells out to. Deliberately kept apart: setup-uv given a
100
+ # python-version creates and activates its own .venv, and then two
101
+ # interpreters disagree about what is installed.
102
+ - uses: actions/setup-python@v5
103
+ with:
104
+ python-version: "3.13"
105
+ - uses: astral-sh/setup-uv@v5
106
+
107
+ - uses: actions/download-artifact@v4
108
+ with:
109
+ name: dist
110
+ path: dist
111
+
112
+ - name: Build the bundle
113
+ shell: bash
114
+ run: |
115
+ wheel=$(ls dist/*.whl)
116
+ python packaging/bundle.py \
117
+ --version "${{ needs.wheel.outputs.version }}" \
118
+ --target "${{ matrix.target }}" \
119
+ --wheel "$wheel" \
120
+ --dest dist/
121
+
122
+ - name: Smoke test
123
+ shell: bash
124
+ run: |
125
+ bash packaging/smoke.sh \
126
+ "dist/aibrowsertoolkit-${{ needs.wheel.outputs.version }}-${{ matrix.target }}"
127
+
128
+ # Named exactly, not globbed. `python -m build` puts an sdist .tar.gz in
129
+ # dist/ too, and this job downloads it -- so `dist/*.tar.gz` would sweep
130
+ # the sdist into all four Unix bundle artifacts, and the release job
131
+ # would then publish it five times over.
132
+ - uses: actions/upload-artifact@v4
133
+ with:
134
+ name: bundle-${{ matrix.target }}
135
+ if-no-files-found: error
136
+ path: |
137
+ dist/aibrowsertoolkit-${{ needs.wheel.outputs.version }}-${{ matrix.target }}.tar.gz
138
+ dist/aibrowsertoolkit-${{ needs.wheel.outputs.version }}-${{ matrix.target }}.zip
139
+
140
+ # Built here rather than in a job of its own. GitHub Actions cannot
141
+ # depend on a single matrix cell, so `needs: bundle` waits for all five
142
+ # -- which left the Windows installer blocked behind the macOS builds
143
+ # despite consuming only the Windows tree. Inside the cell it is ready
144
+ # the moment Windows is.
145
+ #
146
+ # No unzip step either: bundle.py leaves the unpacked tree beside the
147
+ # archive, so Inno points straight at it.
148
+ - name: Compile the installer
149
+ if: matrix.target == 'windows-x86_64'
150
+ shell: bash
151
+ env:
152
+ # `shell: bash` on a Windows runner is Git Bash, which rewrites any
153
+ # argument beginning with `/` into a Windows path. That turns
154
+ # ISCC's own switches -- /DAppVersion, /DPayloadDir, /O -- into
155
+ # filenames, and it fails with the wonderfully misleading "You may
156
+ # not specify more than one script filename". Found by compiling
157
+ # locally in Git Bash, which reproduces the runner exactly.
158
+ MSYS2_ARG_CONV_EXCL: "*"
159
+ run: |
160
+ # Forward slashes throughout: Win32 accepts them, and cmd-style
161
+ # backslashes in YAML are a quoting trap for no benefit.
162
+ iscc="/c/Program Files (x86)/Inno Setup 6/ISCC.exe"
163
+ if [ ! -x "$iscc" ]; then iscc=$(command -v iscc || true); fi
164
+ if [ -z "$iscc" ]; then echo "Inno Setup not found on this runner"; exit 1; fi
165
+ root=$(pwd -W)
166
+ tree="$root/dist/aibrowsertoolkit-${{ needs.wheel.outputs.version }}-windows-x86_64"
167
+ "$iscc" "/DAppVersion=${{ needs.wheel.outputs.version }}" "/DPayloadDir=$tree" "/O$root/out" packaging/windows/abt.iss
168
+
169
+ - name: Upload the installer
170
+ if: matrix.target == 'windows-x86_64'
171
+ uses: actions/upload-artifact@v4
172
+ with:
173
+ name: installer
174
+ if-no-files-found: error
175
+ path: out/*.exe
176
+
177
+ release:
178
+ needs: [wheel, bundle]
179
+ # Both halves matter. The ref check keeps a branch dispatch from
180
+ # publishing; the dry_run check keeps a *tag* dispatch from publishing
181
+ # when the person running it asked for a rehearsal. Without the second,
182
+ # the input would be decorative and the surprise would be a real release.
183
+ if: startsWith(github.ref, 'refs/tags/') && github.event.inputs.dry_run != 'true'
184
+ runs-on: ubuntu-24.04
185
+ steps:
186
+ - uses: actions/download-artifact@v4
187
+ with:
188
+ path: staging
189
+
190
+ - name: Collect every artifact into one directory
191
+ run: |
192
+ set -eu
193
+ mkdir -p out
194
+ find staging -type f \( -name '*.tar.gz' -o -name '*.zip' \
195
+ -o -name '*.whl' -o -name '*.exe' \) -exec cp -n {} out/ \;
196
+ ls -l out
197
+ # Four bundles + wheel + sdist + installer. A missing one means a job
198
+ # was skipped, and shipping a partial release is worse than failing
199
+ # here -- the publishers downstream would each pin a URL that never
200
+ # gets an asset.
201
+ count=$(ls out | wc -l)
202
+ if [ "$count" -lt 7 ]; then
203
+ echo "expected at least 7 artifacts, found $count"
204
+ exit 1
205
+ fi
206
+
207
+ - name: Checksums
208
+ # Generated before the attestation so checksums.txt is itself attested.
209
+ run: cd out && sha256sum * > checksums.txt && cat checksums.txt
210
+
211
+ - uses: actions/attest-build-provenance@v2
212
+ with:
213
+ subject-path: "out/*"
214
+
215
+ - uses: softprops/action-gh-release@v2
216
+ with:
217
+ files: out/*
218
+ generate_release_notes: true
219
+ prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }}
220
+
221
+ pypi:
222
+ needs: release
223
+ if: startsWith(github.ref, 'refs/tags/') && github.event.inputs.dry_run != 'true'
224
+ runs-on: ubuntu-24.04
225
+ # Trusted Publishing: no API token is stored anywhere. The environment
226
+ # name and workflow filename below must match the pending publisher
227
+ # registered on PyPI exactly, or this fails with "not a trusted
228
+ # publisher" -- and that can only be found by releasing, which is what
229
+ # the dry_run input exists to avoid doing twice.
230
+ environment: pypi
231
+ permissions:
232
+ id-token: write
233
+ steps:
234
+ - uses: actions/download-artifact@v4
235
+ with:
236
+ name: dist
237
+ path: dist
238
+ - uses: pypa/gh-action-pypi-publish@release/v1
239
+
240
+ # ---------------------------------------------------------------------
241
+ # Publishers. Independent by design: each reads the finished release, so
242
+ # one failing channel leaves the other five shipped and the fix is a
243
+ # re-run of a single job rather than of the release.
244
+ # ---------------------------------------------------------------------
245
+
246
+ winget:
247
+ needs: [wheel, release]
248
+ # Release candidates are excluded: the winget community repository does
249
+ # not want them.
250
+ if: startsWith(github.ref, 'refs/tags/') && !contains(github.ref_name, '-rc')
251
+ runs-on: ubuntu-24.04
252
+ # winget-releaser only *updates* an identifier that already exists in
253
+ # microsoft/winget-pkgs; it cannot create one. Until the first version is
254
+ # submitted by hand with `wingetcreate new`, this fails with "Package
255
+ # skssmd.AIBrowserToolkit does not exist in the winget-pkgs repository".
256
+ #
257
+ # Non-fatal so one manual prerequisite does not colour every release red.
258
+ # REMOVE THIS LINE once the package exists upstream -- leaving it there
259
+ # would hide a genuine winget failure later.
260
+ continue-on-error: true
261
+ steps:
262
+ # Reads the finished release and derives the three manifests from the
263
+ # installer asset. WINGET_TOKEN must be a *classic* PAT with
264
+ # public_repo -- this action does not work with fine-grained tokens.
265
+ - uses: vedantmgoyal9/winget-releaser@main
266
+ with:
267
+ identifier: skssmd.AIBrowserToolkit
268
+ version: ${{ needs.wheel.outputs.version }}
269
+ installers-regex: '-setup\.exe$'
270
+ fork-user: skssmd
271
+ token: ${{ secrets.WINGET_TOKEN }}
272
+
273
+ scoop:
274
+ needs: [wheel, release]
275
+ if: startsWith(github.ref, 'refs/tags/')
276
+ runs-on: ubuntu-24.04
277
+ steps:
278
+ - uses: actions/checkout@v4
279
+ - uses: actions/download-artifact@v4
280
+ with:
281
+ name: bundle-windows-x86_64
282
+ path: dl
283
+
284
+ - name: Render the manifest
285
+ run: |
286
+ set -eu
287
+ version="${{ needs.wheel.outputs.version }}"
288
+ hash=$(sha256sum dl/*.zip | cut -d' ' -f1)
289
+ sed -e "s/@VERSION@/$version/g" -e "s/@HASH@/$hash/g" \
290
+ packaging/scoop/manifest.json.template > aibrowsertoolkit.json
291
+ cat aibrowsertoolkit.json
292
+
293
+ - name: Push to the bucket
294
+ env:
295
+ TOKEN: ${{ secrets.TAP_TOKEN }}
296
+ run: |
297
+ set -eu
298
+ git clone "https://x-access-token:$TOKEN@github.com/skssmd/scoop-bucket.git" bucket
299
+ mkdir -p bucket/bucket
300
+ cp aibrowsertoolkit.json bucket/bucket/aibrowsertoolkit.json
301
+ cd bucket
302
+ git config user.name "skssmd"
303
+ git config user.email "skssmd78475@gmail.com"
304
+ git add bucket/aibrowsertoolkit.json
305
+ git diff --cached --quiet && exit 0
306
+ git commit -m "aibrowsertoolkit: ${{ needs.wheel.outputs.version }}"
307
+ # Graft's release workflow writes to this repo too; a simultaneous
308
+ # release would otherwise lose this push to a non-fast-forward.
309
+ # Push first, rebase only when the push is rejected. `git pull
310
+ # --rebase` fails outright against a repository with no commits --
311
+ # "no such ref was fetched" -- which is exactly the state a freshly
312
+ # created bucket is in, so pulling first meant the very first
313
+ # release could never land. The rebase still covers the real case
314
+ # it was written for: a concurrent write to a shared repo.
315
+ for attempt in 1 2 3; do
316
+ git push -u origin HEAD:main && exit 0
317
+ git pull --rebase origin main || true
318
+ sleep 5
319
+ done
320
+ exit 1
321
+
322
+ brew:
323
+ needs: [wheel, release]
324
+ if: startsWith(github.ref, 'refs/tags/')
325
+ runs-on: ubuntu-24.04
326
+ steps:
327
+ - uses: actions/checkout@v4
328
+ - uses: actions/download-artifact@v4
329
+ with:
330
+ name: bundle-macos-arm64
331
+ path: dl
332
+
333
+ - name: Render the formula
334
+ run: |
335
+ set -eu
336
+ version="${{ needs.wheel.outputs.version }}"
337
+ arm=$(sha256sum dl/*.tar.gz | cut -d' ' -f1)
338
+ sed -e "s/@VERSION@/$version/g" -e "s/@SHA_ARM@/$arm/g" \
339
+ packaging/homebrew/formula.rb.template > aibrowsertoolkit.rb
340
+ cat aibrowsertoolkit.rb
341
+
342
+ - name: Push to the tap
343
+ env:
344
+ TOKEN: ${{ secrets.TAP_TOKEN }}
345
+ run: |
346
+ set -eu
347
+ git clone "https://x-access-token:$TOKEN@github.com/skssmd/homebrew-tap.git" tap
348
+ mkdir -p tap/Formula
349
+ cp aibrowsertoolkit.rb tap/Formula/aibrowsertoolkit.rb
350
+ cd tap
351
+ git config user.name "skssmd"
352
+ git config user.email "skssmd78475@gmail.com"
353
+ git add Formula/aibrowsertoolkit.rb
354
+ git diff --cached --quiet && exit 0
355
+ git commit -m "aibrowsertoolkit ${{ needs.wheel.outputs.version }}"
356
+ # Graft writes Formula/graft.rb in this same repo.
357
+ # Push first, rebase only when the push is rejected. `git pull
358
+ # --rebase` fails outright against a repository with no commits --
359
+ # "no such ref was fetched" -- which is exactly the state a freshly
360
+ # created bucket is in, so pulling first meant the very first
361
+ # release could never land. The rebase still covers the real case
362
+ # it was written for: a concurrent write to a shared repo.
363
+ for attempt in 1 2 3; do
364
+ git push -u origin HEAD:main && exit 0
365
+ git pull --rebase origin main || true
366
+ sleep 5
367
+ done
368
+ exit 1
369
+
370
+ aur:
371
+ needs: [wheel, release]
372
+ if: startsWith(github.ref, 'refs/tags/') && !contains(github.ref_name, '-rc')
373
+ runs-on: ubuntu-24.04
374
+ steps:
375
+ - uses: actions/checkout@v4
376
+ - uses: actions/download-artifact@v4
377
+ with:
378
+ pattern: bundle-linux-*
379
+ path: dl
380
+
381
+ - name: Render the PKGBUILD
382
+ run: |
383
+ set -eu
384
+ version="${{ needs.wheel.outputs.version }}"
385
+ x86=$(sha256sum dl/bundle-linux-x86_64/*.tar.gz | cut -d' ' -f1)
386
+ arm=$(sha256sum dl/bundle-linux-aarch64/*.tar.gz | cut -d' ' -f1)
387
+ mkdir -p aurpkg
388
+ sed -e "s/@VERSION@/$version/g" -e "s/@SHA_X86@/$x86/g" -e "s/@SHA_ARM@/$arm/g" \
389
+ packaging/aur/PKGBUILD.template > aurpkg/PKGBUILD
390
+ cp packaging/aur/aibrowsertoolkit-bin.install aurpkg/
391
+ cat aurpkg/PKGBUILD
392
+
393
+ - uses: KSXGitHub/github-actions-deploy-aur@v3
394
+ with:
395
+ pkgname: aibrowsertoolkit-bin
396
+ pkgbuild: aurpkg/PKGBUILD
397
+ assets: aurpkg/aibrowsertoolkit-bin.install
398
+ commit_username: skssmd
399
+ commit_email: skssmd78475@gmail.com
400
+ ssh_private_key: ${{ secrets.AUR_SSH_KEY }}
401
+ commit_message: "aibrowsertoolkit-bin ${{ needs.wheel.outputs.version }}"
402
+
403
+ fury:
404
+ needs: [wheel, release]
405
+ if: startsWith(github.ref, 'refs/tags/')
406
+ runs-on: ubuntu-24.04
407
+ steps:
408
+ - uses: actions/checkout@v4
409
+ - uses: actions/download-artifact@v4
410
+ with:
411
+ pattern: bundle-linux-*
412
+ path: dl
413
+
414
+ - name: Install nfpm
415
+ env:
416
+ # Pinned on purpose: a release pipeline that silently picks up a new
417
+ # packaging tool is one that breaks on a day you changed nothing.
418
+ NFPM_VERSION: "2.41.3"
419
+ run: |
420
+ set -eu
421
+ curl -fsSL -o nfpm.tar.gz \
422
+ "https://github.com/goreleaser/nfpm/releases/download/v${NFPM_VERSION}/nfpm_${NFPM_VERSION}_Linux_x86_64.tar.gz"
423
+ tar xf nfpm.tar.gz nfpm && sudo mv nfpm /usr/local/bin/
424
+ nfpm --version
425
+
426
+ - name: Build and push every package
427
+ env:
428
+ PKG_VERSION: ${{ needs.wheel.outputs.version }}
429
+ FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
430
+ run: |
431
+ set -eu
432
+ chmod +x packaging/postinstall.sh packaging/preremove.sh
433
+ mkdir -p out
434
+ for pair in "x86_64:amd64" "aarch64:arm64"; do
435
+ target="${pair%%:*}"
436
+ PKG_ARCH="${pair##*:}"
437
+ export PKG_ARCH
438
+ rm -rf payload && mkdir payload
439
+ tar xzf dl/bundle-linux-$target/*.tar.gz -C payload --strip-components=1
440
+ for fmt in deb rpm apk; do
441
+ nfpm pkg --config packaging/nfpm.yaml --packager "$fmt" --target out/
442
+ done
443
+ done
444
+ ls -l out
445
+ for pkg in out/*; do
446
+ echo "pushing $pkg"
447
+ curl -fsS -F package=@"$pkg" "https://$FURY_TOKEN@push.fury.io/skssmd/"
448
+ done
@@ -0,0 +1,31 @@
1
+ .venv/
2
+ # Browser profiles. One directory per named profile, and every one of them
3
+ # holds live logins -- see docs/superpowers/specs on profile sessions.
4
+ profiles/
5
+ logs/
6
+ .e2e-profile/
7
+ .demo-profile/
8
+ logs-demo/
9
+ .e2e.log
10
+ .e2e.err
11
+ server.log
12
+ server.err
13
+ server-*.log
14
+ server-*.err
15
+ demo.log
16
+ __pycache__/
17
+ *.py[cod]
18
+ *.egg-info/
19
+ .pytest_cache/
20
+ build/
21
+ dist/
22
+ /guidelines/onehr
23
+
24
+ # OneHr client material. The playbooks and session write-ups name a real
25
+ # organisation, its routes and its staff records, so they stay on disk and out
26
+ # of the repo. Name-matched rather than path-matched so a new write-up is
27
+ # covered the moment it is created, not the next time someone remembers.
28
+ # Case variants because git's ignore matching is case-sensitive by default.
29
+ *onehr*
30
+ *OneHr*
31
+ *ONEHR*
@@ -0,0 +1 @@
1
+ https://docs.google.com/spreadsheets/d/1-y9x7VJLL7bTN4rSnazxMB8DRdZtOpX8_YlW8PKHZQM/edit?gid=0#gid=0