Skip to content

Commit d33cd94

Browse files
committed
Merge remote-tracking branch 'upstream/main' into feat/claude-plugin
Signed-off-by: Kody <kaidi.shi.1121@gmail.com>
2 parents f1aea59 + 0c64de9 commit d33cd94

58 files changed

Lines changed: 3497 additions & 371 deletions

Some content is hidden

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

‎.github/workflows/_build.yml‎

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,26 @@ jobs:
8585
# Archive layout/name live in the ONE canonical script the local
8686
# artifact-flow smoke lane also runs (venue-parity contract).
8787
- name: Archive release binary (canonical package-release.sh)
88+
env:
89+
VERSION: ${{ inputs.version }}
8890
run: scripts/package-release.sh ${{ matrix.goos }} ${{ matrix.goarch }}
8991

9092
- name: Attest release binary provenance
9193
if: ${{ inputs.attest }}
9294
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
9395
with:
94-
subject-path: codebase-memory-mcp-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
96+
# *.mcpb matches nothing on the glibc-dynamic linux targets — the
97+
# canonical packager builds bundles only where static/one-click holds.
98+
subject-path: |
99+
codebase-memory-mcp-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
100+
*.mcpb
95101
96102
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
97103
with:
98104
name: binaries-${{ matrix.goos }}-${{ matrix.goarch }}
99-
path: "*.tar.gz"
105+
path: |
106+
*.tar.gz
107+
*.mcpb
100108
101109
build-windows:
102110
runs-on: windows-latest
@@ -133,18 +141,24 @@ jobs:
133141
# artifact-flow smoke lane also runs (venue-parity contract).
134142
- name: Archive release binary (canonical package-release.sh)
135143
shell: msys2 {0}
144+
env:
145+
VERSION: ${{ inputs.version }}
136146
run: scripts/package-release.sh windows amd64 CC=clang CXX=clang++
137147

138148
- name: Attest release binary provenance
139149
if: ${{ inputs.attest }}
140150
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
141151
with:
142-
subject-path: codebase-memory-mcp-windows-amd64.zip
152+
subject-path: |
153+
codebase-memory-mcp-windows-amd64.zip
154+
*.mcpb
143155
144156
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
145157
with:
146158
name: binaries-windows-amd64
147-
path: "*.zip"
159+
path: |
160+
*.zip
161+
*.mcpb
148162
149163
build-windows-arm64:
150164
# Native ARM64 Windows binary via the CLANGARM64 toolchain on the
@@ -183,18 +197,24 @@ jobs:
183197
184198
- name: Archive release binary (canonical package-release.sh)
185199
shell: msys2 {0}
200+
env:
201+
VERSION: ${{ inputs.version }}
186202
run: scripts/package-release.sh windows arm64 CC=clang CXX=clang++
187203

188204
- name: Attest release binary provenance
189205
if: ${{ inputs.attest }}
190206
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
191207
with:
192-
subject-path: codebase-memory-mcp-windows-arm64.zip
208+
subject-path: |
209+
codebase-memory-mcp-windows-arm64.zip
210+
*.mcpb
193211
194212
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
195213
with:
196214
name: binaries-windows-arm64
197-
path: "*.zip"
215+
path: |
216+
*.zip
217+
*.mcpb
198218
199219
build-linux-portable:
200220
# Fully static Linux binaries (gcc -static on Ubuntu).
@@ -235,15 +255,21 @@ jobs:
235255
ldd build/c/codebase-memory-mcp 2>&1 | grep -q "not a dynamic executable" || ldd build/c/codebase-memory-mcp 2>&1 | grep -q "statically linked"
236256
237257
- name: Archive release binary (canonical package-release.sh)
258+
env:
259+
VERSION: ${{ inputs.version }}
238260
run: scripts/package-release.sh linux ${{ matrix.arch }}-portable
239261

240262
- name: Attest release binary provenance
241263
if: ${{ inputs.attest }}
242264
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
243265
with:
244-
subject-path: codebase-memory-mcp-linux-${{ matrix.arch }}-portable.tar.gz
266+
subject-path: |
267+
codebase-memory-mcp-linux-${{ matrix.arch }}-portable.tar.gz
268+
*.mcpb
245269
246270
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
247271
with:
248272
name: binaries-linux-${{ matrix.arch }}-portable
249-
path: "*.tar.gz"
273+
path: |
274+
*.tar.gz
275+
*.mcpb

‎.github/workflows/release.yml‎

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,25 @@ jobs:
144144
merge-multiple: true
145145

146146
- name: List artifacts
147-
run: ls -la *.tar.gz *.zip
147+
run: ls -la *.tar.gz *.zip *.mcpb
148148

149149
- name: Generate checksums
150-
run: sha256sum *.tar.gz *.zip > checksums.txt
150+
run: sha256sum *.tar.gz *.zip *.mcpb > checksums.txt
151151

152152
- name: Attest checksum provenance
153153
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
154154
with:
155155
subject-path: checksums.txt
156156

157+
# publish-mcp-registry reads the *.mcpb sha256 lines from here — a
158+
# same-run workflow artifact, not a draft-release download, so that
159+
# job keeps contents: read and no gh dependency.
160+
- name: Preserve checksums for the registry job
161+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
162+
with:
163+
name: release-checksums
164+
path: checksums.txt
165+
157166
# SBOM content lives in the canonical scripts/ci entry, not inline YAML
158167
# (venue-parity contract): vendored versions are reviewable there.
159168
- name: Generate SBOM
@@ -170,7 +179,7 @@ jobs:
170179

171180
- name: Sign artifacts
172181
run: |
173-
for f in *.tar.gz *.zip checksums.txt; do
182+
for f in *.tar.gz *.zip *.mcpb checksums.txt; do
174183
cosign sign-blob --yes --bundle "${f}.bundle" "$f"
175184
done
176185
@@ -202,6 +211,7 @@ jobs:
202211
files: |
203212
*.tar.gz
204213
*.zip
214+
*.mcpb
205215
checksums.txt
206216
sbom.json
207217
*.bundle
@@ -229,11 +239,14 @@ jobs:
229239
run: |
230240
ARCHIVE_DIR="$RUNNER_TEMP/release-archives"
231241
mkdir -p "$ARCHIVE_DIR" binaries
232-
gh release download "$VERSION" --dir "$ARCHIVE_DIR" --repo "$GITHUB_REPOSITORY" --pattern '*.tar.gz' --pattern '*.zip'
242+
gh release download "$VERSION" --dir "$ARCHIVE_DIR" --repo "$GITHUB_REPOSITORY" --pattern '*.tar.gz' --pattern '*.zip' --pattern '*.mcpb'
243+
# 14 = 8 archives + 6 MCPB bundles; runtime 42 = 8×3 archive
244+
# sidecars + 6×3 bundle members (manifest.json, LICENSE, notices).
245+
# The bundle binaries dedupe to the archive objects byte-for-byte.
233246
scripts/ci/extract-release-archives.sh "$ARCHIVE_DIR" binaries \
234-
--expect-archives=8 \
235-
--expect-binaries=8 \
236-
--expect-runtime-files=24
247+
--expect-archives=14 \
248+
--expect-binaries=14 \
249+
--expect-runtime-files=42
237250
238251
- name: Security audits on all unique extracted release objects
239252
run: |
@@ -350,8 +363,17 @@ jobs:
350363
# A prerelease must never become npm `latest` — plain `npm install`
351364
# would serve the RC to everyone. Publish prereleases under the
352365
# `next` dist-tag; testers opt in via `npm i codebase-memory-mcp@next`.
366+
#
367+
# Idempotency: npm versions are immutable, so a re-run of this job
368+
# after a partial success (v0.10.1: npm published, then the twine
369+
# step died) can never republish — it 403s on its own earlier
370+
# success and the release wedges in draft forever. If the exact
371+
# version already exists on the registry, publishing is DONE; skip.
353372
run: |
354-
if [[ "$RELEASE_VERSION" == *-* ]]; then
373+
V="${RELEASE_VERSION#v}"
374+
if npm view "codebase-memory-mcp@$V" version >/dev/null 2>&1; then
375+
echo "npm already has codebase-memory-mcp@$V — skipping publish (idempotent re-run)"
376+
elif [[ "$RELEASE_VERSION" == *-* ]]; then
355377
npm publish --access public --provenance --tag next
356378
else
357379
npm publish --access public --provenance
@@ -366,18 +388,30 @@ jobs:
366388
- name: Build PyPI distribution
367389
working-directory: pkg/pypi
368390
run: |
369-
# Pin to specific versions (typosquatting / supply-chain mitigation).
370-
# OSSF scorecard prefers --require-hashes, but that needs full
371-
# transitive deps; version pinning is the practical compromise.
372-
python -m pip install --upgrade 'build==1.3.0' 'twine==6.2.0'
391+
# Every byte of the publish toolchain is bound by hash: version pins
392+
# alone still let a compromised index serve different content, and a
393+
# bare `pip install` resolves the whole transitive graph at run time
394+
# (OSSF Scorecard pinned-dependencies). requirements-publish.txt is
395+
# generated on a linux/amd64 python:3.12 image — see its header for
396+
# the exact regeneration command.
397+
#
398+
# twine must stay >= 7 and hatchling is pinned in pyproject.toml:
399+
# `python -m build` resolves its backend fresh in an isolated env, and
400+
# a hatchling emitting Metadata-Version 2.5 against twine 6.2.0 is
401+
# what broke the v0.10.1 publish.
402+
python -m pip install --require-hashes -r requirements-publish.txt
373403
python -m build
404+
twine check dist/*
374405
375406
- name: Publish to PyPI
376407
working-directory: pkg/pypi
377408
env:
378409
TWINE_USERNAME: __token__
379410
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
380-
run: twine upload --non-interactive dist/*
411+
# --skip-existing: PyPI files are immutable like npm versions; a
412+
# re-run after partial success must treat already-uploaded files
413+
# as done, not as a fatal collision.
414+
run: twine upload --non-interactive --skip-existing dist/*
381415

382416
# ── 8b. Publish server.json to the official MCP Registry ──────
383417
# Runs after npm + PyPI so the registry can verify package ownership
@@ -412,6 +446,20 @@ jobs:
412446
jq --arg v "$VERSION" '.version = $v | (.packages[].version) = $v' \
413447
server.json > server.tmp && mv server.tmp server.json
414448
echo "server.json pinned to $VERSION"
449+
450+
# The MCPB entries need each bundle's sha256; checksums.txt comes from
451+
# the release-draft job as a same-run artifact (not a draft-release
452+
# download — that would need contents beyond read plus gh).
453+
- name: Fetch release checksums
454+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
455+
with:
456+
name: release-checksums
457+
458+
- name: Append MCPB package entries to server.json
459+
env:
460+
RELEASE_VERSION: ${{ inputs.version }}
461+
run: |
462+
scripts/ci/gen-mcpb-registry-entries.sh server.json checksums.txt "$RELEASE_VERSION"
415463
cat server.json
416464
417465
- name: Install mcp-publisher
@@ -435,6 +483,16 @@ jobs:
435483
permissions:
436484
contents: write
437485
steps:
486+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
487+
488+
# Legacy ui-* aliases so 0.9.x updaters stop 404ing (#1538). Runs after
489+
# verify: same bytes as archives the VirusTotal gate already cleared, and
490+
# publishing earlier would duplicate the scan set and provenance manifest.
491+
- name: Publish legacy ui-* archive aliases
492+
env:
493+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
494+
run: scripts/ci/publish-legacy-aliases.sh "${{ inputs.version }}" "$GITHUB_REPOSITORY"
495+
438496
- name: Un-draft GitHub release
439497
env:
440498
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

‎.gitignore‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,9 @@ soak-results-query-leak/
8181
# scripts/soak-test.sh stay tracked.
8282
/soak*/
8383
/memlab-*
84+
85+
# Compiled Go wrapper binary. `go build` in pkg/go drops the executable beside
86+
# its sources; one was committed by accident (8.8 MB ELF, flagged by OSSF
87+
# Scorecard as a binary artifact in source). Source only, never the build.
88+
/pkg/go/codebase-memory-mcp
89+
/pkg/go/codebase-memory-mcp.exe

‎SECURITY.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,8 @@ for Linux ELF binaries, so it is not a complete answer either.
288288

289289
| Version | Supported |
290290
|---------|-----------|
291-
| Latest `0.9.x` | Yes — security fixes land in the newest release |
292-
| < 0.9 | No — please upgrade to the latest release |
291+
| Latest `0.10.x` | Yes — security fixes land in the newest release |
292+
| < 0.10 | No — please upgrade to the latest release |
293293

294294
Only the latest release is supported. Security fixes are shipped in a new
295295
patched release rather than backported to older versions; upgrading to the

‎graph-ui/package-lock.json‎

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎graph-ui/package.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
},
4242
"overrides": {
4343
"form-data": ">=4.0.6",
44-
"@babel/core": ">=7.29.6"
44+
"@babel/core": ">=7.29.6",
45+
"nanoid": "^3.3.17"
4546
}
4647
}

‎install.sh‎

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,25 +220,87 @@ if [ "$EXPECTED" != "$ACTUAL" ]; then
220220
fi
221221
echo "Checksum verified."
222222

223+
# Validate the complete archive namespace before extraction. Current and legacy
224+
# release assets use the same four-member root layout; anything outside that
225+
# closed set is a release-integrity failure, not a sidecar to ignore.
226+
if [ "$OS" = "windows" ]; then
227+
ARCHIVE_BINARY="codebase-memory-mcp.exe"
228+
ARCHIVE_INSTALLER="install.ps1"
229+
else
230+
ARCHIVE_BINARY="codebase-memory-mcp"
231+
ARCHIVE_INSTALLER="install.sh"
232+
fi
233+
ARCHIVE_MEMBERS_FILE="$DLDIR/archive-members.txt"
234+
if [ "$EXT" = "zip" ]; then
235+
if ! unzip -Z1 "$DLDIR/$ARCHIVE" > "$ARCHIVE_MEMBERS_FILE"; then
236+
echo "error: could not enumerate release archive" >&2
237+
exit 1
238+
fi
239+
else
240+
if ! tar -tzf "$DLDIR/$ARCHIVE" > "$ARCHIVE_MEMBERS_FILE"; then
241+
echo "error: could not enumerate release archive" >&2
242+
exit 1
243+
fi
244+
fi
245+
246+
BINARY_MEMBERS=0
247+
LICENSE_MEMBERS=0
248+
INSTALLER_MEMBERS=0
249+
NOTICE_MEMBERS=0
250+
ARCHIVE_MEMBER_COUNT=0
251+
while IFS= read -r member || [ -n "$member" ]; do
252+
ARCHIVE_MEMBER_COUNT=$((ARCHIVE_MEMBER_COUNT + 1))
253+
case "$member" in
254+
"$ARCHIVE_BINARY") BINARY_MEMBERS=$((BINARY_MEMBERS + 1)) ;;
255+
LICENSE) LICENSE_MEMBERS=$((LICENSE_MEMBERS + 1)) ;;
256+
"$ARCHIVE_INSTALLER") INSTALLER_MEMBERS=$((INSTALLER_MEMBERS + 1)) ;;
257+
THIRD_PARTY_NOTICES.md) NOTICE_MEMBERS=$((NOTICE_MEMBERS + 1)) ;;
258+
*)
259+
echo "error: release archive contains unexpected member: $member" >&2
260+
exit 1
261+
;;
262+
esac
263+
done < "$ARCHIVE_MEMBERS_FILE"
264+
265+
if [ "$BINARY_MEMBERS" -ne 1 ] || [ "$LICENSE_MEMBERS" -ne 1 ] ||
266+
[ "$INSTALLER_MEMBERS" -ne 1 ] || [ "$NOTICE_MEMBERS" -ne 1 ] ||
267+
[ "$ARCHIVE_MEMBER_COUNT" -ne 4 ]; then
268+
echo "error: release archive does not match the exact member set" >&2
269+
exit 1
270+
fi
271+
223272
# Extract
224273
echo "Extracting..."
225274
if [ "$EXT" = "zip" ]; then
226275
unzip -q "$DLDIR/$ARCHIVE" -d "$DLDIR"
227276
else
228-
tar -xzf "$DLDIR/$ARCHIVE" -C "$DLDIR"
277+
tar --no-same-owner -xzf "$DLDIR/$ARCHIVE" -C "$DLDIR"
229278
fi
230279

231-
DLBIN="$DLDIR/codebase-memory-mcp"
232-
if [ ! -f "$DLBIN" ]; then
280+
for extracted_member in "$ARCHIVE_BINARY" LICENSE "$ARCHIVE_INSTALLER" \
281+
THIRD_PARTY_NOTICES.md; do
282+
if [ ! -f "$DLDIR/$extracted_member" ] || [ -L "$DLDIR/$extracted_member" ]; then
283+
echo "error: release member is not a regular file: $extracted_member" >&2
284+
exit 1
285+
fi
286+
done
287+
288+
DLBIN="$DLDIR/$ARCHIVE_BINARY"
289+
if [ ! -f "$DLBIN" ] || [ -L "$DLBIN" ]; then
233290
echo "error: binary not found after extraction" >&2
234291
exit 1
235292
fi
236293

237294
# macOS: fix signing
238295
if [ "$OS" = "darwin" ]; then
239296
echo "Fixing macOS code signing..."
240-
xattr -d com.apple.quarantine "$DLBIN" 2>/dev/null || true
241-
codesign --sign - --force "$DLBIN" 2>/dev/null || true
297+
# A curl-downloaded archive usually carries no quarantine attribute at all,
298+
# and xattr then prints "No such xattr: com.apple.quarantine" on stderr.
299+
# That harmless line was read as the cause of an unrelated install failure
300+
# and became a bug report's title (#1537) — silence it; nothing here is an
301+
# error worth showing.
302+
xattr -d com.apple.quarantine "$DLBIN" >/dev/null 2>&1 || true
303+
codesign --sign - --force "$DLBIN" >/dev/null 2>&1 || true
242304
fi
243305

244306
# Verify the candidate before it requests account-wide maintenance. The

‎pkg/go/codebase-memory-mcp‎

-8.43 MB
Binary file not shown.

0 commit comments

Comments
 (0)