Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2985d9ade8 | |||
| bff810c1eb | |||
| 904fe85249 | |||
| cda488c565 | |||
| 9ab9a28458 | |||
| d175b31207 |
@@ -11,6 +11,103 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.2.3 — 2026-06-27
|
||||||
|
|
||||||
|
Patch release. Headline: **mempalace-mcp now self-heals** instead of latching
|
||||||
|
`available=false` permanently after a slow cold-open. Also folds in the `yq`
|
||||||
|
and mempalace-skill changes that were sitting unreleased. **No pi/mempalace
|
||||||
|
version change** — pi npm `latest` is still `0.80.2` (= v1.2.2) and the
|
||||||
|
mempalace pin stays `3.5.0`; the base image rebuilds purely because the
|
||||||
|
`mempalace-toolkit` ref advances to pick up the self-heal extension.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **mempalace-mcp self-heal — no more permanent `available=false` latch.**
|
||||||
|
The `mempalace.ts` pi extension (from `mempalace-toolkit`, bumped to
|
||||||
|
[`e12b624`](https://gitea.jordbo.se/joakimp/mempalace-toolkit/commit/e12b624))
|
||||||
|
previously tripped its per-request timeout on a slow virtiofs cold-open of
|
||||||
|
the palace, killed the child, and set `available=false` **forever** (no
|
||||||
|
respawn) — a pi restart was the only recovery.
|
||||||
|
- **Bounded respawn with capped exponential backoff** via `ensureAlive()`
|
||||||
|
(`MEMPALACE_MCP_MAX_RESPAWNS=2`, `MEMPALACE_MCP_RESPAWN_BACKOFF_MS=1000`;
|
||||||
|
set max to `0` to disable). Both `execute()` and initial startup route
|
||||||
|
through it. The respawn budget **resets on any successful JSON-RPC
|
||||||
|
response** (`onStdout`), so a healthy session can't slowly exhaust it.
|
||||||
|
- **Scoped init timeout** raised `120000 → 300000` ms (`MEMPALACE_MCP_INIT_TIMEOUT_MS`),
|
||||||
|
affecting **init only** — the per-call timeout stays `60000`
|
||||||
|
(`MEMPALACE_MCP_TIMEOUT_MS`) — so a genuine cold HNSW deserialize isn't
|
||||||
|
killed mid-open.
|
||||||
|
- **Concurrency hardening:** a generation counter prevents a late-exiting
|
||||||
|
killed process from clobbering a fresh respawn, and an explicit `healthy`
|
||||||
|
flag replaces the racy `proc != null` check.
|
||||||
|
- Note: the build-time `smoke-test.sh` verifies the extension is present and
|
||||||
|
deployed but does **not** exercise respawn behaviour — first live
|
||||||
|
validation is on a running container.
|
||||||
|
- **`yq` is now mikefarah's Go yq, not Debian's Python `yq`.** The base image
|
||||||
|
previously apt-installed `yq`, which on Debian/Ubuntu is the unrelated
|
||||||
|
kislyuk/`yq` (a jq wrapper, v3.x) — incompatible with the mikefarah v4 syntax
|
||||||
|
the `cloud-init` repo's `provision.sh`/`deploy.sh` expect. Dropped the apt
|
||||||
|
package and install the mikefarah binary instead (multi-arch amd64/arm64,
|
||||||
|
following the repo's `latest` convention like `tealdeer`/`uv`; pin a tag
|
||||||
|
with `--build-arg YQ_VERSION=vX.Y.Z`). The build-time `smoke-test.sh` gate
|
||||||
|
asserts `yq --version` reports `mikefarah` **and** major **v4**, so both a
|
||||||
|
regression to the Python package and a surprise future yq v5 fail CI.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Baked `mempalace` skill now teaches temporal grounding.** Added a
|
||||||
|
*Temporal grounding* rule to the image-baked
|
||||||
|
`skills/mempalace/SKILL.md` (Phase 1 wake-up + a matching anti-pattern):
|
||||||
|
before using relative time terms ("yesterday", "last week"), establish the
|
||||||
|
current date/time and compute the delta against the actual diary/drawer
|
||||||
|
timestamp. Explicitly calls out that a **container recreate or fresh session
|
||||||
|
is not a day boundary** — pi-devbox restarts several times a day, so two
|
||||||
|
entries minutes apart can straddle a recreate. Fixes agents mislabelling
|
||||||
|
same-day sessions as "yesterday".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.2.2 — 2026-06-24
|
||||||
|
|
||||||
|
Patch release: pick up **pi `0.80.2`** (npm `latest`) and **mempalace `3.5.0`**,
|
||||||
|
and drop the now-obsolete `diary_write` schema workaround — the upstream fix
|
||||||
|
shipped.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **mempalace pin `3.4.0` → `3.5.0`.** mempalace 3.5.0 carries the upstream
|
||||||
|
fix for the top-level-`anyOf` `diary_write` schema
|
||||||
|
([issue #1728](https://github.com/MemPalace/mempalace/issues/1728) /
|
||||||
|
[PR #1717](https://github.com/MemPalace/mempalace/pull/1717), merged
|
||||||
|
2026-06-14). The advertised schema is now `"required": ["agent_name"]` with
|
||||||
|
`entry`/`content` enforced at dispatch instead of via a root-level `anyOf`,
|
||||||
|
which Anthropic's tools API accepts. Verified against the published 3.5.0
|
||||||
|
wheel's `mcp_server.py` before removing the workaround.
|
||||||
|
- **pi `0.79.10` → `0.80.2`**, auto-resolved from npm `latest` at build time
|
||||||
|
(no pin in the repo; CI's `resolve-versions` job fetches it).
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- **The `diary_write` top-level-`anyOf` workaround in `Dockerfile.base`.** The
|
||||||
|
`perl` patch that rewrote the installed `mcp_server.py` (needed while
|
||||||
|
mempalace 3.3.x/3.4.0 advertised a top-level `anyOf` that Anthropic rejects,
|
||||||
|
failing tool registration at session start) is gone, since 3.5.0 fixes it at
|
||||||
|
the source. Keep `MEMPALACE_VERSION` in lockstep with opencode-devbox.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- Unrelated to this release: a *stalled* `mempalace-mcp` (e.g. a slow virtiofs
|
||||||
|
cold-open of `chroma.sqlite3`) surfaces as `mempalace-mcp not available`
|
||||||
|
because the `mempalace.ts` extension's per-request timeout kills the child
|
||||||
|
and flips `available=false` until pi is restarted — this is the 2026-06-13
|
||||||
|
stall-protection behaving as designed, not the `anyOf` bug.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v1.2.1 — 2026-06-22
|
## v1.2.1 — 2026-06-22
|
||||||
|
|
||||||
Patch release: close the fork/recall + mempalace **under-utilisation gap** in
|
Patch release: close the fork/recall + mempalace **under-utilisation gap** in
|
||||||
|
|||||||
+49
-48
@@ -51,7 +51,8 @@ ENV DEBIAN_FRONTEND=noninteractive
|
|||||||
# See the bundled `dot-watch` helper for live .dot -> PNG
|
# See the bundled `dot-watch` helper for live .dot -> PNG
|
||||||
# re-render (handy with pi-studio's image preview).
|
# re-render (handy with pi-studio's image preview).
|
||||||
# imagemagick — image conversion / resizing for thumbnails, etc. ~50 MB.
|
# imagemagick — image conversion / resizing for thumbnails, etc. ~50 MB.
|
||||||
# yq — YAML-aware companion to jq.
|
# (yq is NOT apt-installed: Debian's `yq` is the unrelated Python tool;
|
||||||
|
# mikefarah's Go yq is installed as a pinned binary further down.)
|
||||||
# socat — TCP relay. Powers `studio-expose`, which bridges
|
# socat — TCP relay. Powers `studio-expose`, which bridges
|
||||||
# pi-studio's container-loopback server to the container's
|
# pi-studio's container-loopback server to the container's
|
||||||
# external interface so a published port can reach it.
|
# external interface so a published port can reach it.
|
||||||
@@ -66,7 +67,6 @@ RUN apt-get update && \
|
|||||||
openssh-client \
|
openssh-client \
|
||||||
gnupg \
|
gnupg \
|
||||||
jq \
|
jq \
|
||||||
yq \
|
|
||||||
ripgrep \
|
ripgrep \
|
||||||
fd-find \
|
fd-find \
|
||||||
tree \
|
tree \
|
||||||
@@ -289,21 +289,34 @@ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64"
|
|||||||
# Always installed in the base. Set INSTALL_MEMPALACE=false at base-build
|
# Always installed in the base. Set INSTALL_MEMPALACE=false at base-build
|
||||||
# time to shave ~300 MB.
|
# time to shave ~300 MB.
|
||||||
#
|
#
|
||||||
# Stall protection (fixed 2026-06-13): mempalace-mcp is launched by the
|
# Stall protection (fixed 2026-06-13; self-heal added 2026-06-25):
|
||||||
# `mempalace.ts` pi extension from mempalace-toolkit (cloned below). That
|
# mempalace-mcp is launched by the `mempalace.ts` pi extension from
|
||||||
# extension now applies a per-REQUEST timeout in its JSON-RPC client and
|
# mempalace-toolkit (cloned below). That extension applies a per-REQUEST
|
||||||
# kills the child on stall, so a virtiofs cold-open of chroma.sqlite3 /
|
# timeout in its JSON-RPC client and kills the child on stall, so a virtiofs
|
||||||
# HNSW load can no longer hang the pi TUI uninterruptibly. Tunables:
|
# cold-open of chroma.sqlite3 / HNSW load can no longer hang the pi TUI
|
||||||
|
# uninterruptibly. A stall-kill is no longer a permanent latch either: the
|
||||||
|
# next tool call respawns the server with capped exponential backoff (the
|
||||||
|
# budget resets on any successful response). Tunables:
|
||||||
# MEMPALACE_MCP_TIMEOUT_MS (default 60000), MEMPALACE_MCP_INIT_TIMEOUT_MS
|
# MEMPALACE_MCP_TIMEOUT_MS (default 60000), MEMPALACE_MCP_INIT_TIMEOUT_MS
|
||||||
# (default 120000); 0 disables. A standalone stdio-watchdog shim is NOT
|
# (default 300000 — generous so a genuine first cold-open isn't killed),
|
||||||
# needed — the extension already owns request/response correlation. See
|
# MEMPALACE_MCP_MAX_RESPAWNS (default 2; 0 disables self-heal),
|
||||||
# CHANGELOG.md "Unreleased > Fixed".
|
# MEMPALACE_MCP_RESPAWN_BACKOFF_MS (default 1000); timeouts of 0 disable.
|
||||||
|
# Defaults live in the extension, so no ENV is needed here. A standalone
|
||||||
|
# stdio-watchdog shim is NOT needed — the extension already owns
|
||||||
|
# request/response correlation. See CHANGELOG.md "Unreleased > Fixed".
|
||||||
ARG INSTALL_MEMPALACE=true
|
ARG INSTALL_MEMPALACE=true
|
||||||
# Pin to a known-good version. Bump deliberately, not implicitly: an
|
# Pin to a known-good version. Bump deliberately, not implicitly: an
|
||||||
# unpinned install silently swept in mempalace 3.3.x/3.4.0 with a broken
|
# unpinned install silently swept in mempalace 3.3.x/3.4.0 with a broken
|
||||||
# diary_write schema (see workaround RUN below + issue #1728). Pinning
|
# diary_write schema. Pinning makes mempalace upgrades a reviewable diff
|
||||||
# makes mempalace upgrades a reviewable diff rather than a surprise.
|
# rather than a surprise.
|
||||||
ARG MEMPALACE_VERSION=3.4.0
|
#
|
||||||
|
# 3.5.0 (2026-06) ships the upstream fix for the top-level-anyOf diary_write
|
||||||
|
# schema (issue #1728 / PR #1717, merged 2026-06-14): the advertised schema
|
||||||
|
# is now `"required": ["agent_name"]` with entry/content enforced at dispatch,
|
||||||
|
# which Anthropic's tools API accepts — so the old mcp_server.py perl
|
||||||
|
# workaround that used to live below is gone. Keep in lockstep with
|
||||||
|
# opencode-devbox when bumping.
|
||||||
|
ARG MEMPALACE_VERSION=3.5.0
|
||||||
ENV UV_TOOL_DIR=/opt/uv-tools
|
ENV UV_TOOL_DIR=/opt/uv-tools
|
||||||
ENV UV_TOOL_BIN_DIR=/usr/local/bin
|
ENV UV_TOOL_BIN_DIR=/usr/local/bin
|
||||||
RUN if [ "${INSTALL_MEMPALACE}" = "true" ]; then \
|
RUN if [ "${INSTALL_MEMPALACE}" = "true" ]; then \
|
||||||
@@ -312,41 +325,9 @@ RUN if [ "${INSTALL_MEMPALACE}" = "true" ]; then \
|
|||||||
/opt/uv-tools/mempalace/bin/python -c "import mempalace; print('mempalace', mempalace.__version__ if hasattr(mempalace, '__version__') else 'installed')" ; \
|
/opt/uv-tools/mempalace/bin/python -c "import mempalace; print('mempalace', mempalace.__version__ if hasattr(mempalace, '__version__') else 'installed')" ; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── workaround: strip top-level anyOf from mempalace_diary_write schema ──
|
# (The mempalace diary_write top-level-anyOf workaround that patched
|
||||||
# Mempalace 3.3.x/3.4.0 advertise diary_write's input_schema with a
|
# mcp_server.py here was removed in v1.2.2 — fixed upstream in mempalace
|
||||||
# top-level `anyOf: [{required:[entry]}, {required:[content]}]` to express
|
# 3.5.0 via issue #1728 / PR #1717 (merged 2026-06-14). See CHANGELOG.md.)
|
||||||
# "either entry or content must be supplied". Anthropic's tools API rejects
|
|
||||||
# top-level anyOf/oneOf/allOf, so pi/Claude fail at session start with
|
|
||||||
# `tools.<n>.custom.input_schema: input_schema does not support oneOf,
|
|
||||||
# allOf, or anyOf at the top level`.
|
|
||||||
#
|
|
||||||
# Patch the advertised schema to require ["agent_name", "entry"] and remove
|
|
||||||
# the anyOf block. The handler keeps accepting `content` server-side as a
|
|
||||||
# kwarg alias so existing callers still work.
|
|
||||||
#
|
|
||||||
# Idempotent and self-deactivating: once upstream releases the fix the
|
|
||||||
# regex no longer matches (and the WARN below fires) — that's the signal
|
|
||||||
# to delete this RUN.
|
|
||||||
# Upstream status (last checked 2026-06-14):
|
|
||||||
# issue #1728 — STILL OPEN (root-level anyOf rejected by Anthropic/Codex)
|
|
||||||
# PR #1735 — CLOSED UNMERGED 2026-06-11; do NOT watch it (dead)
|
|
||||||
# PR #1717 — open; the current live fix candidate to watch
|
|
||||||
# mempalace PyPI latest = 3.4.0 (== our pin) → no release contains the fix yet
|
|
||||||
# https://github.com/MemPalace/mempalace/issues/1728
|
|
||||||
# https://github.com/MemPalace/mempalace/pull/1717
|
|
||||||
# TODO: remove this RUN once a mempalace release > 3.4.0 that actually strips
|
|
||||||
# the root-level anyOf ships on PyPI and is installed by the line above.
|
|
||||||
# Keep MEMPALACE_VERSION in lockstep with opencode-devbox when bumping.
|
|
||||||
RUN if [ "${INSTALL_MEMPALACE}" = "true" ]; then \
|
|
||||||
MP_FILE="$(find /opt/uv-tools/mempalace -path '*/mempalace/mcp_server.py' | head -n1)" && \
|
|
||||||
if [ -z "$MP_FILE" ]; then echo "mempalace mcp_server.py not found" >&2; exit 1; fi && \
|
|
||||||
perl -0777 -i -pe 's/(?:[ \t]*\#[^\n]*\n)*[ \t]*"required":\s*\[\s*"agent_name"\s*\]\s*,\s*\n[ \t]*"anyOf":\s*\[\s*\n[ \t]*\{\s*"required":\s*\[\s*"entry"\s*\]\s*\}\s*,\s*\n[ \t]*\{\s*"required":\s*\[\s*"content"\s*\]\s*\}\s*,?\s*\n[ \t]*\]\s*,\s*\n/ "required": ["agent_name", "entry"],\n/s' "$MP_FILE" && \
|
|
||||||
if grep -q '"required": \["agent_name", "entry"\]' "$MP_FILE"; then \
|
|
||||||
echo "mempalace diary_write anyOf workaround: applied (or already clean)"; \
|
|
||||||
else \
|
|
||||||
echo "WARN: mempalace diary_write anyOf workaround did not match expected schema — upstream may have changed shape" >&2; \
|
|
||||||
fi ; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── mempalace-toolkit — bash wrappers for session/docs mining ────────
|
# ── mempalace-toolkit — bash wrappers for session/docs mining ────────
|
||||||
ARG INSTALL_MEMPALACE_TOOLKIT=true
|
ARG INSTALL_MEMPALACE_TOOLKIT=true
|
||||||
@@ -429,6 +410,26 @@ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64"
|
|||||||
chmod +x /usr/local/bin/tldr && \
|
chmod +x /usr/local/bin/tldr && \
|
||||||
tldr --version
|
tldr --version
|
||||||
|
|
||||||
|
# ── yq (mikefarah) — YAML processor, jq's companion for YAML ─────────
|
||||||
|
# Installed as the mikefarah Go binary — NOT Debian's `yq` apt package, which
|
||||||
|
# is the unrelated Python kislyuk/yq (a jq wrapper with different syntax and
|
||||||
|
# version line, e.g. 3.x). The cloud-init repo's deploy.sh/provision.sh
|
||||||
|
# require mikefarah yq v4 (the unrelated Debian python yq is v3.x). Follows
|
||||||
|
# the repo's `latest` convention (like tealdeer/uv/etc.); the smoke test pins
|
||||||
|
# the contract to major v4, so a future yq v5 fails CI instead of silently
|
||||||
|
# breaking provision.sh. Pin a specific tag with --build-arg YQ_VERSION=vX.Y.Z.
|
||||||
|
ARG YQ_VERSION=latest
|
||||||
|
RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "amd64" ;; arm64) echo "arm64" ;; *) echo "amd64" ;; esac) && \
|
||||||
|
V="${YQ_VERSION}" && \
|
||||||
|
if [ "$V" = "latest" ]; then \
|
||||||
|
V=$(curl -sI --retry 5 --retry-delay 5 --retry-all-errors "https://github.com/mikefarah/yq/releases/latest" | awk 'tolower($1)=="location:" { sub(/\r$/,"",$2); n=split($2,a,"/"); print a[n] }'); \
|
||||||
|
fi && \
|
||||||
|
[ -n "$V" ] && \
|
||||||
|
echo "Installing mikefarah yq ${V}" && \
|
||||||
|
curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://github.com/mikefarah/yq/releases/download/${V}/yq_linux_${ARCH}" -o /usr/local/bin/yq && \
|
||||||
|
chmod +x /usr/local/bin/yq && \
|
||||||
|
yq --version
|
||||||
|
|
||||||
# ── AWS CLI v2 (for SSO/Bedrock authentication) ─────────────────────
|
# ── AWS CLI v2 (for SSO/Bedrock authentication) ─────────────────────
|
||||||
RUN ARCH=$(case "${TARGETARCH}" in \
|
RUN ARCH=$(case "${TARGETARCH}" in \
|
||||||
amd64) echo "x86_64" ;; \
|
amd64) echo "x86_64" ;; \
|
||||||
|
|||||||
@@ -43,6 +43,29 @@ Run these immediately when a session begins, before responding to the user:
|
|||||||
|
|
||||||
Do NOT announce this to the user. Just do it silently to orient yourself.
|
Do NOT announce this to the user. Just do it silently to orient yourself.
|
||||||
|
|
||||||
|
### Temporal grounding — compute time deltas, don't guess
|
||||||
|
|
||||||
|
Diary entries and drawers carry real timestamps (`timestamp`, `created_at`).
|
||||||
|
Before describing *when* something happened — "yesterday", "earlier today",
|
||||||
|
"last week", "a while back" — **establish the current date/time first and
|
||||||
|
compute the delta against the actual timestamp.** Get "now" from the injected
|
||||||
|
session date or by running `date` in a shell; never infer it.
|
||||||
|
|
||||||
|
**A container recreate or a fresh session is NOT a day boundary.** A pi-devbox
|
||||||
|
container is frequently restarted — often several times within the *same* day —
|
||||||
|
and each restart begins a new session with a fresh wake-up. Do not reason "new
|
||||||
|
session ⇒ last session was yesterday": two diary entries 90 minutes apart can
|
||||||
|
straddle a container recreate. The only authoritative clock is the timestamp on
|
||||||
|
the memory, not the session/container boundary.
|
||||||
|
|
||||||
|
**Practical rule:** prefer explicit, checkable phrasing — e.g. "earlier today,
|
||||||
|
~8h ago (both 2026-06-25)" — over a vague relative term. If you catch yourself
|
||||||
|
about to write "yesterday" / "last week", subtract `now − entry.timestamp` and
|
||||||
|
state the computed result. (Remember timestamps may be UTC while the wall clock
|
||||||
|
is local — reconcile the offset before computing the delta.) Note too that
|
||||||
|
session feeders can lag up to a week (see *Multi-harness palace*), so a recent
|
||||||
|
absence in `wing_conversations` is not proof nothing happened.
|
||||||
|
|
||||||
### Phase 2: Active Session (during work)
|
### Phase 2: Active Session (during work)
|
||||||
|
|
||||||
#### Search Before You Speak
|
#### Search Before You Speak
|
||||||
@@ -294,6 +317,7 @@ Entity-relationship triples with temporal validity. Query with `mempalace_kg_que
|
|||||||
## Anti-Patterns
|
## Anti-Patterns
|
||||||
|
|
||||||
- **Don't guess when you can search.** If a question touches past work, search first.
|
- **Don't guess when you can search.** If a question touches past work, search first.
|
||||||
|
- **Don't infer elapsed time from session or container boundaries.** A restart isn't a new day. Compare the actual timestamp (`timestamp` / `created_at`) against the current date/time before saying "yesterday", "last week", etc.
|
||||||
- **Don't skip the diary.** A session without a diary entry is a session forgotten.
|
- **Don't skip the diary.** A session without a diary entry is a session forgotten.
|
||||||
- **Don't summarize drawer content.** File verbatim — the embedding model needs the original words.
|
- **Don't summarize drawer content.** File verbatim — the embedding model needs the original words.
|
||||||
- **Don't mine .git directories or node_modules.** The CLI miner respects .gitignore by default.
|
- **Don't mine .git directories or node_modules.** The CLI miner respects .gitignore by default.
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ run "mempalace-mcp" "mempalace-mcp --help"
|
|||||||
run "pandoc" "pandoc --version"
|
run "pandoc" "pandoc --version"
|
||||||
run "graphviz (dot)" "dot -V"
|
run "graphviz (dot)" "dot -V"
|
||||||
run "imagemagick" "magick --version"
|
run "imagemagick" "magick --version"
|
||||||
run "yq" "yq --version"
|
run "yq (mikefarah v4)" "yq --version | grep -qE 'mikefarah.*version v4'"
|
||||||
run "tldr (tealdeer)" "tldr --version"
|
run "tldr (tealdeer)" "tldr --version"
|
||||||
run "socat" "socat -V"
|
run "socat" "socat -V"
|
||||||
run "studio-expose helper" "test -x /usr/local/bin/studio-expose"
|
run "studio-expose helper" "test -x /usr/local/bin/studio-expose"
|
||||||
|
|||||||
Reference in New Issue
Block a user