Skip to content

Commit 0470ac4

Browse files
ankitranjan7claudebeubax
authored
refactor: move all site adapters to independent plugins (#216)
* docs: design LinkedIn post-comments command * feat: add LinkedIn post-comments command * fix: complete LinkedIn comment collection * refactor: move LinkedIn commands to community plugin * feat: add public Webcmd plugin runtime * fix: preserve plugin runtime error messages * feat: add independent plugin migration infrastructure * fix: make plugin migrations collision safe * refactor: migrate public API adapters to plugins * refactor: migrate package and Jira adapters to plugins * fix: make plugin manifest resolution fail closed * refactor: migrate reference adapters to plugins * refactor: consolidate PyPI commands in plugin * refactor: migrate search and desktop adapters to plugins * refactor: migrate content and market adapters to plugins * refactor: migrate commerce and utility adapters to plugins * refactor: migrate chat and professional adapters to plugins * refactor: migrate document and messaging adapters to plugins * refactor: complete independent adapter plugin migration * refactor: make Webcmd core adapter-free * fix: address plugin migration final review * test: make CI portability checks deterministic * test: fix Windows plugin path portability * fix: match migrated plugin webcmd version floor to the shipping release migrate-cli-sites.mjs hardcoded webcmd: ">=0.6.0" for every plugin it generated a fresh manifest for, but package.json ships as 0.5.3 in this release. Every one of the 108 affected plugins (including linkedin) was uninstallable: `webcmd plugin install` hard-fails checkCompatibility() with "Plugin requires webcmd >=0.6.0, but current version is incompatible" before it ever reaches npm. Derive the floor from package.json's actual version instead of a literal, and regenerate the already-committed manifests (all plugins/*/webcmd-plugin.json + package.json, and the aggregated root webcmd-plugin.json catalog) to match. Update the two unit tests that had the old literal baked into their expectations. * test: install fixture plugins in e2e/smoke suites now that core ships no sites Several e2e and smoke tests invoked site commands (hackernews, dictionary, google, imdb, apple-podcasts, yollomi, paperreview) and asserted on `list` output size/contents, assuming site adapters were bundled in core. After this migration core registers zero site commands until a plugin is installed, so these tests failed deterministically in any clean environment (e.g. `list` returns 0 commands, not >50). Add installFixturePlugin() to place a repo-local plugin directly under an isolated HOME's .webcmd/plugins/ (skipping `plugin install`'s npm step, which only resolves a peerDependency these fixture plugins don't otherwise need and which fails until the in-progress release is actually published). Each affected suite now installs the specific plugin(s) it exercises before running. The smoke test's "all expected sites registered" check is rewritten against the plugin catalog under plugins/, since that's the equivalent invariant post-migration. * docs: fix bundled skills to describe the plugin architecture, not clis/ webcmd-usage, webcmd-adapter-author, and webcmd-autofix (all shipped to npm under skills/**) still described a repo-root clis/ directory as the "official bundle" location for built-in adapters. That directory no longer exists — every main-repo site, official or community, lives under plugins/<site>/ now. Left the ~/.webcmd/clis/ references alone: that private local-iteration directory is unaffected by this migration. * feat: hint at plugin search/install when webcmd list has no sites Now that core ships zero bundled site adapters, an empty `list` (0 sites) is the expected default state on a fresh install rather than a sign something's broken. Point the user at `plugin search`/`plugin install` instead of leaving them looking at a bare zero-count line. * chore: stop committing the generated plugin-command-manifest.json It's a build artifact (npm run build-plugin-manifest scans plugins/* via a Node loader and writes it fresh), read only by CI-time checks within the same run: check-plugin-command-parity, check-typed-error-lint, check-silent-column-drop, and convention-audit. Nothing reads it from a published package or at CLI/hosted runtime, so it doesn't need to be in the npm tarball or in git history — same treatment as hosted-contract.json, which was already gitignored for this reason. * fix: generate plugin-command-manifest.json as part of npm run build Gitignoring it in 4aec6c5 broke the unit-test CI job: that job runs on a separate runner with a fresh checkout and only does `npm ci` + vitest, so the file no longer existed for the three test files that read it from the package root (hosted/availability, hosted/file-contract, build-manifest) — 5 tests failed with ENOENT. hosted-contract.json is safely gitignored only because `npm run build` always regenerates it. Give the plugin manifest the same guarantee instead of leaving it as a build artifact nothing in the install path produces. Costs ~0.9s per build. * feat: export deriveHostedAvailability for hosted consumers * fix: handle ports in local IPv4 address detection for deriveHostedAvailability The isLocalIpDomain function now strips port numbers from domain strings (e.g. '127.0.0.1:3000') before validating the IPv4 format. This ensures desktop-app adapters running on local IP addresses with ports are correctly classified as 'app' rather than 'site'. Includes regression tests to prevent port-handling regressions. * feat: support plugin list, uninstall, update, and create in hosted mode * fix: refuse to update a plugin with uncommitted changes unless forced updatePlugin/updateAllPlugins now check for tracked-file modifications in both the standalone plugin directory and the shared monorepo clone before beginReplaceDir wipes them, with --force to opt out. Local (symlinked) installs are unaffected since they never go through beginReplaceDir. * fix: count untracked files as dirty in the plugin update guard git status --porcelain already omits gitignored paths (node_modules/dist never show up), so --untracked-files=no was only hiding the case the guard exists to catch: a new, unstaged command file. Drop the flag, label refusal entries as new-unstaged vs modified for clarity. * ci: reconcile the hosted plugin catalog on merge to main Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: clarify --update-env-vars semantics and workflow_dispatch usage Add inline comments explaining that --update-env-vars on 'execute' is a per-execution override (not a job-spec mutation) and that workflow_dispatch runs should target main to avoid delisting plugins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: fail closed on unexpected git errors in the plugin dirty-checkout guard getDirtyFiles previously treated any git failure (missing binary, "detected dubious ownership in repository", permission errors) as "clean", letting beginReplaceDir silently destroy uncommitted work. It also ran `git status --porcelain` with no pathspec, which reports the whole enclosing repository rather than just the plugin directory, causing spurious refusals for plugins inside a larger repo (e.g. a dotfiles-tracked home directory). Now getDirtyFiles probes with `git rev-parse --git-dir` first: a genuine non-repository still proceeds, but any other failure refuses the update with a message pointing at --force. `git status` is scoped with `-- .` so only the plugin directory is considered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: ensure parent directories exist before writing plugin output files * chore: update lockfile dependencies for package-lock.json --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: beubax <tejasr@bu.edu>
1 parent f4622fb commit 0470ac4

1,565 files changed

Lines changed: 38939 additions & 31612 deletions

File tree

Some content is hidden

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

.github/workflows/ci.yml

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,22 @@ jobs:
4747
- name: Build
4848
run: npm run build
4949

50+
- name: Build plugin command manifest
51+
if: runner.os == 'Linux'
52+
run: npm run build-plugin-manifest
53+
54+
- name: Check plugin command parity
55+
if: runner.os == 'Linux'
56+
run: npm run check:plugin-parity
57+
5058
- name: Check generated contract artifacts
5159
if: runner.os == 'Linux'
5260
run: npm run check:hosted-contract
5361

62+
- name: Check all generated artifacts are committed
63+
if: runner.os == 'Linux'
64+
run: git diff --exit-code -- cli-manifest.json hosted-contract.json webcmd-plugin.json README.md
65+
5466
- name: Verify packed CLI executables
5567
run: npm run check:package-bin
5668

@@ -85,10 +97,14 @@ jobs:
8597
- name: Run unit tests
8698
run: npx vitest run --project unit --reporter=verbose --shard=${{ matrix.shard }}/2
8799

88-
adapter-test:
89-
name: Adapter tests
100+
plugin-test:
101+
name: Plugin tests (${{ matrix.os }})
90102
needs: build
91-
runs-on: ubuntu-latest
103+
runs-on: ${{ matrix.os }}
104+
strategy:
105+
fail-fast: false
106+
matrix:
107+
os: [ubuntu-latest, macos-latest, windows-latest]
92108
steps:
93109
- uses: actions/checkout@v6
94110

@@ -100,8 +116,11 @@ jobs:
100116
- name: Install dependencies
101117
run: npm ci
102118

103-
- name: Run adapter tests
104-
run: npm run test:adapter -- --reporter=verbose
119+
- name: Build
120+
run: npm run build
121+
122+
- name: Run plugin tests
123+
run: npm run test:plugin -- --reporter=verbose
105124

106125
bun-test:
107126
name: Bun compatibility
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Required repository secrets:
2+
# GCP_WORKLOAD_IDENTITY_PROVIDER — workload identity provider resource name
3+
# GCP_RECONCILE_SERVICE_ACCOUNT — service account with run.jobs.run on the job
4+
# GCP_REGION — region hosting webcmd-reconcile-marketplace
5+
#
6+
# The Cloud Run Job webcmd-reconcile-marketplace must exist and run
7+
# `npm run job:reconcile-marketplace` with DATABASE_URL, WEBCMD_ARTIFACT_ROOT,
8+
# and GITHUB_TOKEN configured.
9+
10+
name: Reconcile hosted plugin catalog
11+
12+
on:
13+
push:
14+
branches: [main]
15+
paths:
16+
- 'plugins/**'
17+
- 'webcmd-plugin.json'
18+
workflow_dispatch:
19+
# Note: manual runs should be triggered against main to ensure github.sha
20+
# points to the current tip. Reconciling an older ref would delist plugins
21+
# added since that commit.
22+
23+
concurrency:
24+
group: reconcile-hosted-plugins
25+
cancel-in-progress: false
26+
27+
jobs:
28+
reconcile:
29+
runs-on: ubuntu-latest
30+
permissions:
31+
contents: read
32+
id-token: write
33+
steps:
34+
- name: Authenticate to Google Cloud
35+
uses: google-github-actions/auth@v2
36+
with:
37+
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
38+
service_account: ${{ secrets.GCP_RECONCILE_SERVICE_ACCOUNT }}
39+
40+
- name: Set up gcloud CLI
41+
uses: google-github-actions/setup-gcloud@v2
42+
43+
- name: Execute Cloud Run Job
44+
run: |
45+
# Note: --update-env-vars on 'execute' (not 'update') is a per-execution
46+
# override that does not modify the job's stored configuration. See:
47+
# "environment variables overrides for an execution of a job"
48+
gcloud run jobs execute webcmd-reconcile-marketplace \
49+
--region "${{ secrets.GCP_REGION }}" \
50+
--update-env-vars "WEBCMD_PLUGINS_COMMIT=${{ github.sha }}" \
51+
--wait

.github/workflows/release.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,26 @@ jobs:
6868
if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }}
6969
run: npm run build
7070

71+
- name: Build plugin command manifest
72+
if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }}
73+
run: npm run build-plugin-manifest
74+
75+
- name: Check plugin command parity
76+
if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }}
77+
run: npm run check:plugin-parity
78+
79+
- name: Check community plugin metadata
80+
if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }}
81+
run: npm run check-community-plugins
82+
7183
- name: Check generated contract artifacts
7284
if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }}
7385
run: npm run check:hosted-contract
7486

87+
- name: Check all generated artifacts are committed
88+
if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }}
89+
run: git diff --exit-code -- cli-manifest.json hosted-contract.json webcmd-plugin.json README.md
90+
7591
- name: Check Codex plugin metadata
7692
if: ${{ steps.release.outputs.release_created || inputs.publish_tag != '' }}
7793
run: npm run check:codex-plugin

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ dist/
33
!extension/dist/
44
*.tsbuildinfo
55
hosted-contract.json
6+
plugin-command-manifest.json
67
.webcmd/
78
.superpowers/
89
.worktrees/

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,21 @@ skills with `webcmd skills add` in Codex.
6161

6262
### Other agents or plugin-free setup
6363

64-
Webcmd requires Node.js 20+.
64+
Webcmd requires Node.js 20.6+.
6565

6666
```bash
6767
npm install -g @agentrhq/webcmd
6868
```
6969

70+
The npm package ships the Webcmd core and browser commands, but no site
71+
adapters. Search the plugin catalog and explicitly install the adapter you
72+
need:
73+
74+
```bash
75+
webcmd plugin search <site> -f json
76+
webcmd plugin install <installSource-from-search>
77+
```
78+
7079
```bash
7180
webcmd skills add
7281
```
@@ -110,7 +119,8 @@ Beyond website adapters, Webcmd can work through authenticated browser sessions,
110119
| AI tools | ChatGPT, Claude, Gemini, NotebookLM | Retrieve conversations, research outputs, notebooks, and generated materials from the tools you already use. |
111120
| shopping and bookings | Amazon, Blinkit, Zepto, BigBasket, District, Practo | Compare products, availability, prices, appointments, events, and delivery options. |
112121

113-
This list is illustrative; ask your agent to use webcmd to discover what is currently available.
122+
This list is illustrative; availability comes from installed plugins. Ask your
123+
agent to search and install the relevant plugin when a site is not installed.
114124

115125
## Learn More
116126

@@ -130,7 +140,7 @@ Webcmd Cloud can run supported commands and browser sessions on hosted infrastru
130140

131141
| Plugin | Description | Author |
132142
| --- | --- | --- |
133-
| [`pypi`](./plugins/pypi/) | Inspect public Python package metadata and releases from PyPI | [Kemal Kaya](https://github.com/yoldaolmak) |
143+
| [`pypi`](./plugins/pypi/) | Inspect public Python package metadata, downloads, and releases from PyPI | [Kemal Kaya](https://github.com/yoldaolmak) |
134144
| [`skyscanner`](./plugins/skyscanner/) | Skyscanner flight search commands for Webcmd | [Rishabh](https://github.com/rishabhraj36) |
135145
<!-- webcmd-community-plugins:end -->
136146

TESTING.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@
55
```bash
66
npm run typecheck
77
npm run build
8+
npm run build-plugin-manifest
89
npm test
910
```
1011

12+
`npm run build` must run before plugin tests because repository plugins import
13+
the compiled public package exports. The core package contains no site
14+
adapters; `npm test` runs the unit and generic plugin projects.
15+
1116
## Focused Checks
1217

1318
```bash
1419
npx vitest run --project unit src/skills.test.ts
1520
npx vitest run --project unit src/package-exports.test.ts
1621
npx vitest run --project unit src/convention-audit.test.ts src/runtime-copy.test.ts
22+
npm run test:plugin -- --reporter=verbose
1723
```
1824

1925
## Cloak Runtime Smoke

0 commit comments

Comments
 (0)