@groeponline/pi-wishcraft 0.23.0 → 0.23.2
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/CHANGELOG.md +11 -0
- package/RELEASE.md +13 -7
- package/package.json +1 -1
- package/src/segments/custom.ts +3 -1
- package/src/segments/registry.ts +19 -11
- package/src/segments/system.ts +19 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.23.2] - 2026-08-20
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Release workflow creates a GitHub Release for each tag so the Releases page Latest matches npm.
|
|
9
|
+
|
|
10
|
+
## [0.23.1] - 2026-08-20
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Per-segment fault isolation: a throwing custom `command` segment shows `!id` instead of blanking the footer.
|
|
14
|
+
- `open_ports` parses macOS netstat dot-separated local addresses (`*.5900`, `127.0.0.1.631`).
|
|
15
|
+
|
|
5
16
|
## [0.23.0] - 2026-08-20
|
|
6
17
|
|
|
7
18
|
### Added
|
package/RELEASE.md
CHANGED
|
@@ -20,11 +20,12 @@ verify it.
|
|
|
20
20
|
**Default:** every merge to `main` is a release. `.github/workflows/release.yml`
|
|
21
21
|
runs `node scripts/release.mjs auto --push` on `main` and publishes in that
|
|
22
22
|
same job (`scripts/npm-publish.sh`) with the GroepOnline org secret
|
|
23
|
-
`NPM_TOKEN
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
`NPM_TOKEN`, then creates a GitHub Release for the tag
|
|
24
|
+
(`scripts/github-release.sh`) so the Releases page Latest matches npm. A tag
|
|
25
|
+
pushed with `GITHUB_TOKEN` does **not** start another workflow, so publish
|
|
26
|
+
cannot wait for the tag event. Manual SSH/PAT tag pushes still run the tag
|
|
27
|
+
job. `auto` reads commit subjects since the last `v*` tag: `feat:` → minor,
|
|
28
|
+
`feat!:` / `BREAKING CHANGE` → major, otherwise patch.
|
|
28
29
|
|
|
29
30
|
The first merge after `v0.18.0` therefore becomes **0.19.0** (the 0.19
|
|
30
31
|
`feat:` commits are already on `main`). Later docs/fix merges become
|
|
@@ -78,6 +79,7 @@ start a second run.
|
|
|
78
79
|
7. `npm run verify:package` (catalog contract gate — see `scripts/verify-package.mjs`)
|
|
79
80
|
8. `node scripts/release.mjs auto --push` (main only)
|
|
80
81
|
9. `sh scripts/npm-publish.sh` with `NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}` (fail-closed `npm view`, idempotent skip, then the Pi catalog URLs)
|
|
82
|
+
10. `sh scripts/github-release.sh` with `GITHUB_TOKEN` (idempotent: skip if that tag already has a Release; notes from `CHANGELOG.md`)
|
|
81
83
|
|
|
82
84
|
`NPM_TOKEN` is the GroepOnline **org** Actions secret (publish rights to the
|
|
83
85
|
`@groeponline` scope). This repo has no override; Actions inherits the org
|
|
@@ -109,10 +111,14 @@ gh run view <databaseId> --repo GroepOnline/pi-wishcraft \
|
|
|
109
111
|
# 4. npm registry (published + propagated)
|
|
110
112
|
npm view @groeponline/pi-wishcraft version # 0.15.0
|
|
111
113
|
npm view @groeponline/pi-wishcraft dist-tags # { latest: '0.15.0' }
|
|
114
|
+
|
|
115
|
+
# 5. GitHub Release Latest matches npm
|
|
116
|
+
gh release list --repo GroepOnline/pi-wishcraft --limit 5
|
|
112
117
|
```
|
|
113
118
|
|
|
114
|
-
A release is "done" when all
|
|
115
|
-
`success`,
|
|
119
|
+
A release is "done" when all five hold: local green, tag on origin, CI
|
|
120
|
+
`success`, `npm view` returns the new version, and GitHub Releases Latest
|
|
121
|
+
is that same tag.
|
|
116
122
|
|
|
117
123
|
## Known gotchas
|
|
118
124
|
|
package/package.json
CHANGED
package/src/segments/custom.ts
CHANGED
|
@@ -35,7 +35,9 @@ function runCommandCached(
|
|
|
35
35
|
commandCache.set(command, { at: now, value: out });
|
|
36
36
|
return out;
|
|
37
37
|
} catch {
|
|
38
|
-
|
|
38
|
+
// Preserve command failures so renderSegment can isolate this segment and
|
|
39
|
+
// expose a visible fault marker instead of silently hiding it.
|
|
40
|
+
throw new Error(`Command failed: ${command}`);
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
|
package/src/segments/registry.ts
CHANGED
|
@@ -164,16 +164,24 @@ export function renderSegment(
|
|
|
164
164
|
id: StatusLineSegmentId,
|
|
165
165
|
ctx: SegmentContext,
|
|
166
166
|
): RenderedSegment {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
167
|
+
try {
|
|
168
|
+
let rendered: RenderedSegment;
|
|
169
|
+
if (isCustomSegmentId(id)) {
|
|
170
|
+
const customId = id.slice("custom:".length);
|
|
171
|
+
const computed = customComputedSegments.get(customId);
|
|
172
|
+
rendered = computed
|
|
173
|
+
? computed.render(ctx)
|
|
174
|
+
: renderCustomSegment(id, ctx);
|
|
175
|
+
} else {
|
|
176
|
+
const segment = SEGMENTS[id];
|
|
177
|
+
rendered = segment ? segment.render(ctx) : { content: "", visible: false };
|
|
178
|
+
}
|
|
179
|
+
return applySegmentDecoration(id, ctx, rendered);
|
|
180
|
+
} catch {
|
|
181
|
+
// ponytail: per-segment fault isolation. A throwing segment (most often a
|
|
182
|
+
// user `command` custom segment with a failing script) must not blank the
|
|
183
|
+
// whole footer; surface a visible `!id` marker so the operator sees
|
|
184
|
+
// which segment broke. No per-render log: the bar repaints ~30/s.
|
|
185
|
+
return { content: `!${id}`, visible: true };
|
|
177
186
|
}
|
|
178
|
-
return applySegmentDecoration(id, ctx, rendered);
|
|
179
187
|
}
|
package/src/segments/system.ts
CHANGED
|
@@ -180,7 +180,21 @@ export function countListeningPorts(includeUdp = false, host?: string): number {
|
|
|
180
180
|
return host ? -1 : readProcListeningPorts(includeUdp);
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
return parseListeningPortsFromText(out).size;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Pure port-count parser shared by `countListeningPorts`. Accepts `ss` and
|
|
188
|
+
* `netstat` output. The port separator is `:` on Linux/`ss`/`lsof` but `.` on
|
|
189
|
+
* macOS `netstat` (`*.5900`, `127.0.0.1.631`); matching both keeps the
|
|
190
|
+
* open-ports segment honest on macOS.
|
|
191
|
+
*
|
|
192
|
+
* ponytail: assumes LISTEN rows always carry a port on the local-address
|
|
193
|
+
* column; a bare IPv4 with no port (not emitted by LISTEN output) would
|
|
194
|
+
* false-match the `.<digits>` form.
|
|
195
|
+
*/
|
|
196
|
+
export function parseListeningPortsFromText(text: string): Set<number> {
|
|
197
|
+
const lines = text
|
|
184
198
|
.split("\n")
|
|
185
199
|
.map((l) => l.trim())
|
|
186
200
|
.filter(Boolean);
|
|
@@ -189,14 +203,14 @@ export function countListeningPorts(includeUdp = false, host?: string): number {
|
|
|
189
203
|
for (const line of lines.slice(start)) {
|
|
190
204
|
// ss/netstat put the local address at different columns; take the first addr:port token
|
|
191
205
|
for (const col of line.split(/\s+/)) {
|
|
192
|
-
const m =
|
|
206
|
+
const m = /(?::|\.)(\d+)$/.exec(col);
|
|
193
207
|
if (m) {
|
|
194
208
|
ports.add(Number(m[1]));
|
|
195
209
|
break;
|
|
196
210
|
}
|
|
197
211
|
}
|
|
198
212
|
}
|
|
199
|
-
return ports
|
|
213
|
+
return ports;
|
|
200
214
|
}
|
|
201
215
|
|
|
202
216
|
function readProcListeningPorts(includeUdp: boolean): number {
|
|
@@ -259,7 +273,7 @@ export function parseOpenPortProcesses(text: string): OpenPortProcess[] {
|
|
|
259
273
|
// numeric Recv-Q there. That tells us which local-address column to use.
|
|
260
274
|
const isSsRow = !/^\d+$/.test(tokens[1] ?? "");
|
|
261
275
|
const local = isSsRow ? tokens[4] : tokens[3];
|
|
262
|
-
const portMatch = local ?
|
|
276
|
+
const portMatch = local ? /(?::|\.)(\d+)$/.exec(local) : null;
|
|
263
277
|
if (!portMatch) continue;
|
|
264
278
|
|
|
265
279
|
let process: string | null = null;
|
|
@@ -275,7 +289,7 @@ export function parseOpenPortProcesses(text: string): OpenPortProcess[] {
|
|
|
275
289
|
entries.push({
|
|
276
290
|
port: Number(portMatch[1]),
|
|
277
291
|
proto: tokens[0].toLowerCase().startsWith("udp") ? "udp" : "tcp",
|
|
278
|
-
address: local.replace(
|
|
292
|
+
address: local.replace(/(?::|\.)(\d+)$/, ""),
|
|
279
293
|
process,
|
|
280
294
|
});
|
|
281
295
|
}
|