@nailuogg/pi-find-packages 0.1.2 → 0.1.4

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.
package/README.md CHANGED
@@ -7,7 +7,7 @@ A [pi](https://pi.dev) extension providing a local catalog of the pi package eco
7
7
  - **Offline catalog**: syncs the full `keywords:pi-package` corpus (~5k+ packages) from npm to local JSONL; search with jq/grep over descriptions, names, and keywords — no dependence on npm's keyword-matched search
8
8
  - **`/find-packages <need>`**: retrieve candidates → read-only source analysis in a sandbox → comparison table and recommendation against criteria (feature overlap / peer compatibility / maintenance activity / supply-chain signals)
9
9
  - **Cold start**: the package ships a bundled catalog snapshot (`data/catalog.jsonl.gz`); install and use immediately with zero network requests on first run
10
- - **Docker isolation by default**: candidate repos are cloned and unpacked inside a credential-free, non-root container; isolation can be disabled explicitly (a risk warning is shown on every use — not recommended)
10
+ - **Docker isolation by default**: candidate repos are fetched and unpacked inside a credential-free, non-root container (read-only root, no capabilities, no-new-privileges, tmpfs work dirs; no host source mount); isolation can be disabled explicitly (a risk warning is shown on every use — not recommended)
11
11
 
12
12
  ## Install
13
13
 
@@ -21,10 +21,25 @@ pi install /path/to/pi-find-packages # local trial
21
21
 
22
22
  `<PI_CODING_AGENT_DIR>/data/pi-find-packages/` (default `~/.pi/agent/data/pi-find-packages/`)
23
23
 
24
- - `catalog.jsonl` — catalog data (one package per line: name/version/description/date/author/keywords/repo)
24
+ - `catalog.jsonl` — catalog data (one package per line: name/version/description/date/author/publisher/keywords/repo)
25
25
  - `config.json` — `{"isolation": "docker" | "off", "semantic": "auto" | "on" | "off"}`
26
26
  - `isolation` defaults to `docker` (sandboxed source analysis)
27
27
  - `semantic` controls semantic search over lazily-cached READMEs (via [qmd](https://github.com/tobi/qmd)); defaults to `auto`: enabled automatically when the `qmd` binary is present, force with `"on"`, disable with `"off"`
28
+ - `readmes/` — READMEs of reviewed candidates, the corpus behind semantic search (see below)
29
+
30
+ ## Semantic search setup (optional)
31
+
32
+ Semantic search reads a qmd collection named `pi-pkg-readmes`, pointed at the `readmes/`
33
+ directory above. Nothing in this package registers that collection, and qmd does not create
34
+ it on demand: an unregistered directory simply stays empty, so semantic search degrades to
35
+ lexical search with no error. Register it once:
36
+
37
+ ```bash
38
+ qmd collection add "$HOME/.pi/agent/data/pi-find-packages/readmes" --name pi-pkg-readmes
39
+ qmd collection show pi-pkg-readmes # confirm the path matches your data directory
40
+ ```
41
+
42
+ Substitute your real data directory when `PI_CODING_AGENT_DIR` is set. Afterwards `/find-packages` uses qmd results only as leads: verify each candidate against its current npm version and the cached README's first line, then overwrite that README after review and index with `qmd update && qmd embed`. It does not bulk-update READMEs.
28
43
 
29
44
  ## Refreshing the catalog
30
45
 
@@ -52,7 +67,7 @@ the latest npm publish and therefore updates on release cadence.
52
67
  docker build -t pi-find-packages-analysis -f docker/Dockerfile.analysis docker
53
68
  ```
54
69
 
55
- The image contains no pi and no credentials: it exists solely to clone/unpack/read third-party source. Never execute a candidate package's install scripts or build artifacts in any environment.
70
+ The image contains no pi and no credentials: it exists solely to fetch, verify, unpack and read third-party source. Runtime must use a read-only root, `--cap-drop=ALL`, `--security-opt=no-new-privileges`, the non-root analyst user, and tmpfs `/analysis` and `/tmp` mounts (`mode=1777`); never bind-mount candidate source from the host. Allow required npm registry/GitHub access. Only analysis text returns to the host. Never execute a candidate package's install scripts or build artifacts in any environment.
56
71
 
57
72
  ## Releasing
58
73
 
@@ -36,7 +36,14 @@ export default function activate(pi: ExtensionAPI) {
36
36
  mkdirSync(dataDir, { recursive: true });
37
37
  if (existsSync(bundled)) {
38
38
  writeFileSync(catalog, gunzipSync(readFileSync(bundled)));
39
- pi.appendEntry("find-packages", { event: "cold-start-extract", from: bundled });
39
+ // pi only accepts registration calls while a factory runs; action methods
40
+ // such as appendEntry throw until pi commits the extension, and a throw here
41
+ // makes pi reject the whole extension (breaking every first run). Pure fs work
42
+ // is fine, so the audit entry is deferred to a session event instead.
43
+ const extracted = { event: "cold-start-extract", from: bundled };
44
+ pi.on("session_start", () => {
45
+ pi.appendEntry("find-packages", extracted);
46
+ });
40
47
  }
41
48
  }
42
49
 
@@ -163,7 +170,7 @@ export default function activate(pi: ExtensionAPI) {
163
170
  "",
164
171
  "⚠️ **Isolation disabled (isolation: off)**: candidate source will be cloned/unpacked on the host.",
165
172
  "Third-party packages may contain malicious install scripts or build logic; host-side analysis is not sandboxed.",
166
- "This mode is not recommended. Delete data/pi-find-packages/config.json or set isolation:\"docker\" to restore the default.",
173
+ "This mode is not recommended. Delete " + configFile + " or set isolation:\"docker\" to restore the default.",
167
174
  ].join("\n")
168
175
  : "";
169
176
  const prompt = [
@@ -171,16 +178,14 @@ export default function activate(pi: ExtensionAPI) {
171
178
  `**${query}**`,
172
179
  "",
173
180
  "Steps:",
174
- "1. Search: run multiple jq/grep keyword passes over `~/.pi/agent/data/pi-find-packages/catalog.jsonl` against description/keywords/name, trying synonyms as needed; "
175
- + (semanticEnabled()
176
- ? "also run `qmd query --collection pi-pkg-readmes` for semantic search over cached READMEs (skip if empty); "
177
- : "")
178
- + "pick the 3-5 most relevant candidates.",
179
- "2. Review each candidate (read-only): `npm view <pkg>` for version/deps/peer; clone or download source via the repo link; read entry points, extension points, and the README; judge maintenance activity, dependency surface, and supply-chain signals. "
180
- + "After reviewing, save the package README to `~/.pi/agent/data/pi-find-packages/readmes/<name with / replaced by __>.md` (first line `# <name> <version> <date>`)"
181
- + (semanticEnabled() ? ", then run `qmd index pi-pkg-readmes` to update the index" : "") + ".",
182
- `3. Execution environment: ${iso === "docker" ? "all cloning/unpacking/source analysis must run inside the Docker container (see docker/Dockerfile.analysis); the host only receives analysis text; never run a candidate's install scripts." : "not isolated — analyze read-only on the host (see the risk note above)."}`,
183
- "4. Criteria: feature overlap with the current setup / installed packages (Unix philosophy: features must not cross); pi compatibility (peer ranges); maintenance activity; dependency and supply-chain safety.",
181
+ `1. Search: run multiple jq/grep keyword passes over \`${catalog}\` against description/keywords/name, trying synonyms as needed; `
182
+ + (semanticEnabled() ? "also run `qmd query --collection pi-pkg-readmes` for semantic search over cached READMEs (skip if empty); " : "")
183
+ + `for broad jq searches, count matches first, then narrow terms or page results; never shortlist from truncated output. ${semanticEnabled() ? "Treat qmd hits only as leads: verify the current npm version against the cached README's first line, then after review overwrite that README with the verified version/date and run `qmd update && qmd embed`; never bulk-update. " : ""}Pick the 3-5 most relevant candidates.`,
184
+ `2. Review each candidate: npm view <pkg> for version/deps/peer, plus the README and registry metadata on the host; judge maintenance activity, dependency surface, and supply-chain signals. Any cloning or unpacking of candidate source happens in step 3's sandbox only — never fetch candidate source on the host. Save the reviewed README to \`${join(dataDir, "readmes")}/<name with / replaced by __>.md\` (first line \`# <name> <version> <date>\`)${semanticEnabled() ? " and refresh the semantic index with qmd update && qmd embed" : ""}.`,
185
+ `3. Execution environment: ${iso === "docker"
186
+ ? "All source analysis must run inside the Docker container with --read-only --cap-drop=ALL --security-opt=no-new-privileges, as the image's non-root analyst user, and tmpfs mounts for /analysis and /tmp with mode=1777 (no host candidate-source bind mount). Allow network access needed for npm registry/GitHub. Fetch, verify, unpack and analyze candidate source only inside the container; only analysis text may reach the host. Never run candidate scripts or build artifacts."
187
+ : "not isolated — analyze read-only on the host (see the risk note above)."}`,
188
+ "4. Criteria: feature overlap with the current setup / installed packages (Unix philosophy: features must not cross); pi compatibility (check actual pi --version and the candidate peer range; note installed package-directory versus running-version drift rather than treating drift alone as incompatibility); maintenance activity; dependency and supply-chain safety.",
184
189
  "5. Output: a candidate comparison table (name/version/activity/fit/risk) + a clear recommendation with reasons. The analysis report is not install authorization — I decide whether to integrate.",
185
190
  riskNote,
186
191
  ].filter(Boolean).join("\n");
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@nailuogg/pi-find-packages",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Local catalog of the pi package ecosystem with /find-packages: offline search, cold-start data, and sandboxed source analysis for integration review",
5
5
  "type": "module",
6
+ "scripts": {
7
+ "test": "node --test"
8
+ },
6
9
  "keywords": [
7
10
  "pi-package",
8
11
  "pi",
@@ -37,5 +40,14 @@
37
40
  },
38
41
  "publishConfig": {
39
42
  "access": "public"
40
- }
43
+ },
44
+ "files": [
45
+ "extensions/",
46
+ "skills/",
47
+ "scripts/",
48
+ "data/",
49
+ "docker/",
50
+ "README.md",
51
+ "LICENSE"
52
+ ]
41
53
  }
@@ -1,37 +1,180 @@
1
- # find-packages: pi package catalog search & integration review
1
+ ---
2
+ name: find-packages
3
+ description: >-
4
+ Search a local offline catalog of the pi package ecosystem (npm keywords:pi-package) and review a
5
+ candidate package before installing it. Use when the user wants to find, compare, or evaluate a pi
6
+ package, extension, or skill to install: lexical and semantic catalog search over cached READMEs,
7
+ catalog refresh when stale, and a fixed integration review (feature overlap with pi built-ins and
8
+ installed packages, peer compatibility, maintenance activity, supply chain) plus sandboxed Docker
9
+ source analysis.
10
+ license: MIT
11
+ compatibility: >-
12
+ Requires jq, node >= 22 and npm; docker for source analysis; network access for catalog refresh and
13
+ npm view. qmd is optional and only enables semantic search over cached READMEs. POSIX shell assumed.
14
+ ---
2
15
 
3
- Maintains a local catalog of the pi package ecosystem (same source as pi.dev/packages: npm `keywords:pi-package`) for offline/semantic package discovery, plus a standard integration-evaluation workflow.
16
+ # find-packages
4
17
 
5
- ## Data
18
+ Find pi packages in a local, offline catalog of the pi ecosystem (npm `keywords:pi-package`) and review
19
+ a candidate before it gets installed. Search is lexical (`jq`), optionally semantic; review is a fixed
20
+ rubric with sandboxed Docker source analysis.
6
21
 
7
- - Catalog file: `~/.pi/agent/data/pi-find-packages/catalog.jsonl` (one package per line: name/version/description/date/author/keywords/npm/repo)
8
- - Refresh (pick the first that works):
9
- 1. `/find-packages update` — pulls the latest snapshot from the jsDelivr `data` branch (falls back to the npmmirror tarball of the latest published version); checksum-verified, atomic replace
10
- 2. `node <package-dir>/scripts/sync-catalog.mjs` — full rebuild from the npm registry search API (~40 requests). Only run when the file is older than 30 days or the user asks; avoid hammering the registry.
22
+ Catalog: `${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/data/pi-find-packages/catalog.jsonl` — one JSON object
23
+ per line, with fields `name`, `version`, `description`, `date` (last publish), `author` (frequently
24
+ empty), `publisher` (use this as the maintainer), `keywords`, `npm`, `repo`, `homepage`. The same
25
+ directory holds `config.json` and the `readmes/` cache.
11
26
 
12
- ## Search methods
27
+ Throughout this file, paths are relative to this skill directory; `../../` is the package root.
13
28
 
14
- 1. Lexical: `jq -r 'select(.description|test("keyword";"i")) | [.name,.version,.description] | @tsv' catalog.jsonl` — try multiple synonym groups (English-first; cover channel words like remote/telegram/discord/web).
15
- 2. Semantic (optional): `qmd query --collection pi-pkg-readmes "need description"` — searches cached READMEs of previously reviewed packages (grows over time; skip when the cache is empty). Only use when the `qmd` binary is available and not disabled via `"semantic":"off"` in `config.json`; otherwise skip this step and rely on lexical search.
16
- 3. When both come up empty, fall back to `npm search` and note the catalog may be stale.
29
+ ## Workflow
17
30
 
18
- ## README cache (written as a side effect of review)
31
+ 1. **Search** the catalog for candidates — §1.
32
+ 2. **Shortlist** the 3–5 closest matches.
33
+ 3. **Review** each candidate against the rubric — §3. Feature overlap is a hard rule.
34
+ 4. **Analyze sources** inside the Docker sandbox when the review needs code-level evidence — §4.
35
+ 5. **Report** a comparison table plus a recommendation — §5.
19
36
 
20
- After reviewing a candidate, save its README to `~/.pi/agent/data/pi-find-packages/readmes/<name with / replaced by __>.md`, first line `# <name> <version> <date>`; then run `qmd index pi-pkg-readmes` to update the index. **Never bulk-fetch all READMEs** — the corpus grows with real reviews.
37
+ Complete the steps in order; never skip the overlap check in step 3.
21
38
 
22
- ## Integration review (check every candidate)
39
+ ## 1. Search the catalog
23
40
 
24
- - **Feature overlap**: does it overlap installed packages (`packages` arrays in `~/.pi/agent/settings.json` and the project `.pi/settings.json`) or pi built-ins? No overlap is a hard rule.
25
- - **Compatibility**: does `npm view <pkg> peerDependencies` cover the current pi version?
26
- - **Activity**: latest publish date, latest repo commit.
27
- - **Supply chain**: author/maintainers, dependency count, install/preinstall scripts in package.json, any data-exfiltration paths.
41
+ Run one pass per synonym group, English first, and always include channel/transport words
42
+ (remote, telegram, discord, web, vnc) when the need is about access or reach:
28
43
 
29
- ## Execution environment
44
+ ```bash
45
+ CAT="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/data/pi-find-packages/catalog.jsonl"
46
+ jq -r 'select((.name + " " + (.description // "") + " " + ((.keywords // [])|join(" ")))
47
+ | test("remote|telegram|discord";"i"))
48
+ | [.name, .version, (.publisher // ""), .date] | @tsv' "$CAT"
49
+ ```
30
50
 
31
- - **Source analysis must run inside Docker by default** (clone/unpack candidate code via `docker/Dockerfile.analysis`: node24-slim + git + ripgrep, no credentials). The host only receives analysis text. **Never execute a candidate's install scripts or build artifacts.**
32
- - `"isolation":"off"` in `data/pi-find-packages/config.json` disables isolation — a risk warning is shown on every use; not recommended.
33
- - Shallow lookups (`npm view`, registry JSON) do not need the container.
51
+ For broad searches, count matches first, then narrow the terms or page through results. Never choose a shortlist from truncated output.
34
52
 
35
- ## Output convention
53
+ Expected output — tab-separated, one candidate per line:
36
54
 
37
- Candidate comparison table (name/version/last publish/activity/fit/risk) + a clear recommendation with reasons. **An analysis report is not install authorization** — the user decides whether to `pi install`.
55
+ ```text
56
+ @99percentpeople/pi-ssh-remote 0.6.2 GitHub Actions 2026-09-08T08:29:12.985Z
57
+ @bacnh85/pi-a2a 0.7.11 bacnh85 2026-09-21T03:51:02.441Z
58
+ ```
59
+
60
+ `.description` and `.keywords` can be `null`, hence the `// ""` guards. Keep `test()` case-insensitive.
61
+
62
+ **Semantic search (optional).** Only when `qmd` is installed and `semantic` is not `"off"` — see
63
+ Configuration. It searches cached READMEs of previously reviewed packages only, so check the collection
64
+ first and skip semantic search while it is empty — `qmd query` spends an LLM expansion pass per call,
65
+ `qmd ls` is free. Treat results only as leads; verify current facts against npm:
66
+
67
+ ```bash
68
+ qmd ls pi-pkg-readmes # "No files found" → skip
69
+ qmd query --collection pi-pkg-readmes "<one sentence>"
70
+ ```
71
+
72
+ **When both come up empty**, fall back to `npm search` and say in the report that the catalog may be
73
+ stale.
74
+
75
+ ## 2. Refresh the catalog
76
+
77
+ Refresh only when the catalog is missing, older than 30 days (`find "$CAT" -mtime +30`), or the user
78
+ asks. Avoid hammering the registry.
79
+
80
+ 1. `/find-packages update` — the command provided by this package's extension. Pulls the snapshot from
81
+ the jsDelivr `data` branch (checksum-verified against the published `.sha256`), then falls back to
82
+ the npmmirror tarball of the latest published version.
83
+ 2. Last resort, when both CDNs fail: run `node ../../scripts/sync-catalog.mjs` to rebuild the catalog
84
+ locally from the npm registry search API (~40 requests). It writes `catalog.jsonl`, its `.gz` and
85
+ `.sha256` into the catalog directory; CI invokes the same script with `--out <dir>`.
86
+
87
+ ## 3. Integration review
88
+
89
+ Run every candidate through all four checks; record the evidence for each one.
90
+
91
+ | Check | How to verify | Verdict |
92
+ |---|---|---|
93
+ | **Feature overlap** (hard rule) | Compare against pi built-ins and the packages installed in the `packages` array of `$PI_CODING_AGENT_DIR/settings.json` and of the project `.pi/settings.json` | Any overlap → recommend against, whatever the other checks say |
94
+ | **pi compatibility** | Check actual `pi --version` and `npm view <pkg> peerDependencies` | Compare the peer range with the running version. If the installed package-directory copy reports another version, record that drift; do not call it incompatible on drift alone |
95
+ | **Maintenance activity** | Catalog `date` (last publish) plus the last commit in `repo` | Stale on both → note as risk, not a blocker |
96
+ | **Supply chain** | `npm view <pkg> maintainers dependencies scripts` | Flag `install`/`preinstall` scripts, large dependency trees, and any path that sends data off-host |
97
+
98
+ ## 4. Analyze sources in the sandbox
99
+
100
+ Source analysis runs in Docker by default, using `../../docker/Dockerfile.analysis` (node24-slim + git +
101
+ ripgrep, no credentials). Do not mount candidate source from the host. Run read-only with
102
+ `--cap-drop=ALL --security-opt=no-new-privileges`, as the non-root analyst user, and tmpfs mounts for
103
+ `/analysis` and `/tmp` with `mode=1777`. Allow network access needed for npm registry/GitHub. Fetch,
104
+ verify, unpack and read candidate code **inside the container**; only analysis text reaches the host.
105
+
106
+ Before analysis, replace `<image>` with the built analysis image name and run this preflight to confirm
107
+ non-root execution and writable workdirs:
108
+
109
+ ```bash
110
+ docker run --rm --read-only --cap-drop=ALL --security-opt=no-new-privileges \
111
+ --tmpfs /analysis:rw,mode=1777 --tmpfs /tmp:rw,mode=1777 \
112
+ <image> -c 'test "$(id -u)" -ne 0 && touch /analysis/.probe /tmp/.probe'
113
+ ```
114
+
115
+ This preflight only checks the runtime. Fetching candidate source, verifying its integrity, and
116
+ unpacking it must still happen inside a container with the same security options; never bind-mount
117
+ candidate source from the host.
118
+
119
+ **Never execute a candidate's install scripts or build artifacts** — not on the host and not in the
120
+ container. Reading `package.json`, the README or registry metadata needs no container, so shallow
121
+ lookups stay on the host.
122
+
123
+ `isolation: "off"` in `config.json` disables isolation and moves analysis to the host; every use shows a
124
+ risk warning. Do not recommend it, and do not fall back to host-side analysis on your own when Docker is
125
+ unavailable — report the blocker instead.
126
+
127
+ ## 5. Report
128
+
129
+ Compare the shortlist in a table, then give one recommendation with reasons:
130
+
131
+ | name | version | last publish | activity | fit | risk |
132
+ |---|---|---|---|---|---|
133
+ | @acme/pi-search | 1.2.0 | 2026-09-05 | active (7d) | no overlap | low |
134
+
135
+ **A report is not install authorization.** The user decides whether to run `pi install`.
136
+
137
+ ## README cache
138
+
139
+ After reviewing a candidate, verify the current npm version and compare it with the first line of any
140
+ cached README; qmd hits are leads only. Overwrite that candidate's README at
141
+ `<catalog dir>/readmes/<name with / replaced by __>.md`, first line `# <name> <verified version> <date>`,
142
+ then refresh the semantic index. Never bulk-update README files:
143
+
144
+ ```bash
145
+ qmd update && qmd embed
146
+ ```
147
+
148
+ This cache is the corpus behind semantic search. Never bulk-fetch READMEs — it grows one real review at
149
+ a time.
150
+
151
+ **One-time setup.** Semantic search needs this directory registered as the qmd collection
152
+ `pi-pkg-readmes`, and nothing in this package registers it: an unregistered directory stays empty and
153
+ semantic search then degrades to lexical search without any error. Register it once:
154
+
155
+ ```bash
156
+ qmd collection add "<catalog dir>/readmes" --name pi-pkg-readmes
157
+ ```
158
+
159
+ Check where the collection points with `qmd collection show pi-pkg-readmes`. If it points at another
160
+ directory, say so and give the user the command above instead of saving READMEs into a directory that is
161
+ never indexed.
162
+
163
+ ## Configuration
164
+
165
+ `<catalog dir>/config.json` (absent file → defaults apply):
166
+
167
+ - `isolation`: `"docker"` (default) | `"off"` — source-analysis environment.
168
+ - `semantic`: `"auto"` (default) | `"on"` | `"off"` — `auto` enables semantic search exactly when the
169
+ `qmd` binary is present.
170
+
171
+ ## Edge cases
172
+
173
+ | Situation | Action |
174
+ |---|---|
175
+ | `catalog.jsonl` missing | Cold start extracts it automatically; if it is still missing, run `/find-packages update` |
176
+ | Catalog older than 30 days, or no matches | Refresh (§2), then search again; note staleness in the report if the refresh fails |
177
+ | `qmd` missing, or `semantic: "off"` | Skip semantic search; rely on lexical passes |
178
+ | `pi-pkg-readmes` empty or registered against another directory | Still skip semantic search, but tell the user the one-time setup command — an unindexed cache never becomes searchable on its own |
179
+ | Docker unavailable | Stop before source analysis, report the blocker, and ask the user how to proceed |
180
+ | `config.json` absent | Defaults apply: Docker isolation on, semantic `auto` |
@@ -1,41 +0,0 @@
1
- name: publish
2
-
3
- on:
4
- push:
5
- tags: ["v*"]
6
-
7
- permissions:
8
- contents: write # create the GitHub release
9
- id-token: write # OIDC token for npm Trusted Publishing
10
-
11
- jobs:
12
- publish:
13
- runs-on: ubuntu-latest
14
- environment: npm
15
- steps:
16
- - uses: actions/checkout@v4
17
-
18
- - uses: actions/setup-node@v4
19
- with:
20
- node-version: 24
21
- registry-url: https://registry.npmjs.org/
22
-
23
- - name: Ensure npm supports Trusted Publishing
24
- run: npm install -g npm@latest # OIDC trusted publishing needs npm >= 11.5.1
25
-
26
- - name: Verify version matches tag
27
- run: |
28
- PKG_VERSION="${GITHUB_REF_NAME#v}"
29
- TAG_VERSION="$(node -p "require('./package.json').version")"
30
- if [ "${PKG_VERSION}" != "${TAG_VERSION}" ]; then
31
- echo "::error::tag ${GITHUB_REF_NAME} != package.json version ${TAG_VERSION}"
32
- exit 1
33
- fi
34
-
35
- - name: Publish (OIDC trusted publishing, no token)
36
- run: npm publish
37
-
38
- - name: Create GitHub release
39
- run: gh release create "${GITHUB_REF_NAME}" --verify-tag --generate-notes
40
- env:
41
- GH_TOKEN: ${{ github.token }}
@@ -1,45 +0,0 @@
1
- name: update-data
2
-
3
- on:
4
- schedule:
5
- - cron: "17 3 * * *" # daily, off the hour
6
- workflow_dispatch:
7
-
8
- permissions:
9
- contents: write
10
-
11
- jobs:
12
- refresh:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - uses: actions/checkout@v4
16
-
17
- - uses: actions/setup-node@v4
18
- with:
19
- node-version: 24
20
-
21
- - name: Fetch latest catalog
22
- run: node scripts/sync-catalog.mjs --out "${RUNNER_TEMP}/catalog"
23
-
24
- - name: Skip if unchanged
25
- id: diff
26
- run: |
27
- NEW="${RUNNER_TEMP}/catalog/catalog.jsonl.gz"
28
- OLD="$(curl -fsSL --max-time 30 https://cdn.jsdelivr.net/gh/${GITHUB_REPOSITORY}@data/data/catalog.jsonl.gz 2>/dev/null | sha256sum | cut -d' ' -f1 || true)"
29
- CUR="$(sha256sum "${NEW}" | cut -d' ' -f1)"
30
- echo "changed=$([[ "${CUR}" != "${OLD}" ]] && echo true || echo false)" >> "$GITHUB_OUTPUT"
31
-
32
- - name: Push to data branch
33
- if: steps.diff.outputs.changed == 'true'
34
- run: |
35
- git config user.name "github-actions[bot]"
36
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
37
- git fetch origin data || true
38
- git checkout -B data origin/data 2>/dev/null || git checkout -B data
39
- mkdir -p data
40
- cp "${RUNNER_TEMP}/catalog/catalog.jsonl" data/
41
- cp "${RUNNER_TEMP}/catalog/catalog.jsonl.gz" data/
42
- cp "${RUNNER_TEMP}/catalog/catalog.jsonl.gz.sha256" data/
43
- git add data/
44
- git commit -qm "chore(data): refresh catalog ($(date -u +%Y-%m-%d))"
45
- git push origin data